SYNERGY-1032 - Use native notifications for secure input on MacOS (#7041)
* SYNERGY-1017 - Try usnig NSUserNotification * SYNERGY-1032 - Use UNUserNotificationCenter instead of deprecated notifications * SYNERGY-1032 - Update app delegate and add log after notification access * SYNERGY-1032 - Try using deprecated notification * SYNERGY-1032 - Add notifications to the platform dependant code * SYNERGY-1032 - Check for development builds before asking permission for notifications * SYNERGY-1032 - Remove old notification and secure input detection * SYNERGY-1032 - Add permission request to the main app * SYNERGY-1032 - Only show notifications on server * SYNERGY-1032 - Request and show notification from main app * SYNERGY-1032 - Remove logging from OSX helpers * SYNERGY-1032 - Add UN notification for testing * SYNERGY-1032 - Show notification once * SYNERGY-1032 - Try replicating old flow * SYNERGY-1032 - Reuse notification center * SYNERGY-1032 - Send notification after granting permission * SYNERGY-1032 - Update app delegate * SYNERGY-1032 - Remove deprecated property * SYNERGY-1032 - Add output for debugging * SYNERGY-1032 - Try updating delegate * SYNERGY-1032 - Print current bundle name * SYNERGY-1032 - Add AppDelegate to the server * SYNERGY-1032 - Move notification creation to synergy app * SYNERGY-1032 - Update notification text * SYNERGY-1032 - Fix QString conversion for notification * SYNERGY-1032 - Move notification creation outside the handler * SYNERGY-1032 - Remove debug notification, show notification with no delay * SYNERGY-1032 - Show notification inside the completion handler * SYNERGY-1032 - Remove timed notification trigger * SYNERGY-1032 - Request permissions on synergy start * SYNERGY-1032 - Remove unused includes * SYNERGY-1032 - Update changelog * SYNERGY-1032 - Add empty notification implementations for Windows and Linux * SYNERGY-1032 - Removed temporary debug messages * SYNERGY-1032 - Resolve code smell
This commit is contained in:
parent
c58b69dfad
commit
5843230169
17 changed files with 277 additions and 107 deletions
|
|
@ -167,6 +167,7 @@ if (UNIX)
|
|||
find_library (lib_ApplicationServices ApplicationServices)
|
||||
find_library (lib_Foundation Foundation)
|
||||
find_library (lib_Carbon Carbon)
|
||||
find_library (lib_UserNotifications UserNotifications)
|
||||
|
||||
list (APPEND libs
|
||||
${lib_ScreenSaver}
|
||||
|
|
@ -174,6 +175,7 @@ if (UNIX)
|
|||
${lib_ApplicationServices}
|
||||
${lib_Foundation}
|
||||
${lib_Carbon}
|
||||
${lib_UserNotifications}
|
||||
)
|
||||
|
||||
else() # not-apple
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Enhancements:
|
|||
- #7026 Update the way we package OpenSSL for Windows build
|
||||
- #7035 Add Fedora 34 build
|
||||
- #7037 Add Ubuntu 21.04 build
|
||||
- #7030 Add user nofication for secure input on Mac
|
||||
- #7030 | #7041 Add user nofication for secure input on Mac
|
||||
===========
|
||||
|
||||
v1.14.0-stable
|
||||
|
|
|
|||
17
src/gui/src/AppDelegate.h
Normal file
17
src/gui/src/AppDelegate.h
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
#ifndef APPDELEGATE_H
|
||||
#define APPDELEGATE_H
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||
|
||||
@interface AppDelegate : NSObject <NSApplicationDelegate, NSUserNotificationCenterDelegate, UNUserNotificationCenterDelegate>
|
||||
|
||||
@end
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // APPDELEGATE_H
|
||||
33
src/gui/src/AppDelegate.mm
Normal file
33
src/gui/src/AppDelegate.mm
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
#import "AppDelegate.h"
|
||||
|
||||
@interface AppDelegate ()
|
||||
|
||||
@property (strong) IBOutlet NSWindow *window;
|
||||
@end
|
||||
|
||||
@implementation AppDelegate
|
||||
{
|
||||
}
|
||||
|
||||
-(void)applicationDidFinishLaunching:(NSNotification *)aNotification
|
||||
{
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] setDelegate:self];
|
||||
[[UNUserNotificationCenter currentNotificationCenter] setDelegate:self];
|
||||
}
|
||||
|
||||
-(BOOL)userNotificationCenter:(NSUserNotificationCenter *)center shouldPresentNotification:(NSUserNotification *)notification{
|
||||
return YES;
|
||||
}
|
||||
|
||||
-(void)userNotificationCenter:(UNUserNotificationCenter *)center
|
||||
willPresentNotification:(UNNotification *)notification
|
||||
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler{
|
||||
UNNotificationPresentationOptions presentationOptions =
|
||||
UNNotificationPresentationOptionSound
|
||||
| UNNotificationPresentationOptionAlert
|
||||
| UNNotificationPresentationOptionBadge;
|
||||
|
||||
completionHandler(presentationOptions);
|
||||
}
|
||||
|
||||
@end
|
||||
|
|
@ -328,42 +328,6 @@ void MainWindow::saveSettings()
|
|||
GUI::Config::ConfigWriter::make()->globalSave();
|
||||
}
|
||||
|
||||
void MainWindow::checkSystemInterruptions()
|
||||
{
|
||||
if(synergyType() == synergyServer)
|
||||
{
|
||||
#if defined(Q_OS_MAC)
|
||||
if(!isOSXSecureInputEnabled())
|
||||
{
|
||||
m_isSecureInputNotificationShown = false;
|
||||
}
|
||||
else if(!m_isSecureInputNotificationShown)
|
||||
{
|
||||
// avoid showing multiple duplicate messages
|
||||
m_isSecureInputNotificationShown = true;
|
||||
|
||||
QMessageBox message(this);
|
||||
message.addButton(QObject::tr("Accept"), QMessageBox::AcceptRole);
|
||||
std::string messageText =
|
||||
"Secure input was enabled in your system by another application. " \
|
||||
"Synergy will not be able to send keyboard strokes while the secure input is enabled\n\n";
|
||||
int secureInputProcessPID = getOSXSecureInputEventPID();
|
||||
std::string infringingProcessName = getOSXProcessName(secureInputProcessPID);
|
||||
|
||||
// IO registry may not contain the secure input process PID
|
||||
// in this case don't add an option to quit the infringing app
|
||||
if(secureInputProcessPID == 0) infringingProcessName = "unknown";
|
||||
else message.addButton(QString("Quit %1").arg(infringingProcessName.c_str()), QMessageBox::ApplyRole);
|
||||
messageText += "Infringing process is " + infringingProcessName;
|
||||
message.setText(QObject::tr(messageText.c_str()));
|
||||
|
||||
// if user decides to stop the app send the SIGTERM signal
|
||||
if (message.exec() == QMessageBox::Accepted && secureInputProcessPID) kill(secureInputProcessPID, SIGTERM);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
void MainWindow::zeroConfToggled() {
|
||||
#if !defined(SYNERGY_ENTERPRISE) && defined(SYNERGY_AUTOCONFIG)
|
||||
updateZeroconfService();
|
||||
|
|
@ -498,6 +462,12 @@ void MainWindow::updateFromLogLine(const QString &line)
|
|||
checkFingerprint(line);
|
||||
checkSecureSocket(line);
|
||||
|
||||
// subprocess (synergys, synergyc) is not allowed to show notifications
|
||||
// process the log from it and show notificatino from synergy instead
|
||||
#ifdef Q_OS_MAC
|
||||
checkOSXNotification(line);
|
||||
#endif
|
||||
|
||||
#ifndef SYNERGY_ENTERPRISE
|
||||
checkLicense(line);
|
||||
#endif
|
||||
|
|
@ -609,6 +579,27 @@ void MainWindow::checkSecureSocket(const QString& line)
|
|||
m_SecureSocketVersion = line.mid(index + strlen(tlsCheckString)); // Compliant: we made sure that tlsCheckString variable ended with null(static const char* declaration)
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
void MainWindow::checkOSXNotification(const QString& line)
|
||||
{
|
||||
static const QString OSXNotificationSubstring = "OSX Notification: ";
|
||||
if (line.contains(OSXNotificationSubstring) && line.contains('|')) {
|
||||
int delimterPosition = line.indexOf('|');
|
||||
int notificationStartPosition = line.indexOf(OSXNotificationSubstring);
|
||||
QString title =
|
||||
line.mid(notificationStartPosition + OSXNotificationSubstring.length(),
|
||||
delimterPosition - notificationStartPosition - OSXNotificationSubstring.length());
|
||||
QString body =
|
||||
line.mid(delimterPosition + 1,
|
||||
line.length() - delimterPosition);
|
||||
if (!showOSXNotification(title, body)) {
|
||||
appendLogInfo("OSX notification was not shown");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
QString MainWindow::getTimeStamp()
|
||||
{
|
||||
QDateTime current = QDateTime::currentDateTime();
|
||||
|
|
@ -646,6 +637,10 @@ void MainWindow::startSynergy()
|
|||
{
|
||||
saveSettings();
|
||||
|
||||
#ifdef Q_OS_MAC
|
||||
requestOSXNotificationPermission();
|
||||
#endif
|
||||
|
||||
#ifndef SYNERGY_ENTERPRISE
|
||||
SerialKey serialKey = m_LicenseManager->serialKey();
|
||||
if (!serialKey.isValid()) {
|
||||
|
|
@ -743,7 +738,6 @@ void MainWindow::startSynergy()
|
|||
connect(synergyProcess(), SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(synergyFinished(int, QProcess::ExitStatus)));
|
||||
connect(synergyProcess(), SIGNAL(readyReadStandardOutput()), this, SLOT(logOutput()));
|
||||
connect(synergyProcess(), SIGNAL(readyReadStandardError()), this, SLOT(logError()));
|
||||
connect(&m_systemInterruptionCheckTimer, SIGNAL(timeout()), this, SLOT(checkSystemInterruptions()));
|
||||
}
|
||||
|
||||
qDebug() << args;
|
||||
|
|
@ -1061,7 +1055,6 @@ void MainWindow::setSynergyState(qSynergyState state)
|
|||
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStopSynergy, SLOT(trigger()));
|
||||
m_pButtonToggleStart->setText(tr("&Stop"));
|
||||
m_pButtonApply->setEnabled(true);
|
||||
m_systemInterruptionCheckTimer.start(5000); // check every 5 seconds
|
||||
}
|
||||
else if (state == synergyDisconnected)
|
||||
{
|
||||
|
|
@ -1069,7 +1062,6 @@ void MainWindow::setSynergyState(qSynergyState state)
|
|||
connect (m_pButtonToggleStart, SIGNAL(clicked()), m_pActionStartSynergy, SLOT(trigger()));
|
||||
m_pButtonToggleStart->setText(tr("&Start"));
|
||||
m_pButtonApply->setEnabled(false);
|
||||
m_systemInterruptionCheckTimer.stop();
|
||||
}
|
||||
|
||||
bool running = false;
|
||||
|
|
|
|||
|
|
@ -164,7 +164,6 @@ public slots:
|
|||
void logError();
|
||||
void updateFound(const QString& version);
|
||||
void saveSettings();
|
||||
void checkSystemInterruptions();
|
||||
|
||||
/// @brief Receives the signal that the auto config option has changed
|
||||
void zeroConfToggled();
|
||||
|
|
@ -207,6 +206,9 @@ public slots:
|
|||
void checkConnected(const QString& line);
|
||||
void checkFingerprint(const QString& line);
|
||||
void checkSecureSocket(const QString& line);
|
||||
#ifdef Q_OS_MAC
|
||||
void checkOSXNotification(const QString& line);
|
||||
#endif
|
||||
#ifndef SYNERGY_ENTERPRISE
|
||||
void checkLicense(const QString& line);
|
||||
#endif
|
||||
|
|
@ -247,11 +249,6 @@ public slots:
|
|||
QString m_SecureSocketVersion; // brief Contains the version of the Secure Socket currently active
|
||||
ServerConnection m_serverConnection;
|
||||
ClientConnection m_clientConnection;
|
||||
QTimer m_systemInterruptionCheckTimer; // Timer used for sceduling a task for detecting the system
|
||||
// operations preventing the Synergy from working properly
|
||||
#if defined(Q_OS_MAC)
|
||||
bool m_isSecureInputNotificationShown = false;
|
||||
#endif
|
||||
|
||||
void updateAutoConfigWidgets();
|
||||
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
#define OSXHELPERS__H
|
||||
|
||||
#include <string>
|
||||
#include <QString>
|
||||
|
||||
enum class IconsTheme {
|
||||
ICONS_DARK,
|
||||
|
|
@ -27,9 +27,9 @@ enum class IconsTheme {
|
|||
ICONS_TEMPLATE
|
||||
};
|
||||
|
||||
bool isOSXSecureInputEnabled();
|
||||
int getOSXSecureInputEventPID();
|
||||
std::string getOSXProcessName(int pid);
|
||||
void requestOSXNotificationPermission();
|
||||
bool isOSXDevelopmentBuild();
|
||||
bool showOSXNotification(const QString& title, const QString& body);
|
||||
bool isOSXInterfaceStyleDark();
|
||||
IconsTheme getOSXIconsTheme();
|
||||
|
||||
|
|
|
|||
|
|
@ -20,76 +20,83 @@
|
|||
#import <Foundation/Foundation.h>
|
||||
#import <CoreData/CoreData.h>
|
||||
#import <Cocoa/Cocoa.h>
|
||||
#import <base/Log.h>
|
||||
#import <UserNotifications/UNNotification.h>
|
||||
#import <UserNotifications/UNUserNotificationCenter.h>
|
||||
#import <UserNotifications/UNNotificationContent.h>
|
||||
#import <UserNotifications/UNNotificationTrigger.h>
|
||||
|
||||
#import <Carbon/Carbon.h>
|
||||
#import <mach/mach_port.h>
|
||||
#import <mach/mach_interface.h>
|
||||
#import <mach/mach_init.h>
|
||||
#import <IOKit/IOMessage.h>
|
||||
#import <IOKit/IOKitLib.h>
|
||||
#import <libproc.h>
|
||||
#import <QtGlobal>
|
||||
|
||||
void requestOSXNotificationPermission()
|
||||
{
|
||||
if (@available(macOS 10.14, *))
|
||||
{
|
||||
if (isOSXDevelopmentBuild())
|
||||
{
|
||||
qWarning("Not requesting notification permission in dev build");
|
||||
return;
|
||||
}
|
||||
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
[center requestAuthorizationWithOptions:(UNAuthorizationOptionAlert + UNAuthorizationOptionSound)
|
||||
completionHandler:^(BOOL granted, NSError * _Nullable error) {
|
||||
if(error != nil)
|
||||
{
|
||||
qWarning("Notification permission request error: %s", [[NSString stringWithFormat:@"%@", error] UTF8String]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
isOSXSecureInputEnabled()
|
||||
isOSXDevelopmentBuild()
|
||||
{
|
||||
return IsSecureEventInputEnabled();
|
||||
std::string bundleURL = [[[NSBundle mainBundle] bundleURL].absoluteString UTF8String];
|
||||
return (bundleURL.find("Applications/Synergy.app") == std::string::npos);
|
||||
}
|
||||
|
||||
int
|
||||
getOSXSecureInputEventPID()
|
||||
bool
|
||||
showOSXNotification(const QString& title, const QString& body)
|
||||
{
|
||||
io_service_t service = MACH_PORT_NULL, service_root = MACH_PORT_NULL;
|
||||
mach_port_t masterPort;
|
||||
if (@available(macOS 10.14, *))
|
||||
{
|
||||
// accessing notification center on unsigned build causes an immidiate
|
||||
// application shutodown (in this case synergys) and cannot be caught
|
||||
// to avoid issues with it need to first check if this is a dev build
|
||||
if (isOSXDevelopmentBuild())
|
||||
{
|
||||
qWarning("Not showing notification in dev build");
|
||||
return false;
|
||||
}
|
||||
|
||||
kern_return_t kr = IOMasterPort( MACH_PORT_NULL, &masterPort );
|
||||
if(kr != KERN_SUCCESS) return 0;
|
||||
requestOSXNotificationPermission();
|
||||
|
||||
// IO registry refuses to tap into the root level directly
|
||||
// as a workaround access the parent of the top user level
|
||||
service = IORegistryEntryFromPath( masterPort, kIOServicePlane ":/" );
|
||||
IORegistryEntryGetParentEntry(service, kIOServicePlane, &service_root);
|
||||
UNUserNotificationCenter* center = [UNUserNotificationCenter currentNotificationCenter];
|
||||
|
||||
std::unique_ptr<std::remove_pointer<CFTypeRef>::type, decltype(&CFRelease)> consoleUsers(
|
||||
IORegistryEntrySearchCFProperty(service_root, kIOServicePlane, CFSTR("IOConsoleUsers"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively),
|
||||
CFRelease
|
||||
);
|
||||
if(!consoleUsers) return 0;
|
||||
UNMutableNotificationContent *content = [[UNMutableNotificationContent alloc] init];
|
||||
content.title = title.toNSString();
|
||||
content.body = body.toNSString();
|
||||
|
||||
CFTypeID type = CFGetTypeID(consoleUsers.get());
|
||||
if(type != CFArrayGetTypeID()) return 0;
|
||||
// Create the request object.
|
||||
UNNotificationRequest* request = [UNNotificationRequest
|
||||
requestWithIdentifier:@"SecureInput" content:content trigger:nil];
|
||||
|
||||
CFTypeRef dict = CFArrayGetValueAtIndex((CFArrayRef)consoleUsers.get(), 0);
|
||||
if(!dict) return 0;
|
||||
|
||||
type = CFGetTypeID(dict);
|
||||
if(type != CFDictionaryGetTypeID()) return 0;
|
||||
|
||||
CFTypeRef secureInputPID = nullptr;
|
||||
CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR("kCGSSessionSecureInputPID"), &secureInputPID);
|
||||
|
||||
if(secureInputPID == nullptr) return 0;
|
||||
|
||||
type = CFGetTypeID(secureInputPID);
|
||||
if(type != CFNumberGetTypeID()) return 0;
|
||||
|
||||
auto pidRef = (CFNumberRef)secureInputPID;
|
||||
CFNumberType numberType = CFNumberGetType(pidRef);
|
||||
if(numberType != kCFNumberSInt32Type) return 0;
|
||||
|
||||
int pid;
|
||||
CFNumberGetValue(pidRef, kCFNumberSInt32Type, &pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
std::string
|
||||
getOSXProcessName(int pid)
|
||||
{
|
||||
if(!pid) return "";
|
||||
char buf[128];
|
||||
proc_name(pid, buf, sizeof(buf));
|
||||
return buf;
|
||||
[center addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
|
||||
if (error != nil) {
|
||||
qWarning("Notification display request error: %s", [[NSString stringWithFormat:@"%@", error] UTF8String]);
|
||||
}
|
||||
}];
|
||||
}
|
||||
else
|
||||
{
|
||||
NSUserNotification* notification = [[NSUserNotification alloc] init];
|
||||
notification.title = title.toNSString();
|
||||
notification.informativeText = body.toNSString();
|
||||
notification.soundName = NSUserNotificationDefaultSoundName; //Will play a default sound
|
||||
[[NSUserNotificationCenter defaultUserNotificationCenter] deliverNotification: notification];
|
||||
[notification autorelease];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
|
|
@ -1988,6 +1988,12 @@ MSWindowsScreen::getDropTarget() const
|
|||
return m_desktopPath;
|
||||
}
|
||||
|
||||
void
|
||||
MSWindowsScreen::createNotification(const String& title, const String& content) const
|
||||
{
|
||||
// TODO: implement notification
|
||||
}
|
||||
|
||||
bool
|
||||
MSWindowsScreen::isModifierRepeat(KeyModifierMask oldState, KeyModifierMask state, WPARAM wParam) const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ public:
|
|||
virtual String& getDraggingFilename();
|
||||
virtual const String&
|
||||
getDropTarget() const;
|
||||
void createNotification(const String& title, const String& content) const override;
|
||||
|
||||
protected:
|
||||
// IPlatformScreen overrides
|
||||
|
|
|
|||
|
|
@ -101,6 +101,7 @@ public:
|
|||
|
||||
const String& getDropTarget() const { return m_dropTarget; }
|
||||
void waitForCarbonLoop() const;
|
||||
void createNotification(const String& title, const String& content) const override;
|
||||
|
||||
protected:
|
||||
// IPlatformScreen overrides
|
||||
|
|
@ -203,6 +204,8 @@ private:
|
|||
|
||||
void getDropTargetThread(void*);
|
||||
|
||||
void createSecureInputNotification();
|
||||
|
||||
private:
|
||||
struct HotKeyItem {
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@
|
|||
#include <AvailabilityMacros.h>
|
||||
#include <IOKit/hidsystem/event_status_driver.h>
|
||||
#include <AppKit/NSEvent.h>
|
||||
#include <libproc.h>
|
||||
|
||||
// This isn't in any Apple SDK that I know of as of yet.
|
||||
enum {
|
||||
|
|
@ -58,6 +59,9 @@ enum {
|
|||
kCarbonLoopWaitTimeout = 10
|
||||
};
|
||||
|
||||
int getSecureInputEventPID();
|
||||
String getProcessName(int pid);
|
||||
|
||||
// TODO: upgrade deprecated function usage in these functions.
|
||||
void setZeroSuppressionInterval();
|
||||
void avoidSupression();
|
||||
|
|
@ -881,6 +885,10 @@ OSXScreen::enter()
|
|||
bool
|
||||
OSXScreen::leave()
|
||||
{
|
||||
if(m_isPrimary && IsSecureEventInputEnabled()) {
|
||||
createSecureInputNotification();
|
||||
}
|
||||
|
||||
hideCursor();
|
||||
|
||||
if (isDraggingStarted()) {
|
||||
|
|
@ -2150,6 +2158,84 @@ OSXScreen::waitForCarbonLoop() const
|
|||
|
||||
}
|
||||
|
||||
void
|
||||
OSXScreen::createNotification(const String& title, const String& content) const
|
||||
{
|
||||
LOG((CLOG_INFO "OSX Notification: %s|%s", title.c_str(), content.c_str()));
|
||||
}
|
||||
|
||||
void
|
||||
OSXScreen::createSecureInputNotification()
|
||||
{
|
||||
std::string secureInputNotificationBody =
|
||||
"Secure input was enabled in your system. " \
|
||||
"Synergy may not be able to send keyboard strokes. ";
|
||||
|
||||
int secureInputProcessPID = getSecureInputEventPID();
|
||||
std::string infringingProcessName = getProcessName(secureInputProcessPID);
|
||||
if(secureInputProcessPID == 0) infringingProcessName = "unknown";
|
||||
secureInputNotificationBody += "Infringing process is " + infringingProcessName;
|
||||
|
||||
createNotification(
|
||||
"Keyboard may not work correctly",
|
||||
secureInputNotificationBody);
|
||||
}
|
||||
|
||||
int
|
||||
getSecureInputEventPID()
|
||||
{
|
||||
io_service_t service = MACH_PORT_NULL, service_root = MACH_PORT_NULL;
|
||||
mach_port_t masterPort;
|
||||
|
||||
kern_return_t kr = IOMasterPort( MACH_PORT_NULL, &masterPort );
|
||||
if(kr != KERN_SUCCESS) return 0;
|
||||
|
||||
// IO registry refuses to tap into the root level directly
|
||||
// as a workaround access the parent of the top user level
|
||||
service = IORegistryEntryFromPath( masterPort, kIOServicePlane ":/" );
|
||||
IORegistryEntryGetParentEntry(service, kIOServicePlane, &service_root);
|
||||
|
||||
std::unique_ptr<std::remove_pointer<CFTypeRef>::type, decltype(&CFRelease)> consoleUsers(
|
||||
IORegistryEntrySearchCFProperty(service_root, kIOServicePlane, CFSTR("IOConsoleUsers"), NULL, kIORegistryIterateParents | kIORegistryIterateRecursively),
|
||||
CFRelease
|
||||
);
|
||||
if(!consoleUsers) return 0;
|
||||
|
||||
CFTypeID type = CFGetTypeID(consoleUsers.get());
|
||||
if(type != CFArrayGetTypeID()) return 0;
|
||||
|
||||
CFTypeRef dict = CFArrayGetValueAtIndex((CFArrayRef)consoleUsers.get(), 0);
|
||||
if(!dict) return 0;
|
||||
|
||||
type = CFGetTypeID(dict);
|
||||
if(type != CFDictionaryGetTypeID()) return 0;
|
||||
|
||||
CFTypeRef secureInputPID = nullptr;
|
||||
CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR("kCGSSessionSecureInputPID"), &secureInputPID);
|
||||
|
||||
if(secureInputPID == nullptr) return 0;
|
||||
|
||||
type = CFGetTypeID(secureInputPID);
|
||||
if(type != CFNumberGetTypeID()) return 0;
|
||||
|
||||
auto pidRef = (CFNumberRef)secureInputPID;
|
||||
CFNumberType numberType = CFNumberGetType(pidRef);
|
||||
if(numberType != kCFNumberSInt32Type) return 0;
|
||||
|
||||
int pid;
|
||||
CFNumberGetValue(pidRef, kCFNumberSInt32Type, &pid);
|
||||
return pid;
|
||||
}
|
||||
|
||||
String
|
||||
getProcessName(int pid)
|
||||
{
|
||||
if(!pid) return "";
|
||||
char buf[128];
|
||||
proc_name(pid, buf, sizeof(buf));
|
||||
return buf;
|
||||
}
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
|
||||
void
|
||||
|
|
|
|||
|
|
@ -475,6 +475,12 @@ XWindowsScreen::isPrimary() const
|
|||
return m_isPrimary;
|
||||
}
|
||||
|
||||
void
|
||||
XWindowsScreen::createNotification(const String& title, const String& content) const
|
||||
{
|
||||
// TODO: implement notification
|
||||
}
|
||||
|
||||
void*
|
||||
XWindowsScreen::getEventTarget() const
|
||||
{
|
||||
|
|
|
|||
|
|
@ -84,6 +84,7 @@ public:
|
|||
virtual void setOptions(const OptionsList& options);
|
||||
virtual void setSequenceNumber(UInt32);
|
||||
virtual bool isPrimary() const;
|
||||
void createNotification(const String& title, const String& content) const override;
|
||||
|
||||
protected:
|
||||
// IPlatformScreen overrides
|
||||
|
|
|
|||
|
|
@ -133,6 +133,12 @@ public:
|
|||
//! Change dragging status
|
||||
virtual void setDraggingStarted(bool started) = 0;
|
||||
|
||||
//! Send desktop user notification
|
||||
/*!
|
||||
Creates a platform dependant user notificaiton using native APIs
|
||||
*/
|
||||
virtual void createNotification(const String& title, const String& content) const = 0;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
|
|
|||
|
|
@ -522,4 +522,10 @@ Screen::leaveSecondary()
|
|||
m_screen->fakeAllKeysUp();
|
||||
}
|
||||
|
||||
void
|
||||
Screen::createNotification(const String& title, const String& content) const
|
||||
{
|
||||
m_screen->createNotification(title, content);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -233,6 +233,13 @@ public:
|
|||
void startDraggingFiles(DragFileList& fileList);
|
||||
|
||||
void setEnableDragDrop(bool enabled);
|
||||
|
||||
//! Send desktop user notification
|
||||
/*!
|
||||
Creates a platform dependant user notificaiton using native APIs
|
||||
*/
|
||||
virtual void createNotification(const String& title, const String& content) const;
|
||||
|
||||
//@}
|
||||
//! @name accessors
|
||||
//@{
|
||||
|
|
|
|||
Loading…
Reference in a new issue