From f7a5da414692e89225bd4142a4a7afffcc2e7abe Mon Sep 17 00:00:00 2001 From: Edward Carrel Date: Tue, 1 Jun 2010 10:49:22 +0000 Subject: [PATCH] Fix for Issue 378, updating deprecated calls for creating mouse events under MacOS 10.6. Thanks to snes350 for the initial patch to help fix this problem. --- cmake/CMakeLists_config.txt | 2 +- lib/platform/COSXScreen.cpp | 205 ++++++++++++++++++++++++++---------- lib/platform/COSXScreen.h | 43 +++++++- lib/synergy/MouseTypes.h | 2 + 4 files changed, 192 insertions(+), 60 deletions(-) diff --git a/cmake/CMakeLists_config.txt b/cmake/CMakeLists_config.txt index ac303dc9..441604ce 100644 --- a/cmake/CMakeLists_config.txt +++ b/cmake/CMakeLists_config.txt @@ -176,7 +176,7 @@ IF(UNIX) ADD_DEFINITIONS(-DSYSAPI_UNIX=1 -DHAVE_CONFIG_H) IF(APPLE) - ADD_DEFINITIONS(-DWINAPI_CARBON=1 -D_THREAD_SAFE) + ADD_DEFINITIONS(-DWINAPI_CARBON=1 -D_THREAD_SAFE -DMACOSX_DEPLOYMENT_TARGET=10.4) ELSE(APPLE) ADD_DEFINITIONS(-DWINAPI_XWINDOWS=1) ENDIF(APPLE) diff --git a/lib/platform/COSXScreen.cpp b/lib/platform/COSXScreen.cpp index b33c8c81..cf575f18 100644 --- a/lib/platform/COSXScreen.cpp +++ b/lib/platform/COSXScreen.cpp @@ -37,7 +37,7 @@ #if !defined(MAC_OS_X_VERSION_10_3) || \ (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3) enum { - kEventClassSystem = 'macs', + kEventClassSystem = 'macs', kEventSystemUserSessionActivated = 10, kEventSystemUserSessionDeactivated = 11 }; @@ -54,11 +54,14 @@ enum { // COSXScreen // -bool COSXScreen::s_testedForGHOM = false; -bool COSXScreen::s_hasGHOM = false; + + +bool COSXScreen::s_testedForGHOM = false; +bool COSXScreen::s_hasGHOM = false; CEvent::Type COSXScreen::s_confirmSleepEvent = CEvent::kUnknown; COSXScreen::COSXScreen(bool isPrimary) : + MouseButtonEventMap(NumButtonIDs), m_isPrimary(isPrimary), m_isOnScreen(m_isPrimary), m_cursorPosValid(false), @@ -107,6 +110,8 @@ COSXScreen::COSXScreen(bool isPrimary) : this, &m_switchEventHandlerRef); DisposeEventHandlerUPP(switchEventHandler); + constructMouseButtonEventMap(); + // watch for requests to sleep EVENTQUEUE->adoptHandler(COSXScreen::getConfirmSleepEvent(), getEventTarget(), @@ -360,6 +365,26 @@ COSXScreen::unregisterHotKey(UInt32 id) } } +void +COSXScreen::constructMouseButtonEventMap() +{ + const CGEventType source[NumButtonIDs][3] = { + kCGEventLeftMouseUp,kCGEventLeftMouseDragged,kCGEventLeftMouseDown, + kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown, + kCGEventRightMouseUp,kCGEventRightMouseDragged,kCGEventRightMouseDown, + kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown, + kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown}; + + for (UInt16 button = 0; button < NumButtonIDs; button++) { + MouseButtonEventMapType new_map; + for (UInt16 state = (UInt32) kMouseButtonUp; state < kMouseButtonStateMax; state++) { + CGEventType curEvent = source[button][state]; + new_map[state] = curEvent; + } + MouseButtonEventMap[button] = new_map; + } +} + void COSXScreen::postMouseEvent(CGPoint& pos) const { @@ -392,41 +417,30 @@ COSXScreen::postMouseEvent(CGPoint& pos) const } } } - - // synthesize event. CGPostMouseEvent is a particularly good - // example of a bad API. we have to shadow the mouse state to - // use this API and if we want to support more buttons we have - // to recompile. - // - // the order of buttons on the mac is: - // 1 - Left - // 2 - Right - // 3 - Middle - // Whatever the USB device defined. - // - // It is a bit weird that the behaviour of buttons over 3 are dependent - // on currently plugged in USB devices. - CGPostMouseEvent(pos, true, sizeof(m_buttons) / sizeof(m_buttons[0]), - m_buttons[0], - m_buttons[2], - m_buttons[1], - m_buttons[3], - m_buttons[4]); -} + + CGEventType type = kCGEventMouseMoved; + SInt8 button = m_buttonState.getFirstButtonDown(); + if (button != -1) { + MouseButtonEventMapType thisButtonType = MouseButtonEventMap[button]; + type = thisButtonType[kMouseButtonDragged]; + } + + CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, button); + CGEventPost(kCGHIDEventTap, event); + + CFRelease(event); +} void COSXScreen::fakeMouseButton(ButtonID id, bool press) const { - // get button index + // Buttons are indexed from one, but the button down array is indexed from zero UInt32 index = id - kButtonLeft; - if (index >= sizeof(m_buttons) / sizeof(m_buttons[0])) { + if (index >= NumButtonIDs) { return; } - - // update state - m_buttons[index] = press; - + CGPoint pos; if (!m_cursorPosValid) { SInt32 x, y; @@ -434,7 +448,36 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) const } pos.x = m_xCursor; pos.y = m_yCursor; - postMouseEvent(pos); + + // synthesize event. CGEventCreateMouseEvent creates a retained mouse + // event, which must also be posted and released. Note this is + // similar to the use of CGEventRef in postMouseEvent above. + // One of the arguments changes based on whether a button is being + // pressed or released, pressed corresponding to when "press" is true. + CGEventRef event; + + // the switch statement handles which button was pressed. the left + // and right mouse buttons must be handled separately from any + // other buttons + + CGEventType type; + MouseButtonState state; + if (press) { + state = kMouseButtonDown; + } else { + state = kMouseButtonUp; + } + + MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index]; + type = thisButtonMap[state]; + + event = CGEventCreateMouseEvent(NULL, type, pos, index); + + m_buttonState.set(index, state); + + CGEventPost(kCGHIDEventTap, event); + + CFRelease(event); } void @@ -480,8 +523,17 @@ void COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const { if (xDelta != 0 || yDelta != 0) { - CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta), - -mapScrollWheelFromSynergy(xDelta)); + // create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine + // is the right choice here over kCGScrollEventUnitPixel + CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(NULL, + kCGScrollEventUnitLine, + 2, + mapScrollWheelFromSynergy(yDelta), + -mapScrollWheelFromSynergy(xDelta)); + + CGEventPost(kCGHIDEventTap, scrollEvent); + + CFRelease(scrollEvent); } } @@ -583,9 +635,7 @@ COSXScreen::enter() } // reset buttons - for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { - m_buttons[i] = false; - } + m_buttonState.reset(); // avoid suppression of local hardware events // stkamp@users.sourceforge.net @@ -642,22 +692,22 @@ COSXScreen::leave() bool COSXScreen::setClipboard(ClipboardID, const IClipboard* src) { - if(src != NULL) { - LOG((CLOG_DEBUG "setting clipboard")); - CClipboard::copy(&m_pasteboard, src); - } - return true; + if(src != NULL) { + LOG((CLOG_DEBUG "setting clipboard")); + CClipboard::copy(&m_pasteboard, src); + } + return true; } void COSXScreen::checkClipboards() { - LOG((CLOG_DEBUG1 "checking clipboard")); - if (m_pasteboard.synchronize()) { - LOG((CLOG_DEBUG "clipboard changed")); - sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); - sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); - } + LOG((CLOG_DEBUG1 "checking clipboard")); + if (m_pasteboard.synchronize()) { + LOG((CLOG_DEBUG "clipboard changed")); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard); + sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection); + } } void @@ -926,7 +976,7 @@ COSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDispla LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask)); if (flags & mask) { /* Something actually did change */ - + LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions")); screen->updateScreenShape(displayID, flags); } @@ -1158,7 +1208,7 @@ COSXScreen::enableDragTimer(bool enable) EVENTQUEUE->adoptHandler(CEvent::kTimer, m_dragTimer, new TMethodEventJob(this, &COSXScreen::handleDrag)); - TrackMouseLocationWithOptions(NULL, 0, 0, &m_dragLastPoint, &modifiers, &res); + TrackMouseLocationWithOptions(NULL, 0, 0, &m_dragLastPoint, &modifiers, &res); } else if (!enable && m_dragTimer != NULL) { EVENTQUEUE->removeHandler(CEvent::kTimer, m_dragTimer); @@ -1186,9 +1236,8 @@ void COSXScreen::updateButtons() { UInt32 buttons = GetCurrentButtonState(); - for (size_t i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) { - m_buttons[i] = ((buttons & (1u << i)) != 0); - } + + m_buttonState.overwrite(buttons); } IKeyState* @@ -1200,7 +1249,7 @@ COSXScreen::getKeyState() const void COSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags flags) { - + // get info for each display CGDisplayCount displayCount = 0; @@ -1243,9 +1292,9 @@ COSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSumm m_yCenter = (rect.origin.y + rect.size.height) / 2; delete[] displays; - if (m_isPrimary && !m_isOnScreen) { - sendEvent(getShapeChangedEvent()); - } + if (m_isPrimary && !m_isOnScreen) { + sendEvent(getShapeChangedEvent()); + } LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays")); } @@ -1610,3 +1659,47 @@ COSXScreen::handleCGInputEvent(CGEventTapProxy proxy, return NULL; } } + +void +COSXScreen::CMouseButtonState::set(UInt32 button, MouseButtonState state) +{ + bool newState = (state == kMouseButtonDown); + m_buttons.set(button, newState); +} + +bool +COSXScreen::CMouseButtonState::any() +{ + return m_buttons.any(); +} + +void +COSXScreen::CMouseButtonState::reset() +{ + m_buttons.reset(); +} + +void +COSXScreen::CMouseButtonState::overwrite(UInt32 buttons) +{ + m_buttons = std::bitset(buttons); +} + +bool +COSXScreen::CMouseButtonState::test(UInt32 button) const +{ + return m_buttons.test(button); +} + +SInt8 +COSXScreen::CMouseButtonState::getFirstButtonDown() const +{ + if (m_buttons.any()) { + for (unsigned short button = 0; button < m_buttons.size(); button++) { + if (m_buttons.test(button)) { + return button; + } + } + } + return -1; +} diff --git a/lib/platform/COSXScreen.h b/lib/platform/COSXScreen.h index 00a04472..14e1076b 100644 --- a/lib/platform/COSXScreen.h +++ b/lib/platform/COSXScreen.h @@ -15,11 +15,14 @@ #ifndef COSXSCREEN_H #define COSXSCREEN_H +#include + +#include "stdmap.h" +#include "stdvector.h" + #include #include "COSXClipboard.h" #include "CPlatformScreen.h" -#include "stdmap.h" -#include "stdvector.h" #include #include @@ -102,6 +105,8 @@ private: bool onMouseButton(bool pressed, UInt16 macButton); bool onMouseWheel(SInt32 xDelta, SInt32 yDelta) const; + void constructMouseButtonEventMap(); + bool onKey(CGEventRef event); bool onHotKey(EventRef event) const; @@ -174,6 +179,28 @@ private: UInt32 m_keycode; UInt32 m_mask; }; + + enum MouseButtonState { + kMouseButtonUp = 0, + kMouseButtonDragged, + kMouseButtonDown, + kMouseButtonStateMax + }; + + + class CMouseButtonState { + public: + void set(UInt32 button, MouseButtonState state); + bool any(); + void reset(); + void overwrite(UInt32 buttons); + + bool test(UInt32 button) const; + SInt8 getFirstButtonDown() const; + private: + std::bitset m_buttons; + }; + typedef std::map HotKeyMap; typedef std::vector HotKeyIDList; typedef std::map ModifierHotKeyMap; @@ -196,7 +223,17 @@ private: // mouse state mutable SInt32 m_xCursor, m_yCursor; mutable bool m_cursorPosValid; - mutable boolean_t m_buttons[5]; + + /* FIXME: this data structure is explicitly marked mutable due + to a need to track the state of buttons since the remote + side only lets us know of change events, and because the + fakeMouseButton button method is marked 'const'. This is + Evil, and this should be moved to a place where it need not + be mutable as soon as possible. */ + mutable CMouseButtonState m_buttonState; + typedef std::map MouseButtonEventMapType; + std::vector MouseButtonEventMap; + bool m_cursorHidden; SInt32 m_dragNumButtonsDown; Point m_dragLastPoint; diff --git a/lib/synergy/MouseTypes.h b/lib/synergy/MouseTypes.h index e4988553..e83cfb6f 100644 --- a/lib/synergy/MouseTypes.h +++ b/lib/synergy/MouseTypes.h @@ -32,4 +32,6 @@ static const ButtonID kButtonRight = 3; static const ButtonID kButtonExtra0 = 4; //@} +static const UInt8 NumButtonIDs = 5; + #endif