Use IOHID to fake mouse buttons
This commit is contained in:
parent
271ac9e057
commit
f83da9ae34
|
@ -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);
|
||||||
|
};
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,6 +16,7 @@
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "platform/OSXIOHID.h"
|
||||||
#include "platform/OSXKeyState.h"
|
#include "platform/OSXKeyState.h"
|
||||||
#include "platform/OSXUchrKeyResource.h"
|
#include "platform/OSXUchrKeyResource.h"
|
||||||
#include "platform/OSXMediaKeySupport.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
|
void
|
||||||
OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
|
OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
|
||||||
const bool postDown)
|
const bool postDown)
|
||||||
{
|
{
|
||||||
static UInt32 modifiers = 0;
|
static UInt32 modifiers = 0;
|
||||||
|
|
||||||
NXEventData event;
|
|
||||||
IOGPoint loc = { 0, 0 };
|
|
||||||
UInt32 modifiersDelta = 0;
|
UInt32 modifiersDelta = 0;
|
||||||
|
OSXIOHID hid;
|
||||||
bzero(&event, sizeof(NXEventData));
|
|
||||||
|
|
||||||
switch (virtualKeyCode)
|
switch (virtualKeyCode)
|
||||||
{
|
{
|
||||||
|
@ -549,22 +517,12 @@ OSXKeyState::postHIDVirtualKey(const UInt8 virtualKeyCode,
|
||||||
else {
|
else {
|
||||||
modifiers &= ~modifiersDelta;
|
modifiers &= ~modifiersDelta;
|
||||||
}
|
}
|
||||||
|
|
||||||
kern_return_t kr;
|
hid.postModifierKeys(modifiers);
|
||||||
kr = IOHIDPostEvent(getEventDriver(), NX_FLAGSCHANGED, loc,
|
|
||||||
&event, kNXEventDataVersion, modifiers, true);
|
|
||||||
assert(KERN_SUCCESS == kr);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
event.key.repeat = false;
|
hid.postKey(virtualKeyCode, postDown);
|
||||||
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);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,8 +18,8 @@
|
||||||
|
|
||||||
#include "platform/OSXScreen.h"
|
#include "platform/OSXScreen.h"
|
||||||
|
|
||||||
#include "base/EventQueue.h"
|
|
||||||
#include "client/Client.h"
|
#include "client/Client.h"
|
||||||
|
#include "platform/OSXIOHID.h"
|
||||||
#include "platform/OSXClipboard.h"
|
#include "platform/OSXClipboard.h"
|
||||||
#include "platform/OSXEventQueueBuffer.h"
|
#include "platform/OSXEventQueueBuffer.h"
|
||||||
#include "platform/OSXKeyState.h"
|
#include "platform/OSXKeyState.h"
|
||||||
|
@ -35,16 +35,18 @@
|
||||||
#include "mt/Mutex.h"
|
#include "mt/Mutex.h"
|
||||||
#include "mt/Thread.h"
|
#include "mt/Thread.h"
|
||||||
#include "arch/XArch.h"
|
#include "arch/XArch.h"
|
||||||
#include "base/Log.h"
|
#include "base/EventQueue.h"
|
||||||
#include "base/IEventQueue.h"
|
#include "base/IEventQueue.h"
|
||||||
#include "base/TMethodEventJob.h"
|
#include "base/TMethodEventJob.h"
|
||||||
#include "base/TMethodJob.h"
|
#include "base/TMethodJob.h"
|
||||||
|
#include "base/Log.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
#include <mach-o/dyld.h>
|
#include <mach-o/dyld.h>
|
||||||
#include <AvailabilityMacros.h>
|
#include <AvailabilityMacros.h>
|
||||||
#include <IOKit/hidsystem/event_status_driver.h>
|
#include <IOKit/hidsystem/event_status_driver.h>
|
||||||
#include <AppKit/NSEvent.h>
|
#include <AppKit/NSEvent.h>
|
||||||
|
#include <IOKit/hidsystem/IOHIDLib.h>
|
||||||
|
|
||||||
// This isn't in any Apple SDK that I know of as of yet.
|
// This isn't in any Apple SDK that I know of as of yet.
|
||||||
enum {
|
enum {
|
||||||
|
@ -514,67 +516,15 @@ OSXScreen::fakeMouseButton(ButtonID id, bool press)
|
||||||
if (index >= NumButtonIDs) {
|
if (index >= NumButtonIDs) {
|
||||||
return;
|
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"));
|
LOG((CLOG_DEBUG1 "faking mouse button id: %d press: %s", index, press ? "pressed" : "released"));
|
||||||
|
|
||||||
MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index];
|
OSXIOHID hid;
|
||||||
CGEventType type = thisButtonMap[state];
|
hid.fakeMouseButton(index, press);
|
||||||
|
|
||||||
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, static_cast<CGMouseButton>(index));
|
EMouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp;
|
||||||
|
|
||||||
CGEventSetIntegerValueField(event, kCGMouseEventClickState, m_clickState);
|
|
||||||
|
|
||||||
// Fix for sticky keys
|
|
||||||
CGEventFlags modifiers = m_keyState->getModifierStateAsOSXFlags();
|
|
||||||
CGEventSetFlags(event, modifiers);
|
|
||||||
|
|
||||||
m_buttonState.set(index, state);
|
m_buttonState.set(index, state);
|
||||||
CGEventPost(kCGHIDEventTap, event);
|
|
||||||
|
|
||||||
CFRelease(event);
|
|
||||||
|
|
||||||
if (!press && (id == kButtonLeft)) {
|
if (!press && (id == kButtonLeft)) {
|
||||||
if (m_fakeDraggingStarted) {
|
if (m_fakeDraggingStarted) {
|
||||||
m_getDropTargetThread = new Thread(new TMethodJob<OSXScreen>(
|
m_getDropTargetThread = new Thread(new TMethodJob<OSXScreen>(
|
||||||
|
@ -1894,21 +1844,22 @@ OSXScreen::handleCGInputEventSecondary(
|
||||||
CGEventRef event,
|
CGEventRef event,
|
||||||
void* refcon)
|
void* refcon)
|
||||||
{
|
{
|
||||||
// this fix is really screwing with the correct show/hide behavior. it
|
CGEventMask mask = kCGEventFlagMaskCommand;
|
||||||
// should be tested better before reintroducing.
|
mask = CGEventGetFlags(event);
|
||||||
return event;
|
LOG ((CLOG_INFO "%x", mask));
|
||||||
|
auto i = CGEventGetIntegerValueField(event, kCGEventSourceUnixProcessID);
|
||||||
|
LOG ((CLOG_INFO "Target PID:%lld", i));
|
||||||
|
|
||||||
OSXScreen* screen = (OSXScreen*)refcon;
|
switch(type) {
|
||||||
if (screen->m_cursorHidden && type == kCGEventMouseMoved) {
|
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;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue