Use IOHID to fake mouse buttons

This commit is contained in:
Jerry (Xinyu Hou) 2017-05-10 16:09:09 +01:00
parent 271ac9e057
commit f83da9ae34
4 changed files with 202 additions and 118 deletions

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <Carbon/Carbon.h>
//! 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);
};

View File

@ -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 <http://www.gnu.org/licenses/>.
*/
#include "platform/OSXIOHID.h"
#include "base/Log.h"
#include <IOKit/hidsystem/event_status_driver.h>
#include <AppKit/NSEvent.h>
#include <IOKit/hidsystem/IOHIDLib.h>
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));
}
}

View File

@ -16,6 +16,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#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)
{
@ -550,21 +518,11 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
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;
}
}

View File

@ -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 <math.h>
#include <mach-o/dyld.h>
#include <AvailabilityMacros.h>
#include <IOKit/hidsystem/event_status_driver.h>
#include <AppKit/NSEvent.h>
#include <IOKit/hidsystem/IOHIDLib.h>
// This isn't in any Apple SDK that I know of as of yet.
enum {
@ -515,65 +517,13 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press)
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];
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast<CGMouseButton>(index));
CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
// Fix for sticky keys
CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
CGEventSetFlags(event, modifiers);
OSXIOHID hid;
hid.fakeMouseButton(index, press);
EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp;
m_buttonState.set(index, state);
CGEventPost(kCGHIDEventTap, event);
CFRelease(event);
if (!press && (id == kButtonLeft)) {
if (m_fakeDraggingStarted) {
@ -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) {
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();
}
switch(type) {
case kCGEventLeftMouseDown:
case kCGEventRightMouseDown:
case kCGEventOtherMouseDown:
case kCGEventScrollWheel:
case kCGEventKeyDown:
case kCGEventFlagsChanged:
;//LOG((CLOG_INFO "local input detected"));
}
return event;
}