Update input-event handing to Quartz-taps on OS X
Created by mthiel Issue #238
This commit is contained in:
parent
985648a95f
commit
9515338c75
|
@ -138,40 +138,57 @@ COSXKeyState::mapModifiersFromOSX(UInt32 mask) const
|
||||||
LOG((CLOG_DEBUG1 "mask: %04x", mask));
|
LOG((CLOG_DEBUG1 "mask: %04x", mask));
|
||||||
// convert
|
// convert
|
||||||
KeyModifierMask outMask = 0;
|
KeyModifierMask outMask = 0;
|
||||||
if ((mask & shiftKey) != 0) {
|
if ((mask & kCGEventFlagMaskShift) != 0) {
|
||||||
outMask |= KeyModifierShift;
|
outMask |= KeyModifierShift;
|
||||||
}
|
}
|
||||||
if ((mask & rightShiftKey) != 0) {
|
if ((mask & kCGEventFlagMaskControl) != 0) {
|
||||||
outMask |= KeyModifierShift;
|
|
||||||
}
|
|
||||||
if ((mask & controlKey) != 0) {
|
|
||||||
outMask |= KeyModifierControl;
|
outMask |= KeyModifierControl;
|
||||||
}
|
}
|
||||||
if ((mask & rightControlKey) != 0) {
|
if ((mask & kCGEventFlagMaskAlternate) != 0) {
|
||||||
outMask |= KeyModifierControl;
|
|
||||||
}
|
|
||||||
if ((mask & cmdKey) != 0) {
|
|
||||||
outMask |= KeyModifierAlt;
|
outMask |= KeyModifierAlt;
|
||||||
}
|
}
|
||||||
if ((mask & optionKey) != 0) {
|
if ((mask & kCGEventFlagMaskCommand) != 0) {
|
||||||
outMask |= KeyModifierSuper;
|
outMask |= KeyModifierSuper;
|
||||||
}
|
}
|
||||||
if ((mask & rightOptionKey) != 0) {
|
if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
|
||||||
outMask |= KeyModifierSuper;
|
|
||||||
}
|
|
||||||
if ((mask & alphaLock) != 0) {
|
|
||||||
outMask |= KeyModifierCapsLock;
|
outMask |= KeyModifierCapsLock;
|
||||||
}
|
}
|
||||||
if ((mask & s_osxNumLock) != 0) {
|
if ((mask & kCGEventFlagMaskNumericPad) != 0) {
|
||||||
outMask |= KeyModifierNumLock;
|
outMask |= KeyModifierNumLock;
|
||||||
}
|
}
|
||||||
|
|
||||||
return outMask;
|
return outMask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
KeyModifierMask
|
||||||
|
COSXKeyState::mapModifiersToCarbon(UInt32 mask) const
|
||||||
|
{
|
||||||
|
KeyModifierMask outMask = 0;
|
||||||
|
if ((mask & kCGEventFlagMaskShift) != 0) {
|
||||||
|
outMask |= shiftKey;
|
||||||
|
}
|
||||||
|
if ((mask & kCGEventFlagMaskControl) != 0) {
|
||||||
|
outMask |= controlKey;
|
||||||
|
}
|
||||||
|
if ((mask & kCGEventFlagMaskCommand) != 0) {
|
||||||
|
outMask |= cmdKey;
|
||||||
|
}
|
||||||
|
if ((mask & kCGEventFlagMaskAlternate) != 0) {
|
||||||
|
outMask |= optionKey;
|
||||||
|
}
|
||||||
|
if ((mask & kCGEventFlagMaskAlphaShift) != 0) {
|
||||||
|
outMask |= alphaLock;
|
||||||
|
}
|
||||||
|
if ((mask & kCGEventFlagMaskNumericPad) != 0) {
|
||||||
|
outMask |= s_osxNumLock;
|
||||||
|
}
|
||||||
|
|
||||||
|
return outMask;
|
||||||
|
}
|
||||||
|
|
||||||
KeyButton
|
KeyButton
|
||||||
COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
KeyModifierMask* maskOut, EventRef event) const
|
KeyModifierMask* maskOut, CGEventRef event) const
|
||||||
{
|
{
|
||||||
ids.clear();
|
ids.clear();
|
||||||
|
|
||||||
|
@ -183,13 +200,11 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
}
|
}
|
||||||
|
|
||||||
// get virtual key
|
// get virtual key
|
||||||
UInt32 vkCode;
|
UInt32 vkCode = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
|
||||||
GetEventParameter(event, kEventParamKeyCode, typeUInt32,
|
|
||||||
NULL, sizeof(vkCode), NULL, &vkCode);
|
|
||||||
|
|
||||||
// handle up events
|
// handle up events
|
||||||
UInt32 eventKind = GetEventKind(event);
|
UInt32 eventKind = CGEventGetType(event);
|
||||||
if (eventKind == kEventRawKeyUp) {
|
if (eventKind == kCGEventKeyUp) {
|
||||||
// the id isn't used. we just need the same button we used on
|
// the id isn't used. we just need the same button we used on
|
||||||
// the key press. note that we don't use or reset the dead key
|
// the key press. note that we don't use or reset the dead key
|
||||||
// state; up events should not affect the dead key state.
|
// state; up events should not affect the dead key state.
|
||||||
|
@ -214,9 +229,9 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
|
|
||||||
// get the event modifiers and remove the command and control
|
// get the event modifiers and remove the command and control
|
||||||
// keys. note if we used them though.
|
// keys. note if we used them though.
|
||||||
|
// UCKeyTranslate expects old-style Carbon modifiers, so convert.
|
||||||
UInt32 modifiers;
|
UInt32 modifiers;
|
||||||
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
|
modifiers = mapModifiersToCarbon(CGEventGetFlags(event));
|
||||||
NULL, sizeof(modifiers), NULL, &modifiers);
|
|
||||||
static const UInt32 s_commandModifiers =
|
static const UInt32 s_commandModifiers =
|
||||||
cmdKey | controlKey | rightControlKey;
|
cmdKey | controlKey | rightControlKey;
|
||||||
bool isCommand = ((modifiers & s_commandModifiers) != 0);
|
bool isCommand = ((modifiers & s_commandModifiers) != 0);
|
||||||
|
@ -224,24 +239,22 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
|
|
||||||
// if we've used a command key then we want the glyph produced without
|
// if we've used a command key then we want the glyph produced without
|
||||||
// the option key (i.e. the base glyph).
|
// the option key (i.e. the base glyph).
|
||||||
if (isCommand) {
|
//if (isCommand) {
|
||||||
modifiers &= ~optionKey;
|
modifiers &= ~optionKey;
|
||||||
}
|
//}
|
||||||
|
|
||||||
// choose action
|
// choose action
|
||||||
UInt16 action;
|
UInt16 action;
|
||||||
switch (eventKind) {
|
if(eventKind==kCGEventKeyDown) {
|
||||||
case kEventRawKeyDown:
|
|
||||||
action = kUCKeyActionDown;
|
action = kUCKeyActionDown;
|
||||||
break;
|
}
|
||||||
|
else if(CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat)==1) {
|
||||||
case kEventRawKeyRepeat:
|
|
||||||
action = kUCKeyActionAutoKey;
|
action = kUCKeyActionAutoKey;
|
||||||
break;
|
}
|
||||||
|
else {
|
||||||
default:
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// translate via uchr resource
|
// translate via uchr resource
|
||||||
CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout,
|
CFDataRef ref = (CFDataRef) TISGetInputSourceProperty(currentKeyboardLayout,
|
||||||
kTISPropertyUnicodeKeyLayoutData);
|
kTISPropertyUnicodeKeyLayoutData);
|
||||||
|
@ -251,6 +264,7 @@ COSXKeyState::mapKeyFromEvent(CKeyIDs& ids,
|
||||||
// translate key
|
// translate key
|
||||||
UniCharCount count;
|
UniCharCount count;
|
||||||
UniChar chars[2];
|
UniChar chars[2];
|
||||||
|
//LOG((CLOG_DEBUG "modifiers: %08x", modifiers & 0xffu));
|
||||||
OSStatus status = UCKeyTranslate(layout,
|
OSStatus status = UCKeyTranslate(layout,
|
||||||
vkCode & 0xffu, action,
|
vkCode & 0xffu, action,
|
||||||
(modifiers >> 8) & 0xffu,
|
(modifiers >> 8) & 0xffu,
|
||||||
|
@ -353,7 +367,8 @@ COSXKeyState::fakeKey(const Keystroke& keystroke)
|
||||||
|
|
||||||
switch (keystroke.m_type) {
|
switch (keystroke.m_type) {
|
||||||
case Keystroke::kButton:
|
case Keystroke::kButton:
|
||||||
LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
|
{
|
||||||
|
LOG((CLOG_DEBUG1 " %03x (%08x) %s", keystroke.m_data.m_button.m_button, keystroke.m_data.m_button.m_client, keystroke.m_data.m_button.m_press ? "down" : "up"));
|
||||||
|
|
||||||
// let system figure out character for us
|
// let system figure out character for us
|
||||||
ref = CGEventCreateKeyboardEvent(0, mapKeyButtonToVirtualKey(
|
ref = CGEventCreateKeyboardEvent(0, mapKeyButtonToVirtualKey(
|
||||||
|
@ -363,11 +378,12 @@ COSXKeyState::fakeKey(const Keystroke& keystroke)
|
||||||
LOG((CLOG_CRIT "unable to create keyboard event for keystroke"));
|
LOG((CLOG_CRIT "unable to create keyboard event for keystroke"));
|
||||||
}
|
}
|
||||||
|
|
||||||
CGEventPost(kCGHIDEventTap, ref);
|
CGEventPost(kCGHIDEventTap, ref);
|
||||||
|
|
||||||
// add a delay if client data isn't zero
|
// add a delay if client data isn't zero
|
||||||
if (keystroke.m_data.m_button.m_client) {
|
if (keystroke.m_data.m_button.m_client) {
|
||||||
ARCH->sleep(0.01);
|
ARCH->sleep(0.01);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -54,6 +54,12 @@ public:
|
||||||
*/
|
*/
|
||||||
KeyModifierMask mapModifiersFromOSX(UInt32 mask) const;
|
KeyModifierMask mapModifiersFromOSX(UInt32 mask) const;
|
||||||
|
|
||||||
|
//! Convert CG flags-style modifier mask to old-style Carbon
|
||||||
|
/*!
|
||||||
|
Still required in a few places for translation calls.
|
||||||
|
*/
|
||||||
|
KeyModifierMask mapModifiersToCarbon(UInt32 mask) const;
|
||||||
|
|
||||||
//! Map key event to keys
|
//! Map key event to keys
|
||||||
/*!
|
/*!
|
||||||
Converts a key event into a sequence of KeyIDs and the shadow modifier
|
Converts a key event into a sequence of KeyIDs and the shadow modifier
|
||||||
|
@ -63,7 +69,7 @@ public:
|
||||||
KeyID.
|
KeyID.
|
||||||
*/
|
*/
|
||||||
KeyButton mapKeyFromEvent(CKeyIDs& ids,
|
KeyButton mapKeyFromEvent(CKeyIDs& ids,
|
||||||
KeyModifierMask* maskOut, EventRef event) const;
|
KeyModifierMask* maskOut, CGEventRef event) const;
|
||||||
|
|
||||||
//! Map key and mask to native values
|
//! Map key and mask to native values
|
||||||
/*!
|
/*!
|
||||||
|
|
|
@ -87,38 +87,9 @@ COSXScreen::COSXScreen(bool isPrimary) :
|
||||||
m_keyState = new COSXKeyState();
|
m_keyState = new COSXKeyState();
|
||||||
|
|
||||||
if (m_isPrimary) {
|
if (m_isPrimary) {
|
||||||
// 1x1 window (to minimze the back buffer allocated for this
|
if (!AXAPIEnabled()) {
|
||||||
// window.
|
LOG((CLOG_ERR "Synergy server requires accessibility API enabled. Please check the option for \"Enable access for assistive devices\" in the Universal Access System Preferences panel. Unintentional key-replication will occur until this is fixed."));
|
||||||
Rect bounds = { 100, 100, 101, 101 };
|
}
|
||||||
|
|
||||||
// m_hiddenWindow is a window meant to let us get mouse moves
|
|
||||||
// when the focus is on another computer. If you get your event
|
|
||||||
// from the application event target you'll get every mouse
|
|
||||||
// moves. On the other hand the Window event target will only
|
|
||||||
// get events when the mouse moves over the window.
|
|
||||||
|
|
||||||
// The ignoreClicks attributes makes it impossible for the
|
|
||||||
// user to click on our invisible window.
|
|
||||||
CreateNewWindow(kUtilityWindowClass,
|
|
||||||
kWindowNoShadowAttribute |
|
|
||||||
kWindowIgnoreClicksAttribute |
|
|
||||||
kWindowNoActivatesAttribute,
|
|
||||||
&bounds, &m_hiddenWindow);
|
|
||||||
|
|
||||||
// Make it invisible
|
|
||||||
SetWindowAlpha(m_hiddenWindow, 0);
|
|
||||||
ShowWindow(m_hiddenWindow);
|
|
||||||
|
|
||||||
// m_userInputWindow is a window meant to let us get mouse moves
|
|
||||||
// when the focus is on this computer.
|
|
||||||
Rect inputBounds = { 100, 100, 200, 200 };
|
|
||||||
CreateNewWindow(kUtilityWindowClass,
|
|
||||||
kWindowNoShadowAttribute |
|
|
||||||
kWindowOpaqueForEventsAttribute |
|
|
||||||
kWindowStandardHandlerAttribute,
|
|
||||||
&inputBounds, &m_userInputWindow);
|
|
||||||
|
|
||||||
SetWindowAlpha(m_userInputWindow, 0);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// install display manager notification handler
|
// install display manager notification handler
|
||||||
|
@ -155,15 +126,6 @@ COSXScreen::COSXScreen(bool isPrimary) :
|
||||||
}
|
}
|
||||||
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
|
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
|
||||||
|
|
||||||
if (m_hiddenWindow) {
|
|
||||||
CFRelease(m_hiddenWindow);
|
|
||||||
m_hiddenWindow = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_userInputWindow) {
|
|
||||||
CFRelease(m_userInputWindow);
|
|
||||||
m_userInputWindow = NULL;
|
|
||||||
}
|
|
||||||
delete m_keyState;
|
delete m_keyState;
|
||||||
delete m_screensaver;
|
delete m_screensaver;
|
||||||
throw;
|
throw;
|
||||||
|
@ -209,15 +171,6 @@ COSXScreen::~COSXScreen()
|
||||||
RemoveEventHandler(m_switchEventHandlerRef);
|
RemoveEventHandler(m_switchEventHandlerRef);
|
||||||
|
|
||||||
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
|
CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
|
||||||
if (m_hiddenWindow) {
|
|
||||||
CFRelease(m_hiddenWindow);
|
|
||||||
m_hiddenWindow = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_userInputWindow) {
|
|
||||||
CFRelease(m_userInputWindow);
|
|
||||||
m_userInputWindow = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete m_keyState;
|
delete m_keyState;
|
||||||
delete m_screensaver;
|
delete m_screensaver;
|
||||||
|
@ -544,6 +497,20 @@ COSXScreen::enable()
|
||||||
|
|
||||||
if (m_isPrimary) {
|
if (m_isPrimary) {
|
||||||
// FIXME -- start watching jump zones
|
// FIXME -- start watching jump zones
|
||||||
|
|
||||||
|
// kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally)
|
||||||
|
m_eventTapPort=CGEventTapCreate(kCGHIDEventTap, kCGHIDEventTap, 0,
|
||||||
|
kCGEventMaskForAllEvents,
|
||||||
|
handleCGInputEvent,
|
||||||
|
this);
|
||||||
|
if(!m_eventTapPort) {
|
||||||
|
LOG((CLOG_ERR "Failed to create quartz event tap."));
|
||||||
|
}
|
||||||
|
m_eventTapRLSR=CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0);
|
||||||
|
if(!m_eventTapRLSR) {
|
||||||
|
LOG((CLOG_ERR "Failed to create a CFRunLoopSourceRef for the quartz event tap."));
|
||||||
|
}
|
||||||
|
CFRunLoopAddSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// FIXME -- prevent system from entering power save mode
|
// FIXME -- prevent system from entering power save mode
|
||||||
|
@ -566,6 +533,15 @@ COSXScreen::disable()
|
||||||
{
|
{
|
||||||
if (m_isPrimary) {
|
if (m_isPrimary) {
|
||||||
// FIXME -- stop watching jump zones, stop capturing input
|
// FIXME -- stop watching jump zones, stop capturing input
|
||||||
|
|
||||||
|
if(m_eventTapRLSR) {
|
||||||
|
CFRelease(m_eventTapRLSR);
|
||||||
|
m_eventTapRLSR=NULL;
|
||||||
|
}
|
||||||
|
if(m_eventTapPort) {
|
||||||
|
CFRelease(m_eventTapPort);
|
||||||
|
m_eventTapPort=NULL;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// show cursor
|
// show cursor
|
||||||
|
@ -595,12 +571,6 @@ void
|
||||||
COSXScreen::enter()
|
COSXScreen::enter()
|
||||||
{
|
{
|
||||||
if (m_isPrimary) {
|
if (m_isPrimary) {
|
||||||
// stop capturing input, watch jump zones
|
|
||||||
HideWindow( m_userInputWindow );
|
|
||||||
ShowWindow( m_hiddenWindow );
|
|
||||||
|
|
||||||
SetMouseCoalescingEnabled(true, NULL);
|
|
||||||
|
|
||||||
CGSetLocalEventsSuppressionInterval(0.0);
|
CGSetLocalEventsSuppressionInterval(0.0);
|
||||||
|
|
||||||
// enable global hotkeys
|
// enable global hotkeys
|
||||||
|
@ -640,17 +610,9 @@ COSXScreen::leave()
|
||||||
// warp to center
|
// warp to center
|
||||||
warpCursor(m_xCenter, m_yCenter);
|
warpCursor(m_xCenter, m_yCenter);
|
||||||
|
|
||||||
// capture events
|
// This used to be necessary to get smooth mouse motion on other screens,
|
||||||
HideWindow(m_hiddenWindow);
|
// but now is just to avoid a hesitating cursor when transitioning to
|
||||||
ShowWindow(m_userInputWindow);
|
// the primary (this) screen.
|
||||||
RepositionWindow(m_userInputWindow,
|
|
||||||
m_userInputWindow, kWindowCenterOnMainScreen);
|
|
||||||
SetUserFocusWindow(m_userInputWindow);
|
|
||||||
|
|
||||||
// The OS will coalesce some events if they are similar enough in a
|
|
||||||
// short period of time this is bad for us since we need every event
|
|
||||||
// to send it over to other machines. So disable it.
|
|
||||||
SetMouseCoalescingEnabled(false, NULL);
|
|
||||||
CGSetLocalEventsSuppressionInterval(0.0001);
|
CGSetLocalEventsSuppressionInterval(0.0001);
|
||||||
|
|
||||||
// disable global hotkeys
|
// disable global hotkeys
|
||||||
|
@ -796,79 +758,6 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*)
|
||||||
switch (eventClass) {
|
switch (eventClass) {
|
||||||
case kEventClassMouse:
|
case kEventClassMouse:
|
||||||
switch (GetEventKind(*carbonEvent)) {
|
switch (GetEventKind(*carbonEvent)) {
|
||||||
case kEventMouseDown:
|
|
||||||
{
|
|
||||||
UInt16 myButton;
|
|
||||||
GetEventParameter(*carbonEvent,
|
|
||||||
kEventParamMouseButton,
|
|
||||||
typeMouseButton,
|
|
||||||
NULL,
|
|
||||||
sizeof(myButton),
|
|
||||||
NULL,
|
|
||||||
&myButton);
|
|
||||||
onMouseButton(true, myButton);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kEventMouseUp:
|
|
||||||
{
|
|
||||||
UInt16 myButton;
|
|
||||||
GetEventParameter(*carbonEvent,
|
|
||||||
kEventParamMouseButton,
|
|
||||||
typeMouseButton,
|
|
||||||
NULL,
|
|
||||||
sizeof(myButton),
|
|
||||||
NULL,
|
|
||||||
&myButton);
|
|
||||||
onMouseButton(false, myButton);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kEventMouseDragged:
|
|
||||||
case kEventMouseMoved:
|
|
||||||
{
|
|
||||||
HIPoint point;
|
|
||||||
GetEventParameter(*carbonEvent,
|
|
||||||
kEventParamMouseLocation,
|
|
||||||
typeHIPoint,
|
|
||||||
NULL,
|
|
||||||
sizeof(point),
|
|
||||||
NULL,
|
|
||||||
&point);
|
|
||||||
onMouseMove((SInt32)point.x, (SInt32)point.y);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kEventMouseWheelMoved:
|
|
||||||
{
|
|
||||||
EventMouseWheelAxis axis;
|
|
||||||
SInt32 delta;
|
|
||||||
GetEventParameter(*carbonEvent,
|
|
||||||
kEventParamMouseWheelAxis,
|
|
||||||
typeMouseWheelAxis,
|
|
||||||
NULL,
|
|
||||||
sizeof(axis),
|
|
||||||
NULL,
|
|
||||||
&axis);
|
|
||||||
if (axis == kEventMouseWheelAxisX ||
|
|
||||||
axis == kEventMouseWheelAxisY) {
|
|
||||||
GetEventParameter(*carbonEvent,
|
|
||||||
kEventParamMouseWheelDelta,
|
|
||||||
typeLongInteger,
|
|
||||||
NULL,
|
|
||||||
sizeof(delta),
|
|
||||||
NULL,
|
|
||||||
&delta);
|
|
||||||
if (axis == kEventMouseWheelAxisX) {
|
|
||||||
onMouseWheel(-mapScrollWheelToSynergy((SInt32)delta), 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
onMouseWheel(0, mapScrollWheelToSynergy((SInt32)delta));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
case kSynergyEventMouseScroll:
|
case kSynergyEventMouseScroll:
|
||||||
{
|
{
|
||||||
OSStatus r;
|
OSStatus r;
|
||||||
|
@ -906,21 +795,14 @@ COSXScreen::handleSystemEvent(const CEvent& event, void*)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case kEventClassKeyboard:
|
case kEventClassKeyboard:
|
||||||
switch (GetEventKind(*carbonEvent)) {
|
switch (GetEventKind(*carbonEvent)) {
|
||||||
case kEventRawKeyUp:
|
case kEventHotKeyPressed:
|
||||||
case kEventRawKeyDown:
|
case kEventHotKeyReleased:
|
||||||
case kEventRawKeyRepeat:
|
onHotKey(*carbonEvent);
|
||||||
case kEventRawKeyModifiersChanged:
|
break;
|
||||||
onKey(*carbonEvent);
|
}
|
||||||
break;
|
|
||||||
|
|
||||||
case kEventHotKeyPressed:
|
|
||||||
case kEventHotKeyReleased:
|
|
||||||
onHotKey(*carbonEvent);
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
|
|
||||||
case kEventClassWindow:
|
case kEventClassWindow:
|
||||||
SendEventToWindow(*carbonEvent, m_userInputWindow);
|
SendEventToWindow(*carbonEvent, m_userInputWindow);
|
||||||
|
@ -1071,22 +953,17 @@ COSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDispla
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
COSXScreen::onKey(EventRef event)
|
COSXScreen::onKey(CGEventRef event)
|
||||||
{
|
{
|
||||||
UInt32 eventKind = GetEventKind(event);
|
CGEventType eventKind = CGEventGetType(event);
|
||||||
|
|
||||||
// get the key and active modifiers
|
// get the key and active modifiers
|
||||||
UInt32 virtualKey, macMask;
|
UInt32 virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
|
||||||
GetEventParameter(event, kEventParamKeyCode, typeUInt32,
|
CGEventFlags macMask = CGEventGetFlags(event);
|
||||||
NULL, sizeof(virtualKey), NULL, &virtualKey);
|
|
||||||
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
|
|
||||||
NULL, sizeof(macMask), NULL, &macMask);
|
|
||||||
LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));
|
LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));
|
||||||
|
|
||||||
// sadly, OS X doesn't report the virtualKey for modifier keys.
|
// Special handling to track state of modifiers
|
||||||
// virtualKey will be zero for modifier keys. since that's not good
|
if (eventKind == kCGEventFlagsChanged) {
|
||||||
// enough we'll have to figure out what the key was.
|
|
||||||
if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) {
|
|
||||||
// get old and new modifier state
|
// get old and new modifier state
|
||||||
KeyModifierMask oldMask = getActiveModifiers();
|
KeyModifierMask oldMask = getActiveModifiers();
|
||||||
KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
|
KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
|
||||||
|
@ -1126,17 +1003,19 @@ COSXScreen::onKey(EventRef event)
|
||||||
// so we check for a key/modifier match in our hot key map.
|
// so we check for a key/modifier match in our hot key map.
|
||||||
if (!m_isOnScreen) {
|
if (!m_isOnScreen) {
|
||||||
HotKeyToIDMap::const_iterator i =
|
HotKeyToIDMap::const_iterator i =
|
||||||
m_hotKeyToIDMap.find(CHotKeyItem(virtualKey, macMask & 0xff00u));
|
m_hotKeyToIDMap.find(CHotKeyItem(virtualKey,
|
||||||
|
m_keyState->mapModifiersToCarbon(macMask)
|
||||||
|
& 0xff00u));
|
||||||
if (i != m_hotKeyToIDMap.end()) {
|
if (i != m_hotKeyToIDMap.end()) {
|
||||||
UInt32 id = i->second;
|
UInt32 id = i->second;
|
||||||
|
|
||||||
// determine event type
|
// determine event type
|
||||||
CEvent::Type type;
|
CEvent::Type type;
|
||||||
UInt32 eventKind = GetEventKind(event);
|
//UInt32 eventKind = GetEventKind(event);
|
||||||
if (eventKind == kEventRawKeyDown) {
|
if (eventKind == kCGEventKeyDown) {
|
||||||
type = getHotKeyDownEvent();
|
type = getHotKeyDownEvent();
|
||||||
}
|
}
|
||||||
else if (eventKind == kEventRawKeyUp) {
|
else if (eventKind == kCGEventKeyUp) {
|
||||||
type = getHotKeyUpEvent();
|
type = getHotKeyUpEvent();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -1151,9 +1030,9 @@ COSXScreen::onKey(EventRef event)
|
||||||
}
|
}
|
||||||
|
|
||||||
// decode event type
|
// decode event type
|
||||||
bool down = (eventKind == kEventRawKeyDown);
|
bool down = (eventKind == kCGEventKeyDown);
|
||||||
bool up = (eventKind == kEventRawKeyUp);
|
bool up = (eventKind == kCGEventKeyUp);
|
||||||
bool isRepeat = (eventKind == kEventRawKeyRepeat);
|
bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1);
|
||||||
|
|
||||||
// map event to keys
|
// map event to keys
|
||||||
KeyModifierMask mask;
|
KeyModifierMask mask;
|
||||||
|
@ -1681,3 +1560,73 @@ COSXScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const
|
||||||
return (m_keycode < x.m_keycode ||
|
return (m_keycode < x.m_keycode ||
|
||||||
(m_keycode == x.m_keycode && m_mask < x.m_mask));
|
(m_keycode == x.m_keycode && m_mask < x.m_mask));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Quartz event tap support
|
||||||
|
CGEventRef
|
||||||
|
COSXScreen::handleCGInputEvent(CGEventTapProxy proxy,
|
||||||
|
CGEventType type,
|
||||||
|
CGEventRef event,
|
||||||
|
void* refcon)
|
||||||
|
{
|
||||||
|
COSXScreen* screen = (COSXScreen*)refcon;
|
||||||
|
CGPoint pos;
|
||||||
|
|
||||||
|
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 kCGEventMouseMoved:
|
||||||
|
case kCGEventLeftMouseDragged:
|
||||||
|
case kCGEventRightMouseDragged:
|
||||||
|
case kCGEventOtherMouseDragged:
|
||||||
|
pos = CGEventGetLocation(event);
|
||||||
|
screen->onMouseMove(pos.x, pos.y);
|
||||||
|
|
||||||
|
// 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->mapScrollWheelToSynergy(
|
||||||
|
CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis2)),
|
||||||
|
screen->mapScrollWheelToSynergy(
|
||||||
|
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((CLOG_NOTE "Quartz Event tap was disabled by timeout. Re-enabling."));
|
||||||
|
break;
|
||||||
|
case kCGEventTapDisabledByUserInput:
|
||||||
|
LOG((CLOG_ERR "Quartz Event tap was disabled by user input!"));
|
||||||
|
break;
|
||||||
|
case NX_NULLEVENT:
|
||||||
|
break;
|
||||||
|
case NX_SYSDEFINED:
|
||||||
|
// Unknown, forward it
|
||||||
|
return event;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
LOG((CLOG_NOTE "Unknown Quartz Event type: 0x%02x", type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(screen->m_isOnScreen) {
|
||||||
|
return event;
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
|
@ -94,14 +94,14 @@ private:
|
||||||
void sendClipboardEvent(CEvent::Type type, ClipboardID id) const;
|
void sendClipboardEvent(CEvent::Type type, ClipboardID id) const;
|
||||||
|
|
||||||
// message handlers
|
// message handlers
|
||||||
bool onMouseMove(SInt32 x, SInt32 y);
|
bool onMouseMove(SInt32 mx, SInt32 my);
|
||||||
// mouse button handler. pressed is true if this is a mousedown
|
// mouse button handler. pressed is true if this is a mousedown
|
||||||
// event, false if it is a mouseup event. macButton is the index
|
// event, false if it is a mouseup event. macButton is the index
|
||||||
// of the button pressed using the mac button mapping.
|
// of the button pressed using the mac button mapping.
|
||||||
bool onMouseButton(bool pressed, UInt16 macButton);
|
bool onMouseButton(bool pressed, UInt16 macButton);
|
||||||
bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
|
bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const;
|
||||||
|
|
||||||
bool onKey(EventRef event);
|
bool onKey(CGEventRef event);
|
||||||
bool onHotKey(EventRef event) const;
|
bool onHotKey(EventRef event) const;
|
||||||
|
|
||||||
// map mac mouse button to synergy buttons
|
// map mac mouse button to synergy buttons
|
||||||
|
@ -153,6 +153,11 @@ private:
|
||||||
static void setGlobalHotKeysEnabled(bool enabled);
|
static void setGlobalHotKeysEnabled(bool enabled);
|
||||||
static bool getGlobalHotKeysEnabled();
|
static bool getGlobalHotKeysEnabled();
|
||||||
|
|
||||||
|
// Quartz event tap support
|
||||||
|
static CGEventRef handleCGInputEvent(CGEventTapProxy proxy,
|
||||||
|
CGEventType type,
|
||||||
|
CGEventRef event,
|
||||||
|
void* refcon);
|
||||||
private:
|
private:
|
||||||
struct CHotKeyItem {
|
struct CHotKeyItem {
|
||||||
public:
|
public:
|
||||||
|
@ -241,6 +246,10 @@ private:
|
||||||
|
|
||||||
// events
|
// events
|
||||||
static CEvent::Type s_confirmSleepEvent;
|
static CEvent::Type s_confirmSleepEvent;
|
||||||
|
|
||||||
|
// Quartz input event support
|
||||||
|
CFMachPortRef m_eventTapPort;
|
||||||
|
CFRunLoopSourceRef m_eventTapRLSR;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue