From f83da9ae348fccaed46b0e754b5fe56254047af3 Mon Sep 17 00:00:00 2001 From: "Jerry (Xinyu Hou)" Date: Wed, 10 May 2017 16:09:09 +0100 Subject: [PATCH] Use IOHID to fake mouse buttons --- src/lib/platform/OSXIOHID.h | 34 ++++++++ src/lib/platform/OSXIOHID.mm | 141 +++++++++++++++++++++++++++++++ src/lib/platform/OSXKeyState.cpp | 52 ++---------- src/lib/platform/OSXScreen.mm | 93 +++++--------------- 4 files changed, 202 insertions(+), 118 deletions(-) create mode 100644 src/lib/platform/OSXIOHID.h create mode 100644 src/lib/platform/OSXIOHID.mm diff --git a/src/lib/platform/OSXIOHID.h b/src/lib/platform/OSXIOHID.h new file mode 100644 index 00000000..16c18646 --- /dev/null +++ b/src/lib/platform/OSXIOHID.h @@ -0,0 +1,34 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#include + +//! IOHID event on Mac +class OSXIOHID { +public: + void postModifierKeys(UInt32 mask); + void postKey(const UInt8 virtualKeyCode, + const bool down); + void fakeMouseButton(UInt32 button, bool press); + +private: + void postMouseEvent(io_connect_t event, UInt32 type, + NXEventData* ev, IOOptionBits flags, + IOOptionBits options); +}; diff --git a/src/lib/platform/OSXIOHID.mm b/src/lib/platform/OSXIOHID.mm new file mode 100644 index 00000000..803ce724 --- /dev/null +++ b/src/lib/platform/OSXIOHID.mm @@ -0,0 +1,141 @@ +/* + * synergy -- mouse and keyboard sharing utility + * Copyright (C) 2012-2016 Symless Ltd. + * + * This package is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * found in the file LICENSE that should have accompanied this file. + * + * This package is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "platform/OSXIOHID.h" + +#include "base/Log.h" + +#include +#include +#include + +static io_connect_t getEventDriver(void) +{ + static mach_port_t sEventDrvrRef = 0; + mach_port_t masterPort, service, iter; + kern_return_t kr; + + if (!sEventDrvrRef) { + // Get master device port + kr = IOMasterPort(bootstrap_port, &masterPort); + assert(KERN_SUCCESS == kr); + + kr = IOServiceGetMatchingServices(masterPort, + IOServiceMatching(kIOHIDSystemClass), &iter); + assert(KERN_SUCCESS == kr); + + service = IOIteratorNext(iter); + assert(service); + + kr = IOServiceOpen(service, mach_task_self(), + kIOHIDParamConnectType, &sEventDrvrRef); + assert(KERN_SUCCESS == kr); + + IOObjectRelease(service); + IOObjectRelease(iter); + } + + return sEventDrvrRef; +} + +void +OSXIOHID::postModifierKeys(UInt32 mask) +{ + NXEventData event; + bzero(&event, sizeof(NXEventData)); + IOGPoint loc = { 0, 0 }; + kern_return_t kr; + kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc, + &event, kNXEventDataVersion, mask, true); + assert(KERN_SUCCESS == kr); +} + +void +OSXIOHID::postKey(const UInt8 virtualKeyCode, + const bool down) +{ + NXEventData event; + bzero(&event, sizeof(NXEventData)); + IOGPoint loc = { 0, 0 }; + event.key.repeat = false; + event.key.keyCode = virtualKeyCode; + event.key.origCharSet = event.key.charSet = NX_ASCIISET; + event.key.origCharCode = event.key.charCode = 0; + kern_return_t kr; + kr = IOHIDPostEvent(getEventDriver(), + down ? NX_KEYDOWN : NX_KEYUP, + loc, &event, kNXEventDataVersion, 0, false); + assert(KERN_SUCCESS == kr); +} + +void +OSXIOHID::fakeMouseButton(UInt32 button, bool press) +{ + NXEventData event; + memset (&event, 0, sizeof(event)); + + // Mouse presses actually generate two events, one with a bitfield of buttons, one with a button number + event.compound.subType = NX_SUBTYPE_AUX_MOUSE_BUTTONS; + event.compound.misc.L[0] = (1 << button); + event.compound.misc.L[1] = press ? (1 << button) : 0; + postMouseEvent(getEventDriver(), NX_SYSDEFINED, &event, 0, 0); + + memset(&event, 0, sizeof(event)); + event.mouse.buttonNumber = button; + UInt32 type; + + switch (button){ + case 0: + type = press ? NX_LMOUSEDOWN : NX_LMOUSEUP; + break; + case 1: + type = press ? NX_RMOUSEDOWN : NX_RMOUSEUP; + break; + default: + type = press ? NX_OMOUSEDOWN : NX_OMOUSEUP; + } + + if (press) { + event.mouse.pressure = 255; + } + + postMouseEvent(getEventDriver(), type, &event, 0, 0); +} + +void +OSXIOHID::postMouseEvent( + io_connect_t event, UInt32 type, + NXEventData* ev, IOOptionBits flags, + IOOptionBits options) +{ + + IOGPoint location = {0, 0}; + if ((options & kIOHIDSetRelativeCursorPosition) && type != NX_MOUSEMOVED){ + // Mouse button only accepts absolute coordinates + CGEventRef cge = CGEventCreate(nil); + CGPoint loc = CGEventGetLocation(cge); + CFRelease(cge); + location.x = floor(loc.x + ev->mouseMove.dx); + location.y = floor(loc.y + ev->mouseMove.dy); + options = (options & ~kIOHIDSetRelativeCursorPosition) | kIOHIDSetCursorPosition; + } + + kern_return_t res = IOHIDPostEvent(event, type, location, ev, kNXEventDataVersion, flags | NX_NONCOALSESCEDMASK, options); + if (res != kIOReturnSuccess) { + LOG((CLOG_DEBUG1 "IOHIDPostEvent event failed: %x\n", res)); + } +} diff --git a/src/lib/platform/OSXKeyState.cpp b/src/lib/platform/OSXKeyState.cpp index 95492340..cf48b993 100644 --- a/src/lib/platform/OSXKeyState.cpp +++ b/src/lib/platform/OSXKeyState.cpp @@ -16,6 +16,7 @@ * along with this program. If not, see . */ +#include "platform/OSXIOHID.h" #include "platform/OSXKeyState.h" #include "platform/OSXUchrKeyResource.h" #include "platform/OSXMediaKeySupport.h" @@ -470,46 +471,13 @@ OSXKeyState::getKeyMap(synergy::KeyMap& keyMap) } } -static io_connect_t getEventDriver(void) -{ - static mach_port_t sEventDrvrRef = 0; - mach_port_t masterPort, service, iter; - kern_return_t kr; - - if (!sEventDrvrRef) { - // Get master device port - kr = IOMasterPort(bootstrap_port, &masterPort); - assert(KERN_SUCCESS == kr); - - kr = IOServiceGetMatchingServices(masterPort, - IOServiceMatching(kIOHIDSystemClass), &iter); - assert(KERN_SUCCESS == kr); - - service = IOIteratorNext(iter); - assert(service); - - kr = IOServiceOpen(service, mach_task_self(), - kIOHIDParamConnectType, &sEventDrvrRef); - assert(KERN_SUCCESS == kr); - - IOObjectRelease(service); - IOObjectRelease(iter); - } - - return sEventDrvrRef; -} - void OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, const bool postDown) { static UInt32 modifiers = 0; - - NXEventData event; - IOGPoint loc = { 0, 0 }; UInt32 modifiersDelta = 0; - - bzero(&event, sizeof(NXEventData)); + OSXIOHID hid; switch (virtualKeyCode) { @@ -549,22 +517,12 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode, else { modifiers &= ~modifiersDelta; } - - kern_return_t kr; - kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc, - &event, kNXEventDataVersion, modifiers, true); - assert(KERN_SUCCESS == kr); + + hid.postModifierKeys(modifiers); break; default: - event.key.repeat = false; - event.key.keyCode = virtualKeyCode; - event.key.origCharSet = event.key.charSet = NX_ASCIISET; - event.key.origCharCode = event.key.charCode = 0; - kr = IOHIDPostEvent(getEventDriver(), - postDown ? NX_KEYDOWN : NX_KEYUP, - loc, &event, kNXEventDataVersion, 0, false); - assert(KERN_SUCCESS == kr); + hid.postKey(virtualKeyCode, postDown); break; } } diff --git a/src/lib/platform/OSXScreen.mm b/src/lib/platform/OSXScreen.mm index 8e619306..d96ed1ab 100644 --- a/src/lib/platform/OSXScreen.mm +++ b/src/lib/platform/OSXScreen.mm @@ -18,8 +18,8 @@ #include "platform/OSXScreen.h" -#include "base/EventQueue.h" #include "client/Client.h" +#include "platform/OSXIOHID.h" #include "platform/OSXClipboard.h" #include "platform/OSXEventQueueBuffer.h" #include "platform/OSXKeyState.h" @@ -35,16 +35,18 @@ #include "mt/Mutex.h" #include "mt/Thread.h" #include "arch/XArch.h" -#include "base/Log.h" +#include "base/EventQueue.h" #include "base/IEventQueue.h" #include "base/TMethodEventJob.h" #include "base/TMethodJob.h" +#include "base/Log.h" #include #include #include #include #include +#include // This isn't in any Apple SDK that I know of as of yet. enum { @@ -514,67 +516,15 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press) if (index >= NumButtonIDs) { return; } - - CGPoint pos; - if (!m_cursorPosValid) { - SInt32 x, y; - getCursorPos(x, y); - } - pos.x = m_xCursor; - pos.y = m_yCursor; - // variable used to detect mouse coordinate differences between - // old & new mouse clicks. Used in double click detection. - SInt32 xDiff = m_xCursor - m_lastSingleClickXCursor; - SInt32 yDiff = m_yCursor - m_lastSingleClickYCursor; - 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; - - double clickTime = [NSEvent doubleClickInterval]; - - // 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){ - m_clickState++; - } - else { - m_clickState = 1; - } - - m_lastClickTime = ARCH->time(); - } - - if (m_clickState == 1){ - m_lastSingleClickXCursor = m_xCursor; - m_lastSingleClickYCursor = m_yCursor; - } - - EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; - LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released")); - MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; - CGEventType type = thisButtonMap[state]; + OSXIOHID hid; + hid.fakeMouseButton(index, press); - CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast(index)); - - CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState); - - // Fix for sticky keys - CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags(); - CGEventSetFlags(event, modifiers); - + EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp; m_buttonState.set(index, state); - CGEventPost(kCGHIDEventTap, event); - - CFRelease(event); - + if (!press && (id == kButtonLeft)) { if (m_fakeDraggingStarted) { m_getDropTargetThread = new Thread(new TMethodJob( @@ -1894,21 +1844,22 @@ OSXScreen::handleCGInputEventSecondary( CGEventRef event, void* refcon) { - // this fix is really screwing with the correct show/hide behavior. it - // should be tested better before reintroducing. - return event; + CGEventMask mask = kCGEventFlagMaskCommand; + mask = CGEventGetFlags(event); + LOG ((CLOG_INFO "%x", mask)); + auto i = CGEventGetIntegerValueField(event, kCGEventSourceUnixProcessID); + LOG ((CLOG_INFO "Target PID:%lld", i)); - OSXScreen* screen = (OSXScreen*)refcon; - if (screen->m_cursorHidden && type == kCGEventMouseMoved) { + switch(type) { + case kCGEventLeftMouseDown: + case kCGEventRightMouseDown: + case kCGEventOtherMouseDown: + case kCGEventScrollWheel: + case kCGEventKeyDown: + case kCGEventFlagsChanged: + ;//LOG((CLOG_INFO "local input detected")); + } - CGPoint pos = CGEventGetLocation(event); - if (pos.x != screen->m_xCenter || pos.y != screen->m_yCenter) { - - LOG((CLOG_DEBUG "show cursor on secondary, type=%d pos=%d,%d", - type, pos.x, pos.y)); - screen->showCursor(); - } - } return event; }