diff --git a/src/gui/src/MainWindow.cpp b/src/gui/src/MainWindow.cpp index c530c8091..d46458754 100644 --- a/src/gui/src/MainWindow.cpp +++ b/src/gui/src/MainWindow.cpp @@ -333,13 +333,32 @@ void MainWindow::checkSystemInterruptions() if(synergyType() == synergyServer) { #if defined(Q_OS_MAC) - if(!m_isSecureInputNotificationShown && isOSXSecureInputEnabled()) + 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); - message.setText(QObject::tr("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")); - message.exec(); + 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 } diff --git a/src/gui/src/OSXHelpers.h b/src/gui/src/OSXHelpers.h index 15876aecf..cfae2b4a4 100644 --- a/src/gui/src/OSXHelpers.h +++ b/src/gui/src/OSXHelpers.h @@ -19,7 +19,11 @@ #define OSXHELPERS__H +#include + bool isOSXSecureInputEnabled(); +int getOSXSecureInputEventPID(); +std::string getOSXProcessName(int pid); bool isOSXInterfaceStyleDark(); bool isOSXUseDarkIcons(); diff --git a/src/gui/src/OSXHelpers.mm b/src/gui/src/OSXHelpers.mm index 511709ebf..9a919ec9e 100644 --- a/src/gui/src/OSXHelpers.mm +++ b/src/gui/src/OSXHelpers.mm @@ -20,7 +20,15 @@ #import #import #import +#import + #import +#import +#import +#import +#import +#import +#import bool @@ -29,6 +37,61 @@ isOSXSecureInputEnabled() return IsSecureEventInputEnabled(); } +int +getOSXSecureInputEventPID() +{ + 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::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; +} + +std::string +getOSXProcessName(int pid) +{ + if(!pid) return ""; + char buf[128]; + proc_name(pid, buf, sizeof(buf)); + return buf; +} + bool isOSXInterfaceStyleDark() {