deskflow/src/lib/platform/OSXScreen.mm

1883 lines
53 KiB
Text
Raw Normal View History

2012-06-10 16:50:54 +00:00
/*
* Deskflow -- mouse and keyboard sharing utility
* SPDX-FileCopyrightText: (C) 2025 - 2026 Deskflow Developers
* SPDX-FileCopyrightText: (C) 2012 - 2016 Symless Ltd.
* SPDX-FileCopyrightText: (C) 2004 Chris Schoeneman
* SPDX-License-Identifier: GPL-2.0-only WITH LicenseRef-OpenSSL-Exception
2012-06-10 16:50:54 +00:00
*/
#include "platform/OSXScreen.h"
2012-06-10 16:50:54 +00:00
2025-11-04 02:12:25 +00:00
#include "arch/Arch.h"
#include "arch/ArchException.h"
#include "base/EventQueue.h"
2024-12-18 01:42:03 +00:00
#include "base/IEventQueue.h"
#include "base/Log.h"
#include "base/TMethodJob.h"
#include "client/Client.h"
#include "common/Settings.h"
2024-12-18 01:42:03 +00:00
#include "deskflow/ClientApp.h"
#include "deskflow/Clipboard.h"
2024-12-18 01:42:03 +00:00
#include "deskflow/DisplayInvalidException.h"
#include "deskflow/KeyMap.h"
#include "mt/CondVar.h"
#include "mt/Lock.h"
#include "mt/Mutex.h"
#include "mt/Thread.h"
2024-12-18 01:42:03 +00:00
#include "platform/OSXClipboard.h"
#include "platform/OSXEventQueueBuffer.h"
#include "platform/OSXKeyState.h"
#include "platform/OSXMediaKeySupport.h"
#include "platform/OSXPasteboardPeeker.h"
#include "platform/OSXScreenSaver.h"
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
#include <AppKit/NSEvent.h>
2012-06-10 16:50:54 +00:00
#include <AvailabilityMacros.h>
#include <IOKit/hidsystem/event_status_driver.h>
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
2021-06-29 10:20:56 +00:00
#include <libproc.h>
2024-12-18 01:42:03 +00:00
#include <mach-o/dyld.h>
#include <math.h>
2012-06-10 16:50:54 +00:00
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
// The following creates a section that tells Mac OS X
// that it is OK to let us inject input in the login screen.
// Just the name of the section is important, not its contents.
2024-12-18 01:42:03 +00:00
__attribute__((used)) __attribute__((section("__CGPreLoginApp,__cgpreloginapp"))) static const char magic_section[] =
"";
////////////////////////////////////////////////////////////
2012-06-10 16:50:54 +00:00
// This isn't in any Apple SDK that I know of as of yet.
2024-12-18 01:42:03 +00:00
enum
{
kDeskflowEventMouseScroll = 11,
kDeskflowMouseScrollAxisX = 'saxx',
kDeskflowMouseScrollAxisY = 'saxy'
2012-06-10 16:50:54 +00:00
};
static const double kCarbonLoopWaitTimeout = 10.0;
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
2021-06-29 10:20:56 +00:00
int getSecureInputEventPID();
std::string getProcessName(int pid);
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
2021-06-29 10:20:56 +00:00
// TODO: upgrade deprecated function usage in these functions.
void setZeroSuppressionInterval();
void avoidSupression();
void logCursorVisibility();
void avoidHesitatingCursor();
2012-06-10 16:50:54 +00:00
//
2014-11-11 13:51:47 +00:00
// OSXScreen
2012-06-10 16:50:54 +00:00
//
2024-12-18 01:42:03 +00:00
bool OSXScreen::s_testedForGHOM = false;
bool OSXScreen::s_hasGHOM = false;
OSXScreen::OSXScreen(IEventQueue *events, bool isPrimary, bool enableLangSync)
: PlatformScreen(events),
2024-12-18 01:42:03 +00:00
m_isPrimary(isPrimary),
m_isOnScreen(m_isPrimary),
m_cursorPosValid(false),
MouseButtonEventMap(NumButtonIDs),
m_cursorHidden(false),
m_keyState(nullptr),
2024-12-18 01:42:03 +00:00
m_sequenceNumber(0),
m_screensaver(nullptr),
2024-12-18 01:42:03 +00:00
m_screensaverNotify(false),
m_ownClipboard(false),
m_clipboardTimer(nullptr),
m_hiddenWindow(nullptr),
m_userInputWindow(nullptr),
2024-12-18 01:42:03 +00:00
m_switchEventHandlerRef(0),
m_pmMutex(new Mutex),
m_pmWatchThread(nullptr),
2024-12-18 01:42:03 +00:00
m_pmThreadReady(new CondVar<bool>(m_pmMutex, false)),
m_pmRootPort(0),
m_activeModifierHotKey(0),
m_activeModifierHotKeyMask(0),
m_eventTapPort(nullptr),
m_eventTapRLSR(nullptr),
m_lastClickTime(0),
m_clickState(1),
m_lastSingleClickXCursor(0),
m_lastSingleClickYCursor(0),
m_events(events),
m_impl(nullptr)
2024-12-18 01:42:03 +00:00
{
m_displayID = CGMainDisplayID();
if (!updateScreenShape(m_displayID, 0)) {
throw DisplayInvalidException("failed to initialize screen shape");
}
try {
m_screensaver = new OSXScreenSaver(m_events, getEventTarget());
m_keyState = new OSXKeyState(m_events, AppUtil::instance().getKeyboardLayoutList(), enableLangSync);
if (Settings::value(Settings::Core::PreventSleep).toBool()) {
2024-12-18 01:42:03 +00:00
m_powerManager.disableSleep();
}
2024-12-18 01:42:03 +00:00
// only needed when running as a server.
if (m_isPrimary) {
// we can't pass options to show the dialog, this must be done by the gui.
if (!AXIsProcessTrusted()) {
throw std::runtime_error("assistive devices does not trust this process, allow it in system settings.");
2024-12-18 01:42:03 +00:00
}
}
// install display manager notification handler
CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this);
// install fast user switching event handler
EventTypeSpec switchEventTypes[2];
switchEventTypes[0].eventClass = kEventClassSystem;
switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated;
switchEventTypes[1].eventClass = kEventClassSystem;
switchEventTypes[1].eventKind = kEventSystemUserSessionActivated;
EventHandlerUPP switchEventHandler = NewEventHandlerUPP(userSwitchCallback);
InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes, this, &m_switchEventHandlerRef);
DisposeEventHandlerUPP(switchEventHandler);
constructMouseButtonEventMap();
// watch for requests to sleep
m_events->addHandler(EventTypes::OsxScreenConfirmSleep, getEventTarget(), [this](const auto &e) {
handleConfirmSleep(e);
});
2024-12-18 01:42:03 +00:00
// create thread for monitoring system power state.
*m_pmThreadReady = false;
m_carbonLoopMutex = new Mutex();
m_carbonLoopReady = new CondVar<bool>(m_carbonLoopMutex, false);
LOG_DEBUG("starting watchSystemPowerThread");
2024-12-18 01:42:03 +00:00
m_pmWatchThread = new Thread(new TMethodJob<OSXScreen>(this, &OSXScreen::watchSystemPowerThread));
} catch (...) {
m_events->removeHandler(EventTypes::OsxScreenConfirmSleep, getEventTarget());
2024-12-18 01:42:03 +00:00
if (m_switchEventHandlerRef != 0) {
RemoveEventHandler(m_switchEventHandlerRef);
}
2024-12-18 01:42:03 +00:00
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
delete m_keyState;
delete m_screensaver;
throw;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// install event handlers
m_events->addHandler(EventTypes::System, m_events->getSystemTarget(), [this](const auto &e) {
handleSystemEvent(e);
});
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// install the platform event queue
m_events->adoptBuffer(new OSXEventQueueBuffer(m_events));
2012-06-10 16:50:54 +00:00
}
2014-11-11 13:51:47 +00:00
OSXScreen::~OSXScreen()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
disable();
m_events->adoptBuffer(nullptr);
m_events->removeHandler(EventTypes::System, m_events->getSystemTarget());
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (m_pmWatchThread) {
// make sure the thread has setup the runloop.
{
Lock lock(m_pmMutex);
while (!(bool)*m_pmThreadReady) {
m_pmThreadReady->wait();
}
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// now exit the thread's runloop and wait for it to exit
LOG_DEBUG("stopping watchSystemPowerThread");
2024-12-18 01:42:03 +00:00
CFRunLoopStop(m_pmRunloop);
m_pmWatchThread->wait();
delete m_pmWatchThread;
m_pmWatchThread = nullptr;
2024-12-18 01:42:03 +00:00
}
delete m_pmThreadReady;
delete m_pmMutex;
m_events->removeHandler(EventTypes::OsxScreenConfirmSleep, getEventTarget());
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
RemoveEventHandler(m_switchEventHandlerRef);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
delete m_keyState;
delete m_screensaver;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
delete m_carbonLoopMutex;
delete m_carbonLoopReady;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void *OSXScreen::getEventTarget() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return const_cast<OSXScreen *>(this);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::getClipboard(ClipboardID, IClipboard *dst) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
Clipboard::copy(dst, &m_pasteboard);
return true;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::getShape(int32_t &x, int32_t &y, int32_t &w, int32_t &h) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
x = m_x;
y = m_y;
w = m_w;
h = m_h;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::getCursorPos(int32_t &x, int32_t &y) const
2012-06-10 16:50:54 +00:00
{
CGEventRef event = CGEventCreate(nullptr);
2024-12-18 01:42:03 +00:00
CGPoint mouse = CGEventGetLocation(event);
x = mouse.x;
y = mouse.y;
m_cursorPosValid = true;
m_xCursor = x;
m_yCursor = y;
CFRelease(event);
2012-06-10 16:50:54 +00:00
}
void OSXScreen::reconfigure(uint32_t activeSides)
2012-06-10 16:50:54 +00:00
{
const static auto sidesText = sidesMaskToString(activeSides);
LOG_DEBUG("active sides: %s (0x%02x)", sidesText.c_str(), activeSides);
m_activeSides = activeSides;
}
uint32_t OSXScreen::activeSides()
{
return m_activeSides;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::warpCursor(int32_t x, int32_t y)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// move cursor without generating events
CGPoint pos;
pos.x = x;
pos.y = y;
CGWarpMouseCursorPosition(pos);
// save new cursor position
m_xCursor = x;
m_yCursor = y;
m_cursorPosValid = true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::fakeInputBegin()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// FIXME -- not implemented
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::fakeInputEnd()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// FIXME -- not implemented
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
int32_t OSXScreen::getJumpZoneSize() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return 1;
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
bool OSXScreen::isAnyMouseButtonDown(uint32_t &buttonID) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
if (m_buttonState.test(0)) {
buttonID = kButtonLeft;
return true;
}
2024-12-18 01:42:03 +00:00
return (GetCurrentButtonState() != 0);
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::getCursorCenter(int32_t &x, int32_t &y) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
x = m_xCenter;
y = m_yCenter;
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
uint32_t OSXScreen::registerHotKey(KeyID key, KeyModifierMask mask)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// get mac virtual key and modifier mask matching deskflow key and mask
2025-01-11 02:13:19 +00:00
uint32_t macKey, macMask;
2024-12-18 01:42:03 +00:00
if (!m_keyState->mapDeskflowHotKeyToMac(key, mask, macKey, macMask)) {
LOG_DEBUG("could not map hotkey id=%04x mask=%04x", key, mask);
2024-12-18 01:42:03 +00:00
return 0;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// choose hotkey id
2025-01-11 02:13:19 +00:00
uint32_t id;
2024-12-18 01:42:03 +00:00
if (!m_oldHotKeyIDs.empty()) {
id = m_oldHotKeyIDs.back();
m_oldHotKeyIDs.pop_back();
} else {
id = m_hotKeys.size() + 1;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// if this hot key has modifiers only then we'll handle it specially
EventHotKeyRef ref = nullptr;
2024-12-18 01:42:03 +00:00
bool okay;
if (key == kKeyNone) {
if (m_modifierHotKeys.count(mask) > 0) {
// already registered
okay = false;
} else {
m_modifierHotKeys[mask] = id;
okay = true;
}
} else {
2025-01-11 02:13:19 +00:00
EventHotKeyID hkid = {'SNRG', (uint32_t)id};
2024-12-18 01:42:03 +00:00
OSStatus status = RegisterEventHotKey(macKey, macMask, hkid, GetApplicationEventTarget(), 0, &ref);
okay = (status == noErr);
m_hotKeyToIDMap[HotKeyItem(macKey, macMask)] = id;
}
if (!okay) {
m_oldHotKeyIDs.push_back(id);
m_hotKeyToIDMap.erase(HotKeyItem(macKey, macMask));
LOG_WARN(
"failed to register hotkey %s (id=%04x mask=%04x)", deskflow::KeyMap::formatKey(key, mask).c_str(), key, mask
2024-12-18 01:42:03 +00:00
);
return 0;
}
m_hotKeys.insert(std::make_pair(id, HotKeyItem(ref, macKey, macMask)));
LOG_DEBUG(
"registered hotkey %s (id=%04x mask=%04x) as id=%d", deskflow::KeyMap::formatKey(key, mask).c_str(), key, mask, id
2024-12-18 01:42:03 +00:00
);
return id;
}
2025-01-11 02:13:19 +00:00
void OSXScreen::unregisterHotKey(uint32_t id)
2024-12-18 01:42:03 +00:00
{
// look up hotkey
HotKeyMap::iterator i = m_hotKeys.find(id);
if (i == m_hotKeys.end()) {
return;
}
// unregister with OS
bool okay;
if (i->second.getRef() != nullptr) {
2024-12-18 01:42:03 +00:00
okay = (UnregisterEventHotKey(i->second.getRef()) == noErr);
} else {
okay = false;
// XXX -- this is inefficient
for (ModifierHotKeyMap::iterator j = m_modifierHotKeys.begin(); j != m_modifierHotKeys.end(); ++j) {
if (j->second == id) {
m_modifierHotKeys.erase(j);
okay = true;
break;
}
}
}
if (!okay) {
LOG_WARN("failed to unregister hotkey id=%d", id);
2024-12-18 01:42:03 +00:00
} else {
LOG_DEBUG("unregistered hotkey id=%d", id);
2024-12-18 01:42:03 +00:00
}
// discard hot key from map and record old id for reuse
m_hotKeyToIDMap.erase(i->second);
m_hotKeys.erase(i);
m_oldHotKeyIDs.push_back(id);
if (m_activeModifierHotKey == id) {
m_activeModifierHotKey = 0;
m_activeModifierHotKeyMask = 0;
}
}
void OSXScreen::constructMouseButtonEventMap()
{
const CGEventType source[NumButtonIDs][3] = {
{kCGEventLeftMouseUp, kCGEventLeftMouseDragged, kCGEventLeftMouseDown},
{kCGEventRightMouseUp, kCGEventRightMouseDragged, kCGEventRightMouseDown},
{kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown},
{kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown},
{kCGEventOtherMouseUp, kCGEventOtherMouseDragged, kCGEventOtherMouseDown}
};
2025-01-11 00:58:26 +00:00
for (uint16_t button = 0; button < NumButtonIDs; button++) {
2024-12-18 01:42:03 +00:00
MouseButtonEventMapType new_map;
2025-01-11 02:13:19 +00:00
for (uint16_t state = (uint32_t)kMouseButtonUp; state < kMouseButtonStateMax; state++) {
2024-12-18 01:42:03 +00:00
CGEventType curEvent = source[button][state];
new_map[state] = curEvent;
}
MouseButtonEventMap[button] = new_map;
}
}
void OSXScreen::postMouseEvent(CGPoint &pos) const
{
// check if cursor position is valid on the client display configuration
// stkamp@users.sourceforge.net
CGDisplayCount displayCount = 0;
CGGetDisplaysWithPoint(pos, 0, nullptr, &displayCount);
2024-12-18 01:42:03 +00:00
if (displayCount == 0) {
// cursor position invalid - clamp to bounds of last valid display.
// find the last valid display using the last cursor position.
displayCount = 0;
CGDirectDisplayID displayID;
CGGetDisplaysWithPoint(CGPointMake(m_xCursor, m_yCursor), 1, &displayID, &displayCount);
if (displayCount != 0) {
CGRect displayRect = CGDisplayBounds(displayID);
if (pos.x < displayRect.origin.x) {
pos.x = displayRect.origin.x;
} else if (pos.x > displayRect.origin.x + displayRect.size.width - 1) {
pos.x = displayRect.origin.x + displayRect.size.width - 1;
}
if (pos.y < displayRect.origin.y) {
pos.y = displayRect.origin.y;
} else if (pos.y > displayRect.origin.y + displayRect.size.height - 1) {
pos.y = displayRect.origin.y + displayRect.size.height - 1;
}
}
}
CGEventType type = kCGEventMouseMoved;
2025-01-10 23:42:34 +00:00
int8_t button = m_buttonState.getFirstButtonDown();
2024-12-18 01:42:03 +00:00
if (button != -1) {
MouseButtonEventMapType thisButtonType = MouseButtonEventMap[button];
type = thisButtonType[kMouseButtonDragged];
}
CGEventRef event = CGEventCreateMouseEvent(nullptr, type, pos, static_cast<CGMouseButton>(button));
2024-12-18 01:42:03 +00:00
// Dragging events also need the click state
CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// Fix for sticky keys
CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
CGEventSetFlags(event, modifiers);
// Set movement deltas to fix issues with certain 3D programs
SInt64 deltaX = pos.x;
deltaX -= m_xCursor;
SInt64 deltaY = pos.y;
deltaY -= m_yCursor;
CGEventSetIntegerValueField(event, kCGMouseEventDeltaX, deltaX);
CGEventSetIntegerValueField(event, kCGMouseEventDeltaY, deltaY);
double deltaFX = deltaX;
double deltaFY = deltaY;
CGEventSetDoubleValueField(event, kCGMouseEventDeltaX, deltaFX);
CGEventSetDoubleValueField(event, kCGMouseEventDeltaY, deltaFY);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::fakeMouseButton(ButtonID id, bool press)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// Buttons are indexed from one, but the button down array is indexed from zero
2025-01-11 02:13:19 +00:00
uint32_t index = mapDeskflowButtonToMac(id) - kButtonLeft;
2024-12-18 01:42:03 +00:00
if (index >= NumButtonIDs) {
return;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CGPoint pos;
if (!m_cursorPosValid) {
2025-01-11 01:11:56 +00:00
int32_t x, y;
2024-12-18 01:42:03 +00:00
getCursorPos(x, y);
}
pos.x = m_xCursor;
pos.y = m_yCursor;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// variable used to detect mouse coordinate differences between
// old & new mouse clicks. Used in double click detection.
2025-01-11 01:11:56 +00:00
int32_t xDiff = m_xCursor - m_lastSingleClickXCursor;
int32_t yDiff = m_yCursor - m_lastSingleClickYCursor;
2024-12-18 01:42:03 +00:00
double diff = sqrt(xDiff * xDiff + yDiff * yDiff);
// max sqrt(x^2 + y^2) difference allowed to double click
// since we don't have double click distance in NX APIs
// we define our own defaults.
const double maxDiff = sqrt(2) + 0.0001;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
double clickTime = [NSEvent doubleClickInterval];
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// As long as the click is within the time window and distance window
// increase clickState (double click, triple click, etc)
// This will allow for higher than triple click but the quartz documenation
// does not specify that this should be limited to triple click
if (press) {
if ((Arch::time() - m_lastClickTime) <= clickTime && diff <= maxDiff) {
2024-12-18 01:42:03 +00:00
m_clickState++;
} else {
m_clickState = 1;
}
2012-06-10 16:50:54 +00:00
m_lastClickTime = Arch::time();
2024-12-18 01:42:03 +00:00
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (m_clickState == 1) {
m_lastSingleClickXCursor = m_xCursor;
m_lastSingleClickYCursor = m_yCursor;
}
2024-12-18 01:42:03 +00:00
EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp;
LOG_DEBUG1("faking mouse button id: %d press: %s", index, press ? "pressed" : "released");
2024-12-18 01:42:03 +00:00
MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index];
CGEventType type = thisButtonMap[state];
CGEventRef event = CGEventCreateMouseEvent(nullptr, type, pos, static_cast<CGMouseButton>(index));
2024-12-18 01:42:03 +00:00
CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
2024-12-18 01:42:03 +00:00
// Fix for sticky keys
CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
CGEventSetFlags(event, modifiers);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
m_buttonState.set(index, state);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::fakeMouseMove(int32_t x, int32_t y)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// synthesize event
CGPoint pos;
pos.x = x;
pos.y = y;
postMouseEvent(pos);
// save new cursor position
2025-01-11 01:11:56 +00:00
m_xCursor = static_cast<int32_t>(pos.x);
m_yCursor = static_cast<int32_t>(pos.y);
2024-12-18 01:42:03 +00:00
m_cursorPosValid = true;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::fakeMouseRelativeMove(int32_t dx, int32_t dy) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// OS X does not appear to have a fake relative mouse move function.
// simulate it by getting the current mouse position and adding to
// that. this can yield the wrong answer but there's not much else
// we can do.
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// get current position
CGEventRef event = CGEventCreate(nullptr);
2024-12-18 01:42:03 +00:00
CGPoint oldPos = CGEventGetLocation(event);
CFRelease(event);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// synthesize event
CGPoint pos;
2025-01-11 01:11:56 +00:00
m_xCursor = static_cast<int32_t>(oldPos.x);
m_yCursor = static_cast<int32_t>(oldPos.y);
2024-12-18 01:42:03 +00:00
pos.x = oldPos.x + dx;
pos.y = oldPos.y + dy;
postMouseEvent(pos);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// we now assume we don't know the current cursor position
m_cursorPosValid = false;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
void OSXScreen::fakeMouseWheel(int32_t xDelta, int32_t yDelta) const
2024-12-18 01:42:03 +00:00
{
if (xDelta != 0 || yDelta != 0) {
// use server's acceleration with a little boost since other platforms
// take one wheel step as a larger step than the mac does.
auto adjustedDeltas =
applyScrollModifier({static_cast<int32_t>(3.0 * xDelta / 120.0), static_cast<int32_t>(3.0 * y / 120.0)});
2024-12-18 01:42:03 +00:00
// create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine
// is the right choice here over kCGScrollEventUnitPixel
CGEventRef scrollEvent =
CGEventCreateScrollWheelEvent(nullptr, kCGScrollEventUnitLine, 2, adjustedDeltas.y, adjustedDeltas.x);
2024-12-18 01:42:03 +00:00
// Fix for sticky keys
CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
CGEventSetFlags(scrollEvent, modifiers);
CGEventPost(kCGHIDEventTap, scrollEvent);
CFRelease(scrollEvent);
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::showCursor()
2012-06-10 16:50:54 +00:00
{
LOG_DEBUG("showing cursor");
CFStringRef propertyString = CFStringCreateWithCString(nullptr, "SetsCursorInBackground", kCFStringEncodingMacRoman);
2024-12-18 01:42:03 +00:00
CGSSetConnectionProperty(_CGSDefaultConnection(), _CGSDefaultConnection(), propertyString, kCFBooleanTrue);
2024-12-18 01:42:03 +00:00
CFRelease(propertyString);
2024-12-18 01:42:03 +00:00
CGError error = CGDisplayShowCursor(m_displayID);
if (error != kCGErrorSuccess) {
LOG_ERR("failed to show cursor, error=%d", error);
2024-12-18 01:42:03 +00:00
}
2024-12-18 01:42:03 +00:00
// appears to fix "mouse randomly not showing" bug
CGAssociateMouseAndMouseCursorPosition(true);
2024-12-18 01:42:03 +00:00
logCursorVisibility();
2024-12-18 01:42:03 +00:00
m_cursorHidden = false;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::hideCursor()
2012-06-10 16:50:54 +00:00
{
LOG_DEBUG("hiding cursor");
CFStringRef propertyString = CFStringCreateWithCString(nullptr, "SetsCursorInBackground", kCFStringEncodingMacRoman);
2024-12-18 01:42:03 +00:00
CGSSetConnectionProperty(_CGSDefaultConnection(), _CGSDefaultConnection(), propertyString, kCFBooleanTrue);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CFRelease(propertyString);
2024-12-18 01:42:03 +00:00
CGError error = CGDisplayHideCursor(m_displayID);
if (error != kCGErrorSuccess) {
LOG_ERR("failed to hide cursor, error=%d", error);
2024-12-18 01:42:03 +00:00
}
2024-12-18 01:42:03 +00:00
// appears to fix "mouse randomly not hiding" bug
CGAssociateMouseAndMouseCursorPosition(true);
2024-12-18 01:42:03 +00:00
logCursorVisibility();
2024-12-18 01:42:03 +00:00
m_cursorHidden = true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::enable()
{
// watch the clipboard
m_clipboardTimer = m_events->newTimer(1.0, nullptr);
m_events->addHandler(EventTypes::Timer, m_clipboardTimer, [this](const auto &) { checkClipboards(); });
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (m_isPrimary) {
// FIXME -- start watching jump zones
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally)
m_eventTapPort = CGEventTapCreate(
kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents, handleCGInputEvent,
this
);
} else {
// FIXME -- prevent system from entering power save mode
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
hideCursor();
2024-12-18 01:42:03 +00:00
// warp the mouse to the cursor center
fakeMouseMove(m_xCenter, m_yCenter);
// there may be a better way to do this, but we register an event handler even if we're
// not on the primary display (acting as a client). This way, if a local event comes in
// (either keyboard or mouse), we can make sure to show the cursor if we've hidden it.
m_eventTapPort = CGEventTapCreate(
kCGHIDEventTap, kCGHeadInsertEventTap, kCGEventTapOptionDefault, kCGEventMaskForAllEvents,
handleCGInputEventSecondary, this
);
}
if (m_eventTapPort) {
m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0);
if (m_eventTapRLSR) {
CFRunLoopAddSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
} else {
LOG_ERR("failed to create a CFRunLoopSourceRef for the quartz event tap");
2024-12-18 01:42:03 +00:00
}
} else {
LOG_ERR("failed to create quartz event tap");
2024-12-18 01:42:03 +00:00
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::disable()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
showCursor();
2024-12-18 01:42:03 +00:00
// FIXME -- stop watching jump zones, stop capturing input
2024-12-18 01:42:03 +00:00
if (m_eventTapRLSR) {
CFRunLoopRemoveSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
CFRelease(m_eventTapRLSR);
m_eventTapRLSR = nullptr;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (m_eventTapPort) {
CGEventTapEnable(m_eventTapPort, false);
CFRelease(m_eventTapPort);
m_eventTapPort = nullptr;
}
// FIXME -- allow system to enter power saving mode
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// uninstall clipboard timer
if (m_clipboardTimer != nullptr) {
m_events->removeHandler(EventTypes::Timer, m_clipboardTimer);
2024-12-18 01:42:03 +00:00
m_events->deleteTimer(m_clipboardTimer);
m_clipboardTimer = nullptr;
2024-12-18 01:42:03 +00:00
}
m_isOnScreen = m_isPrimary;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::enter()
2012-06-10 16:50:54 +00:00
{
m_isOnScreen = true;
2024-12-18 01:42:03 +00:00
showCursor();
2024-12-18 01:42:03 +00:00
if (m_isPrimary) {
setZeroSuppressionInterval();
} else {
// reset buttons
m_buttonState.reset();
2024-12-18 01:42:03 +00:00
// wakes the client screen
io_registry_entry_t entry =
IORegistryEntryFromPath(kIOMasterPortDefault, "IOService:/IOResources/IODisplayWrangler");
if (entry != MACH_PORT_NULL) {
IORegistryEntrySetCFProperty(entry, CFSTR("IORequestIdle"), kCFBooleanFalse);
IOObjectRelease(entry);
}
avoidSupression();
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::canLeave()
{
2024-12-18 01:42:03 +00:00
return true;
}
2024-12-18 01:42:03 +00:00
void OSXScreen::leave()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
hideCursor();
2024-12-18 01:42:03 +00:00
if (m_isPrimary) {
avoidHesitatingCursor();
}
// now off screen
m_isOnScreen = false;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::setClipboard(ClipboardID, const IClipboard *src)
2012-06-10 16:50:54 +00:00
{
if (src != nullptr) {
LOG_DEBUG("setting clipboard");
2024-12-18 01:42:03 +00:00
Clipboard::copy(&m_pasteboard, src);
}
return true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::checkClipboards()
2012-06-10 16:50:54 +00:00
{
LOG_DEBUG2("checking clipboard");
2024-12-18 01:42:03 +00:00
if (m_pasteboard.synchronize()) {
LOG_DEBUG("clipboard changed");
sendClipboardEvent(EventTypes::ClipboardGrabbed, kClipboardClipboard);
sendClipboardEvent(EventTypes::ClipboardGrabbed, kClipboardSelection);
2024-12-18 01:42:03 +00:00
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::openScreensaver(bool notify)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
m_screensaverNotify = notify;
if (!m_screensaverNotify) {
m_screensaver->disable();
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::closeScreensaver()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
if (!m_screensaverNotify) {
m_screensaver->enable();
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::screensaver(bool activate)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
if (activate) {
m_screensaver->activate();
} else {
m_screensaver->deactivate();
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::resetOptions()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// no options
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::setOptions(const OptionsList &)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// no options
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
void OSXScreen::setSequenceNumber(uint32_t seqNum)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
m_sequenceNumber = seqNum;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::isPrimary() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return m_isPrimary;
2012-06-10 16:50:54 +00:00
}
void OSXScreen::sendEvent(EventTypes type, void *data) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
m_events->addEvent(Event(type, getEventTarget(), data));
2012-06-10 16:50:54 +00:00
}
void OSXScreen::sendClipboardEvent(EventTypes type, ClipboardID id) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
ClipboardInfo *info = (ClipboardInfo *)malloc(sizeof(ClipboardInfo));
info->m_id = id;
info->m_sequenceNumber = m_sequenceNumber;
sendEvent(type, info);
2012-06-10 16:50:54 +00:00
}
void OSXScreen::handleSystemEvent(const Event &event)
2024-12-18 01:42:03 +00:00
{
EventRef *carbonEvent = static_cast<EventRef *>(event.getData());
assert(carbonEvent != nullptr);
2012-06-10 16:50:54 +00:00
2025-01-11 02:13:19 +00:00
uint32_t eventClass = GetEventClass(*carbonEvent);
2024-12-18 01:42:03 +00:00
switch (eventClass) {
case kEventClassMouse:
switch (GetEventKind(*carbonEvent)) {
case kDeskflowEventMouseScroll: {
OSStatus r;
long xScroll;
long yScroll;
// get scroll amount
r = GetEventParameter(
*carbonEvent, kDeskflowMouseScrollAxisX, typeSInt32, nullptr, sizeof(xScroll), nullptr, &xScroll
);
2024-12-18 01:42:03 +00:00
if (r != noErr) {
xScroll = 0;
}
r = GetEventParameter(
*carbonEvent, kDeskflowMouseScrollAxisY, typeSInt32, nullptr, sizeof(yScroll), nullptr, &yScroll
);
2024-12-18 01:42:03 +00:00
if (r != noErr) {
yScroll = 0;
}
if (xScroll != 0 || yScroll != 0) {
onMouseWheel(-mapScrollWheelToDeskflow(xScroll), mapScrollWheelToDeskflow(yScroll));
}
}
}
break;
case kEventClassKeyboard:
switch (GetEventKind(*carbonEvent)) {
case kEventHotKeyPressed:
case kEventHotKeyReleased:
onHotKey(*carbonEvent);
break;
}
break;
case kEventClassWindow:
// 2nd param was formerly GetWindowEventTarget(m_userInputWindow) which is 32-bit only,
// however as m_userInputWindow is never initialized to anything we can take advantage of
// the fact that GetWindowEventTarget(nullptr) == nullptr
SendEventToEventTarget(*carbonEvent, nullptr);
2024-12-18 01:42:03 +00:00
switch (GetEventKind(*carbonEvent)) {
case kEventWindowActivated:
LOG_DEBUG1("window activated");
2024-12-18 01:42:03 +00:00
break;
case kEventWindowDeactivated:
LOG_DEBUG1("window deactivated");
2024-12-18 01:42:03 +00:00
break;
case kEventWindowFocusAcquired:
LOG_DEBUG1("focus acquired");
2024-12-18 01:42:03 +00:00
break;
case kEventWindowFocusRelinquish:
LOG_DEBUG1("focus released");
2024-12-18 01:42:03 +00:00
break;
}
break;
default:
SendEventToEventTarget(*carbonEvent, GetEventDispatcherTarget());
break;
}
2012-06-10 16:50:54 +00:00
}
bool OSXScreen::onMouseMove()
2012-06-10 16:50:54 +00:00
{
// when we receive a mouse-move event, it is possible it was queued for a period
// and that the mouse has already moved again since then. to handle this, we need
// to query the current mouse position rather than using the position in the event.
CGEventRef event = CGEventCreate(NULL);
CGPoint pos = CGEventGetLocation(event);
CFRelease(event);
CGFloat mx = pos.x;
CGFloat my = pos.y;
LOG_DEBUG2("mouse move %+f,%+f", mx, my);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CGFloat x = mx - m_xCursor;
CGFloat y = my - m_yCursor;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) {
return true;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// save position to compute delta of next motion
2025-01-11 01:11:56 +00:00
m_xCursor = (int32_t)mx;
m_yCursor = (int32_t)my;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (m_isOnScreen) {
// motion on primary screen
sendEvent(EventTypes::PrimaryScreenMotionOnPrimary, MotionInfo::alloc(m_xCursor, m_yCursor));
2024-12-18 01:42:03 +00:00
} else {
// motion on secondary screen. warp mouse back to
// center.
warpCursor(m_xCenter, m_yCenter);
// examine the motion. if it's about the distance
// from the center of the screen to an edge then
// it's probably a bogus motion that we want to
// ignore (see warpCursorNoFlush() for a further
// description).
2025-01-11 01:11:56 +00:00
static int32_t bogusZoneSize = 10;
2024-12-18 01:42:03 +00:00
if (-x + bogusZoneSize > m_xCenter - m_x || x + bogusZoneSize > m_x + m_w - m_xCenter ||
-y + bogusZoneSize > m_yCenter - m_y || y + bogusZoneSize > m_y + m_h - m_yCenter) {
LOG_DEBUG("dropped bogus motion %+d,%+d", x, y);
2024-12-18 01:42:03 +00:00
} else {
// send motion
// Accumulate together the move into the running total
static CGFloat m_xFractionalMove = 0;
static CGFloat m_yFractionalMove = 0;
m_xFractionalMove += x;
m_yFractionalMove += y;
// Return the integer part
2025-01-11 01:11:56 +00:00
int32_t intX = (int32_t)m_xFractionalMove;
int32_t intY = (int32_t)m_yFractionalMove;
2024-12-18 01:42:03 +00:00
// And keep only the fractional part
m_xFractionalMove -= intX;
m_yFractionalMove -= intY;
sendEvent(EventTypes::PrimaryScreenMotionOnSecondary, MotionInfo::alloc(intX, intY));
2024-12-18 01:42:03 +00:00
}
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
return true;
2012-06-10 16:50:54 +00:00
}
2025-01-11 00:58:26 +00:00
bool OSXScreen::onMouseButton(bool pressed, uint16_t macButton)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// Buttons 2 and 3 are inverted on the mac
ButtonID button = mapMacButtonToDeskflow(macButton);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (pressed) {
LOG_DEBUG1("event: button press button=%d", button);
2024-12-18 01:42:03 +00:00
if (button != kButtonNone) {
KeyModifierMask mask = m_keyState->getActiveModifiers();
sendEvent(EventTypes::PrimaryScreenButtonDown, ButtonInfo::alloc(button, mask));
2024-12-18 01:42:03 +00:00
}
} else {
LOG_DEBUG1("event: button release button=%d", button);
2024-12-18 01:42:03 +00:00
if (button != kButtonNone) {
KeyModifierMask mask = m_keyState->getActiveModifiers();
sendEvent(EventTypes::PrimaryScreenButtonUp, ButtonInfo::alloc(button, mask));
2024-12-18 01:42:03 +00:00
}
}
return true;
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
bool OSXScreen::onMouseWheel(int32_t xDelta, int32_t yDelta) const
2012-06-10 16:50:54 +00:00
{
LOG_DEBUG1("event: button wheel delta=%+d,%+d", xDelta, yDelta);
sendEvent(EventTypes::PrimaryScreenWheel, WheelInfo::alloc(xDelta, yDelta));
2024-12-18 01:42:03 +00:00
return true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::displayReconfigurationCallback(
CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void *inUserData
)
{
OSXScreen *screen = (OSXScreen *)inUserData;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// Closing or opening the lid when an external monitor is
// connected causes an kCGDisplayBeginConfigurationFlag event
CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag | kCGDisplaySetModeFlag |
kCGDisplayAddFlag | kCGDisplayRemoveFlag | kCGDisplayEnabledFlag |
kCGDisplayDisabledFlag | kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag |
kCGDisplayDesktopShapeChangedFlag;
2012-06-10 16:50:54 +00:00
LOG_DEBUG1("event: display was reconfigured: %x %x %x", flags, mask, flags & mask);
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (flags & mask) { /* Something actually did change */
LOG_DEBUG1("event: screen changed shape; refreshing dimensions");
2024-12-18 01:42:03 +00:00
if (!screen->updateScreenShape(displayID, flags)) {
LOG_ERR("failed to update screen shape during display reconfiguration");
2024-12-18 01:42:03 +00:00
}
}
}
bool OSXScreen::onKey(CGEventRef event)
{
CGEventType eventKind = CGEventGetType(event);
// get the key and active modifiers
2025-01-11 02:13:19 +00:00
uint32_t virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
2024-12-18 01:42:03 +00:00
CGEventFlags macMask = CGEventGetFlags(event);
LOG_DEBUG1("event: Key event kind: %d, keycode=%d", eventKind, virtualKey);
2024-12-18 01:42:03 +00:00
// Special handling to track state of modifiers
if (eventKind == kCGEventFlagsChanged) {
// get old and new modifier state
KeyModifierMask oldMask = getActiveModifiers();
KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);
// if the current set of modifiers exactly matches a modifiers-only
// hot key then generate a hot key down event.
if (m_activeModifierHotKey == 0) {
if (m_modifierHotKeys.count(newMask) > 0) {
m_activeModifierHotKey = m_modifierHotKeys[newMask];
m_activeModifierHotKeyMask = newMask;
m_events->addEvent(
Event(EventTypes::PrimaryScreenHotkeyDown, getEventTarget(), HotKeyInfo::alloc(m_activeModifierHotKey))
);
2024-12-18 01:42:03 +00:00
}
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// if a modifiers-only hot key is active and should no longer be
// then generate a hot key up event.
else if (m_activeModifierHotKey != 0) {
KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask);
if (mask != m_activeModifierHotKeyMask) {
m_events->addEvent(
Event(EventTypes::PrimaryScreenHotkeyUp, getEventTarget(), HotKeyInfo::alloc(m_activeModifierHotKey))
2024-12-18 01:42:03 +00:00
);
m_activeModifierHotKey = 0;
m_activeModifierHotKeyMask = 0;
}
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
return true;
}
// check for hot key
HotKeyToIDMap::const_iterator i =
m_hotKeyToIDMap.find(HotKeyItem(virtualKey, m_keyState->mapModifiersToCarbon(macMask) & 0xff00u));
if (i != m_hotKeyToIDMap.end()) {
2025-01-11 02:13:19 +00:00
uint32_t id = i->second;
2024-12-18 01:42:03 +00:00
// determine event type
EventTypes type;
2024-12-18 01:42:03 +00:00
if (eventKind == kCGEventKeyDown) {
type = EventTypes::PrimaryScreenHotkeyDown;
2024-12-18 01:42:03 +00:00
} else if (eventKind == kCGEventKeyUp) {
type = EventTypes::PrimaryScreenHotkeyUp;
2024-12-18 01:42:03 +00:00
} else {
return false;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
m_events->addEvent(Event(type, getEventTarget(), HotKeyInfo::alloc(id)));
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
return true;
}
// decode event type
bool down = (eventKind == kCGEventKeyDown);
bool up = (eventKind == kCGEventKeyUp);
bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1);
// map event to keys
KeyModifierMask mask;
OSXKeyState::KeyIDs keys;
KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
if (button == 0) {
return false;
}
// check for AltGr in mask. if set we send neither the AltGr nor
// the super modifiers to clients then remove AltGr before passing
// the modifiers to onKey.
KeyModifierMask sendMask = (mask & ~KeyModifierAltGr);
if ((mask & KeyModifierAltGr) != 0) {
sendMask &= ~KeyModifierSuper;
}
mask &= ~KeyModifierAltGr;
// update button state
if (down) {
m_keyState->onKey(button, true, mask);
} else if (up) {
if (!m_keyState->isKeyDown(button)) {
// up event for a dead key. throw it away.
return false;
}
m_keyState->onKey(button, false, mask);
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// send key events
for (OSXKeyState::KeyIDs::const_iterator i = keys.begin(); i != keys.end(); ++i) {
m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, *i, sendMask, 1, button);
}
return true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::onMediaKey(CGEventRef event)
{
2024-12-18 01:42:03 +00:00
KeyID keyID;
bool down;
bool isRepeat;
2024-12-18 01:42:03 +00:00
if (!getMediaKeyEventInfo(event, &keyID, &down, &isRepeat)) {
LOG_ERR("Failed to decode media key event");
2024-12-18 01:42:03 +00:00
return;
}
LOG_DEBUG2("Media key event: keyID=0x%02x, %s, repeat=%s", keyID, (down ? "down" : "up"), (isRepeat ? "yes" : "no"));
2024-12-18 01:42:03 +00:00
KeyButton button = 0;
KeyModifierMask mask = m_keyState->getActiveModifiers();
m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat, keyID, mask, 1, button);
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::onHotKey(EventRef event) const
{
// get the hotkey id
EventHotKeyID hkid;
GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID, nullptr, sizeof(EventHotKeyID), nullptr, &hkid);
2025-01-11 02:13:19 +00:00
uint32_t id = hkid.id;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// determine event type
EventTypes type;
2025-01-11 02:13:19 +00:00
uint32_t eventKind = GetEventKind(event);
2024-12-18 01:42:03 +00:00
if (eventKind == kEventHotKeyPressed) {
type = EventTypes::PrimaryScreenHotkeyDown;
2024-12-18 01:42:03 +00:00
} else if (eventKind == kEventHotKeyReleased) {
type = EventTypes::PrimaryScreenHotkeyUp;
2024-12-18 01:42:03 +00:00
} else {
return false;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
m_events->addEvent(Event(type, getEventTarget(), HotKeyInfo::alloc(id)));
return true;
2012-06-10 16:50:54 +00:00
}
2025-01-11 00:58:26 +00:00
ButtonID OSXScreen::mapDeskflowButtonToMac(uint16_t button) const
{
2024-12-18 01:42:03 +00:00
switch (button) {
case 1:
return kButtonLeft;
case 2:
return kMacButtonMiddle;
case 3:
return kMacButtonRight;
case 4:
return kButtonExtra0;
case 5:
return kButtonExtra1;
default:
return kButtonNone;
2024-12-18 01:42:03 +00:00
}
}
2025-01-11 00:58:26 +00:00
ButtonID OSXScreen::mapMacButtonToDeskflow(uint16_t macButton) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
switch (macButton) {
case 1:
return kButtonLeft;
case 2:
return kButtonRight;
case 3:
return kButtonMiddle;
case 4:
return kButtonExtra0;
case 5:
return kButtonExtra1;
default:
return kButtonNone;
2024-12-18 01:42:03 +00:00
}
2012-06-10 16:50:54 +00:00
}
2025-01-11 01:11:56 +00:00
int32_t OSXScreen::mapScrollWheelToDeskflow(int32_t x) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// return accelerated scrolling
double d = (1.0 + getScrollSpeed()) * x;
2025-01-11 01:11:56 +00:00
return static_cast<int32_t>(120.0 * d);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
double OSXScreen::getScrollSpeed() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
double scaling = 0.0;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CFPropertyListRef pref = ::CFPreferencesCopyValue(
CFSTR("com.apple.scrollwheel.scaling"), kCFPreferencesAnyApplication, kCFPreferencesCurrentUser,
kCFPreferencesAnyHost
);
if (pref != nullptr) {
2024-12-18 01:42:03 +00:00
CFTypeID id = CFGetTypeID(pref);
if (id == CFNumberGetTypeID()) {
CFNumberRef value = static_cast<CFNumberRef>(pref);
if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) {
if (scaling < 0.0) {
scaling = 0.0;
}
}
}
CFRelease(pref);
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
return scaling;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::updateButtons()
2012-06-10 16:50:54 +00:00
{
2025-01-11 02:13:19 +00:00
uint32_t buttons = GetCurrentButtonState();
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
m_buttonState.overwrite(buttons);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
IKeyState *OSXScreen::getKeyState() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return m_keyState;
2012-06-10 16:50:54 +00:00
}
bool OSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags flags)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return updateScreenShape();
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::updateScreenShape()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// get info for each display
CGDisplayCount displayCount = 0;
2012-06-10 16:50:54 +00:00
if (CGGetActiveDisplayList(0, nullptr, &displayCount) != CGDisplayNoErr) {
2024-12-18 01:42:03 +00:00
return false;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (displayCount == 0) {
return false;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
CGDirectDisplayID *displays = new CGDirectDisplayID[displayCount];
if (displays == nullptr) {
2024-12-18 01:42:03 +00:00
return false;
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (CGGetActiveDisplayList(displayCount, displays, &displayCount) != CGDisplayNoErr) {
delete[] displays;
return false;
}
// get smallest rect enclosing all display rects
CGRect totalBounds = CGRectZero;
for (CGDisplayCount i = 0; i < displayCount; ++i) {
CGRect bounds = CGDisplayBounds(displays[i]);
totalBounds = CGRectUnion(totalBounds, bounds);
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// get shape of default screen
2025-01-11 01:11:56 +00:00
m_x = (int32_t)totalBounds.origin.x;
m_y = (int32_t)totalBounds.origin.y;
m_w = (int32_t)totalBounds.size.width;
m_h = (int32_t)totalBounds.size.height;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// get center of default screen
2012-06-10 16:50:54 +00:00
CGDirectDisplayID main = CGMainDisplayID();
const CGRect rect = CGDisplayBounds(main);
m_xCenter = (rect.origin.x + rect.size.width) / 2;
m_yCenter = (rect.origin.y + rect.size.height) / 2;
2024-12-18 01:42:03 +00:00
delete[] displays;
// We want to notify the peer screen whether we are primary screen or not
sendEvent(EventTypes::ScreenShapeChanged);
2012-06-10 16:50:54 +00:00
LOG_DEBUG(
"screen shape: center=%d,%d size=%dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount,
(displayCount == 1) ? "display" : "displays"
2024-12-18 01:42:03 +00:00
);
2024-12-18 01:42:03 +00:00
return true;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
#pragma mark -
2012-06-10 16:50:54 +00:00
//
// FAST USER SWITCH NOTIFICATION SUPPORT
//
2014-11-11 13:51:47 +00:00
// OSXScreen::userSwitchCallback(void*)
2024-12-18 01:42:03 +00:00
//
2012-06-10 16:50:54 +00:00
// gets called if a fast user switch occurs
//
2024-12-18 01:42:03 +00:00
pascal OSStatus OSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler, EventRef theEvent, void *inUserData)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
OSXScreen *screen = (OSXScreen *)inUserData;
2025-01-11 02:13:19 +00:00
uint32_t kind = GetEventKind(theEvent);
2024-12-18 01:42:03 +00:00
IEventQueue *events = screen->getEvents();
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
if (kind == kEventSystemUserSessionDeactivated) {
LOG_DEBUG("user session deactivated");
events->addEvent(Event(EventTypes::ScreenSuspend, screen->getEventTarget()));
2024-12-18 01:42:03 +00:00
} else if (kind == kEventSystemUserSessionActivated) {
LOG_DEBUG("user session activated");
events->addEvent(Event(EventTypes::ScreenResume, screen->getEventTarget()));
2024-12-18 01:42:03 +00:00
}
return (CallNextEventHandler(nextHandler, theEvent));
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
#pragma mark -
2012-06-10 16:50:54 +00:00
//
// SLEEP/WAKEUP NOTIFICATION SUPPORT
//
2014-11-11 13:51:47 +00:00
// OSXScreen::watchSystemPowerThread(void*)
2024-12-18 01:42:03 +00:00
//
2012-06-10 16:50:54 +00:00
// main of thread monitoring system power (sleep/wakup) using a CFRunLoop
//
2025-05-23 20:52:46 +00:00
void OSXScreen::watchSystemPowerThread(const void *)
2024-12-18 01:42:03 +00:00
{
io_object_t notifier;
IONotificationPortRef notificationPortRef;
CFRunLoopSourceRef runloopSourceRef = 0;
m_pmRunloop = CFRunLoopGetCurrent();
// install system power change callback
m_pmRootPort = IORegisterForSystemPower(this, &notificationPortRef, powerChangeCallback, &notifier);
if (m_pmRootPort == 0) {
LOG_WARN("IORegisterForSystemPower failed");
2024-12-18 01:42:03 +00:00
} else {
runloopSourceRef = IONotificationPortGetRunLoopSource(notificationPortRef);
CFRunLoopAddSource(m_pmRunloop, runloopSourceRef, kCFRunLoopCommonModes);
}
// thread is ready
{
Lock lock(m_pmMutex);
*m_pmThreadReady = true;
m_pmThreadReady->signal();
}
// if we were unable to initialize then exit. we must do this after
// setting m_pmThreadReady to true otherwise the parent thread will
// block waiting for it.
if (m_pmRootPort == 0) {
LOG_WARN("failed to init watchSystemPowerThread");
2024-12-18 01:42:03 +00:00
return;
}
LOG_DEBUG("started watchSystemPowerThread");
2024-12-18 01:42:03 +00:00
LOG_DEBUG("waiting for event loop");
2024-12-18 01:42:03 +00:00
m_events->waitForReady();
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
{
Lock lockCarbon(m_carbonLoopMutex);
if (*m_carbonLoopReady == false) {
// we signalling carbon loop ready before starting
// unless we know how to do it within the loop
LOG_DEBUG("signalling carbon loop ready");
2024-12-18 01:42:03 +00:00
*m_carbonLoopReady = true;
m_carbonLoopReady->signal();
}
}
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
// start the run loop
LOG_DEBUG("starting carbon loop");
2024-12-18 01:42:03 +00:00
CFRunLoopRun();
LOG_DEBUG("carbon loop has stopped");
2024-12-18 01:42:03 +00:00
// cleanup
if (notificationPortRef) {
CFRunLoopRemoveSource(m_pmRunloop, runloopSourceRef, kCFRunLoopDefaultMode);
CFRunLoopSourceInvalidate(runloopSourceRef);
CFRelease(runloopSourceRef);
}
Lock lock(m_pmMutex);
IODeregisterForSystemPower(&notifier);
m_pmRootPort = 0;
LOG_DEBUG("stopped watchSystemPowerThread");
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::powerChangeCallback(void *refcon, io_service_t service, natural_t messageType, void *messageArg)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
((OSXScreen *)refcon)->handlePowerChangeRequest(messageType, messageArg);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::handlePowerChangeRequest(natural_t messageType, void *messageArg)
{
// we've received a power change notification
switch (messageType) {
case kIOMessageSystemWillSleep:
// OSXScreen has to handle this in the main thread so we have to
// queue a confirm sleep event here. we actually don't allow the
// system to sleep until the event is handled.
m_events->addEvent(
Event(EventTypes::OsxScreenConfirmSleep, getEventTarget(), messageArg, Event::EventFlags::DontFreeData)
);
2024-12-18 01:42:03 +00:00
return;
2012-06-10 16:50:54 +00:00
2024-12-18 01:42:03 +00:00
case kIOMessageSystemHasPoweredOn:
LOG_DEBUG("system wakeup");
m_events->addEvent(Event(EventTypes::ScreenResume, getEventTarget()));
2024-12-18 01:42:03 +00:00
break;
default:
break;
}
Lock lock(m_pmMutex);
if (m_pmRootPort != 0) {
IOAllowPowerChange(m_pmRootPort, (long)messageArg);
}
2012-06-10 16:50:54 +00:00
}
void OSXScreen::handleConfirmSleep(const Event &event)
2024-12-18 01:42:03 +00:00
{
long messageArg = (long)event.getData();
if (messageArg != 0) {
Lock lock(m_pmMutex);
if (m_pmRootPort != 0) {
// deliver suspend event immediately.
m_events->addEvent(
Event(EventTypes::ScreenSuspend, getEventTarget(), nullptr, Event::EventFlags::DeliverImmediately)
);
2024-12-18 01:42:03 +00:00
LOG_DEBUG("system will sleep");
2024-12-18 01:42:03 +00:00
IOAllowPowerChange(m_pmRootPort, messageArg);
}
}
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
#pragma mark -
2012-06-10 16:50:54 +00:00
//
// GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3)
//
// CoreGraphics private API (OSX 10.3)
// Source: http://ichiro.nnip.org/osx/Cocoa/GlobalHotkey.html
//
// We load the functions dynamically because they're not available in
// older SDKs. We don't use weak linking because we want users of
// older SDKs to build an app that works on newer systems and older
// SDKs will not provide the symbols.
//
// FIXME: This is hosed as of OS 10.5; patches to repair this are
// a good thing.
//
#if 0
2024-12-18 01:42:03 +00:00
#ifdef __cplusplus
2012-06-10 16:50:54 +00:00
extern "C" {
#endif
typedef int CGSConnection;
typedef enum {
CGSGlobalHotKeyEnable = 0,
CGSGlobalHotKeyDisable = 1,
2012-06-10 16:50:54 +00:00
} CGSGlobalHotKeyOperatingMode;
extern CGSConnection _CGSDefaultConnection(void) WEAK_IMPORT_ATTRIBUTE;
extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode) WEAK_IMPORT_ATTRIBUTE;
extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode) WEAK_IMPORT_ATTRIBUTE;
typedef CGSConnection (*_CGSDefaultConnection_t)(void);
typedef CGError (*CGSGetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode);
typedef CGError (*CGSSetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
static _CGSDefaultConnection_t s__CGSDefaultConnection;
static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode;
static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode;
2024-12-18 01:42:03 +00:00
#ifdef __cplusplus
2012-06-10 16:50:54 +00:00
}
#endif
2024-12-18 01:42:03 +00:00
#define LOOKUP(name_) \
s_##name_ = nullptr; \
2024-12-18 01:42:03 +00:00
if (NSIsSymbolNameDefinedWithHint("_" #name_, "CoreGraphics")) { \
s_##name_ = (name_##_t)NSAddressOfSymbol(NSLookupAndBindSymbolWithHint("_" #name_, "CoreGraphics")); \
}
2012-06-10 16:50:54 +00:00
bool OSXScreen::isGlobalHotKeyOperatingModeAvailable()
2012-06-10 16:50:54 +00:00
{
if (!s_testedForGHOM) {
s_testedForGHOM = true;
LOOKUP(_CGSDefaultConnection);
LOOKUP(CGSGetGlobalHotKeyOperatingMode);
LOOKUP(CGSSetGlobalHotKeyOperatingMode);
s_hasGHOM = (s__CGSDefaultConnection != nullptr &&
s_CGSGetGlobalHotKeyOperatingMode != nullptr &&
s_CGSSetGlobalHotKeyOperatingMode != nullptr);
}
return s_hasGHOM;
2012-06-10 16:50:54 +00:00
}
void OSXScreen::setGlobalHotKeysEnabled(bool enabled)
2012-06-10 16:50:54 +00:00
{
if (isGlobalHotKeyOperatingModeAvailable()) {
CGSConnection conn = s__CGSDefaultConnection();
2012-06-10 16:50:54 +00:00
CGSGlobalHotKeyOperatingMode mode;
s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
2012-06-10 16:50:54 +00:00
if (enabled && mode == CGSGlobalHotKeyDisable) {
s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable);
}
else if (!enabled && mode == CGSGlobalHotKeyEnable) {
s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable);
}
}
2012-06-10 16:50:54 +00:00
}
bool OSXScreen::getGlobalHotKeysEnabled()
2012-06-10 16:50:54 +00:00
{
CGSGlobalHotKeyOperatingMode mode;
if (isGlobalHotKeyOperatingModeAvailable()) {
CGSConnection conn = s__CGSDefaultConnection();
s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
}
else {
mode = CGSGlobalHotKeyEnable;
}
return (mode == CGSGlobalHotKeyEnable);
2012-06-10 16:50:54 +00:00
}
#endif
//
2014-11-11 13:51:47 +00:00
// OSXScreen::HotKeyItem
2012-06-10 16:50:54 +00:00
//
OSXScreen::HotKeyItem::HotKeyItem(uint32_t keycode, uint32_t mask) : m_ref(nullptr), m_keycode(keycode), m_mask(mask)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// do nothing
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
OSXScreen::HotKeyItem::HotKeyItem(EventHotKeyRef ref, uint32_t keycode, uint32_t mask)
2024-12-18 01:42:03 +00:00
: m_ref(ref),
m_keycode(keycode),
m_mask(mask)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// do nothing
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
EventHotKeyRef OSXScreen::HotKeyItem::getRef() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return m_ref;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::HotKeyItem::operator<(const HotKeyItem &x) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return (m_keycode < x.m_keycode || (m_keycode == x.m_keycode && m_mask < x.m_mask));
2012-06-10 16:50:54 +00:00
}
// Quartz event tap support for the secondary display. This makes sure that we
// will show the cursor if a local event comes in while deskflow has the cursor
// off the screen.
2012-06-10 16:50:54 +00:00
CGEventRef
2024-12-18 01:42:03 +00:00
OSXScreen::handleCGInputEventSecondary(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
// this fix is really screwing with the correct show/hide behavior. it
// should be tested better before reintroducing.
return event;
2024-12-18 01:42:03 +00:00
OSXScreen *screen = (OSXScreen *)refcon;
if (screen->m_cursorHidden && type == kCGEventMouseMoved) {
2024-12-18 01:42:03 +00:00
CGPoint pos = CGEventGetLocation(event);
if (pos.x != screen->m_xCenter || pos.y != screen->m_yCenter) {
LOG_DEBUG("show cursor on secondary, type=%d pos=%d,%d", type, pos.x, pos.y);
2024-12-18 01:42:03 +00:00
screen->showCursor();
}
}
return event;
2012-06-10 16:50:54 +00:00
}
// Quartz event tap support
2024-12-18 01:42:03 +00:00
CGEventRef OSXScreen::handleCGInputEvent(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon)
{
OSXScreen *screen = (OSXScreen *)refcon;
switch (type) {
case kCGEventLeftMouseDown:
case kCGEventRightMouseDown:
case kCGEventOtherMouseDown:
screen->onMouseButton(true, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
break;
case kCGEventLeftMouseUp:
case kCGEventRightMouseUp:
case kCGEventOtherMouseUp:
screen->onMouseButton(false, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
break;
case kCGEventLeftMouseDragged:
case kCGEventRightMouseDragged:
case kCGEventOtherMouseDragged:
case kCGEventMouseMoved:
// we intentionally ignore the position in the event here as the events are
// queued and will no longer be accurate when we process them.
screen->onMouseMove();
2024-12-18 01:42:03 +00:00
// The system ignores our cursor-centering calls if
// we don't return the event. This should be harmless,
// but might register as slight movement to other apps
// on the system. It hasn't been a problem before, though.
return event;
break;
case kCGEventScrollWheel:
screen->onMouseWheel(
screen->mapScrollWheelToDeskflow(CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis2)),
screen->mapScrollWheelToDeskflow(CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1))
);
break;
case kCGEventKeyDown:
case kCGEventKeyUp:
case kCGEventFlagsChanged:
screen->onKey(event);
break;
case kCGEventTapDisabledByTimeout:
// Re-enable our event-tap
CGEventTapEnable(screen->m_eventTapPort, true);
LOG_INFO("quartz event tap was disabled by timeout, re-enabling");
2024-12-18 01:42:03 +00:00
break;
case kCGEventTapDisabledByUserInput:
LOG_ERR("quartz event tap was disabled by user input");
2024-12-18 01:42:03 +00:00
break;
case NX_NULLEVENT:
break;
default:
if (type == NX_SYSDEFINED) {
if (isMediaKeyEvent(event)) {
LOG_DEBUG2("detected media key event");
2024-12-18 01:42:03 +00:00
screen->onMediaKey(event);
} else {
LOG_DEBUG2("ignoring unknown system defined event");
2024-12-18 01:42:03 +00:00
return event;
}
break;
}
LOG_DEBUG2("unknown quartz event type: 0x%02x", type);
2024-12-18 01:42:03 +00:00
}
if (screen->m_isOnScreen) {
return event;
} else {
return nullptr;
2024-12-18 01:42:03 +00:00
}
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
void OSXScreen::MouseButtonState::set(uint32_t button, EMouseButtonState state)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
bool newState = (state == kMouseButtonDown);
m_buttons.set(button, newState);
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
bool OSXScreen::MouseButtonState::any()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return m_buttons.any();
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
void OSXScreen::MouseButtonState::reset()
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
m_buttons.reset();
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
void OSXScreen::MouseButtonState::overwrite(uint32_t buttons)
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
m_buttons = std::bitset<NumButtonIDs>(buttons);
2012-06-10 16:50:54 +00:00
}
2025-01-11 02:13:19 +00:00
bool OSXScreen::MouseButtonState::test(uint32_t button) const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
return m_buttons.test(button);
2012-06-10 16:50:54 +00:00
}
2025-01-10 23:42:34 +00:00
int8_t OSXScreen::MouseButtonState::getFirstButtonDown() const
2012-06-10 16:50:54 +00:00
{
2024-12-18 01:42:03 +00:00
if (m_buttons.any()) {
for (unsigned short button = 0; button < m_buttons.size(); button++) {
if (m_buttons.test(button)) {
return button;
}
}
}
return -1;
2012-06-10 16:50:54 +00:00
}
2024-12-18 01:42:03 +00:00
char *OSXScreen::CFStringRefToUTF8String(CFStringRef aString)
{
if (aString == nullptr) {
return nullptr;
2024-12-18 01:42:03 +00:00
}
CFIndex length = CFStringGetLength(aString);
CFIndex maxSize = CFStringGetMaximumSizeForEncoding(length, kCFStringEncodingUTF8);
char *buffer = (char *)malloc(maxSize);
if (!CFStringGetCString(aString, buffer, maxSize, kCFStringEncodingUTF8)) {
free(buffer);
buffer = nullptr;
2024-12-18 01:42:03 +00:00
}
2024-12-18 01:42:03 +00:00
return buffer;
}
2024-12-18 01:42:03 +00:00
void OSXScreen::waitForCarbonLoop() const
{
2024-12-18 01:42:03 +00:00
if (*m_carbonLoopReady) {
LOG_DEBUG("carbon loop already ready");
2024-12-18 01:42:03 +00:00
return;
}
2024-12-18 01:42:03 +00:00
Lock lock(m_carbonLoopMutex);
LOG_DEBUG("waiting for carbon loop");
double timeout = Arch::time() + kCarbonLoopWaitTimeout;
2024-12-18 01:42:03 +00:00
while (!m_carbonLoopReady->wait()) {
if (Arch::time() > timeout) {
LOG_DEBUG("carbon loop not ready, waiting again");
timeout = Arch::time() + kCarbonLoopWaitTimeout;
2024-12-18 01:42:03 +00:00
}
}
LOG_DEBUG("carbon loop ready");
}
std::string OSXScreen::getSecureInputApp() const
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
2021-06-29 10:20:56 +00:00
{
2024-12-18 01:42:03 +00:00
if (IsSecureEventInputEnabled()) {
int secureInputProcessPID = getSecureInputEventPID();
if (secureInputProcessPID == 0)
return "unknown";
return getProcessName(secureInputProcessPID);
}
return "";
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
2021-06-29 10:20:56 +00:00
}
2024-12-18 01:42:03 +00:00
int getSecureInputEventPID()
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
2021-06-29 10:20:56 +00:00
{
2024-12-18 01:42:03 +00:00
io_service_t service = MACH_PORT_NULL, service_root = MACH_PORT_NULL;
mach_port_t masterPort;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
kern_return_t kr = IOMasterPort(MACH_PORT_NULL, &masterPort);
if (kr != KERN_SUCCESS)
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
// 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);
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
std::unique_ptr<std::remove_pointer<CFTypeRef>::type, decltype(&CFRelease)> consoleUsers(
IORegistryEntrySearchCFProperty(
service_root, kIOServicePlane, CFSTR("IOConsoleUsers"), nullptr,
2024-12-18 01:42:03 +00:00
kIORegistryIterateParents | kIORegistryIterateRecursively
),
CFRelease
);
if (!consoleUsers)
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
CFTypeID type = CFGetTypeID(consoleUsers.get());
if (type != CFArrayGetTypeID())
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
CFTypeRef dict = CFArrayGetValueAtIndex((CFArrayRef)consoleUsers.get(), 0);
if (!dict)
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
type = CFGetTypeID(dict);
if (type != CFDictionaryGetTypeID())
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
CFTypeRef secureInputPID = nullptr;
CFDictionaryGetValueIfPresent((CFDictionaryRef)dict, CFSTR("kCGSSessionSecureInputPID"), &secureInputPID);
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
if (secureInputPID == nullptr)
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
type = CFGetTypeID(secureInputPID);
if (type != CFNumberGetTypeID())
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
auto pidRef = (CFNumberRef)secureInputPID;
CFNumberType numberType = CFNumberGetType(pidRef);
if (numberType != kCFNumberSInt32Type)
return 0;
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
2021-06-29 10:20:56 +00:00
2024-12-18 01:42:03 +00:00
int pid;
CFNumberGetValue(pidRef, kCFNumberSInt32Type, &pid);
return pid;
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
2021-06-29 10:20:56 +00:00
}
std::string getProcessName(int pid)
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
2021-06-29 10:20:56 +00:00
{
2024-12-18 01:42:03 +00:00
if (!pid)
return "";
char buf[128];
proc_name(pid, buf, sizeof(buf));
return buf;
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
2021-06-29 10:20:56 +00:00
}
#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
2024-12-18 01:42:03 +00:00
void setZeroSuppressionInterval()
{
2024-12-18 01:42:03 +00:00
CGSetLocalEventsSuppressionInterval(0.0);
}
2024-12-18 01:42:03 +00:00
void avoidSupression()
{
2024-12-18 01:42:03 +00:00
// avoid suppression of local hardware events
// stkamp@users.sourceforge.net
CGSetLocalEventsFilterDuringSupressionState(
kCGEventFilterMaskPermitAllEvents, kCGEventSupressionStateSupressionInterval
);
CGSetLocalEventsFilterDuringSupressionState(
(kCGEventFilterMaskPermitLocalKeyboardEvents | kCGEventFilterMaskPermitSystemDefinedEvents),
kCGEventSupressionStateRemoteMouseDrag
);
}
2024-12-18 01:42:03 +00:00
void logCursorVisibility()
{
2024-12-18 01:42:03 +00:00
// CGCursorIsVisible is probably deprecated because its unreliable.
if (!CGCursorIsVisible()) {
LOG_WARN("cursor may not be visible");
2024-12-18 01:42:03 +00:00
}
}
2024-12-18 01:42:03 +00:00
void avoidHesitatingCursor()
{
2024-12-18 01:42:03 +00:00
// This used to be necessary to get smooth mouse motion on other screens,
// but now is just to avoid a hesitating cursor when transitioning to
// the primary (this) screen.
CGSetLocalEventsSuppressionInterval(0.0001);
}
2014-02-26 15:53:28 +00:00
#pragma GCC diagnostic error "-Wdeprecated-declarations"