00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "COSXScreen.h"
00020 #include "COSXClipboard.h"
00021 #include "COSXEventQueueBuffer.h"
00022 #include "COSXKeyState.h"
00023 #include "COSXScreenSaver.h"
00024 #include "CClipboard.h"
00025 #include "CKeyMap.h"
00026 #include "CCondVar.h"
00027 #include "CLock.h"
00028 #include "CMutex.h"
00029 #include "CThread.h"
00030 #include "CLog.h"
00031 #include "IEventQueue.h"
00032 #include "TMethodEventJob.h"
00033 #include "TMethodJob.h"
00034 #include "XArch.h"
00035
00036 #include <math.h>
00037
00038 #include <mach-o/dyld.h>
00039 #include <AvailabilityMacros.h>
00040 #include <IOKit/hidsystem/event_status_driver.h>
00041
00042
00043
00044 #if !defined(MAC_OS_X_VERSION_10_3) || \
00045 (MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3)
00046 enum {
00047 kEventClassSystem = 'macs',
00048 kEventSystemUserSessionActivated = 10,
00049 kEventSystemUserSessionDeactivated = 11
00050 };
00051 #endif
00052
00053
00054 enum {
00055 kSynergyEventMouseScroll = 11,
00056 kSynergyMouseScrollAxisX = 'saxx',
00057 kSynergyMouseScrollAxisY = 'saxy'
00058 };
00059
00060
00061
00062
00063
00064
00065
00066 bool COSXScreen::s_testedForGHOM = false;
00067 bool COSXScreen::s_hasGHOM = false;
00068 CEvent::Type COSXScreen::s_confirmSleepEvent = CEvent::kUnknown;
00069
00070 COSXScreen::COSXScreen(bool isPrimary, bool autoShowHideCursor) :
00071 MouseButtonEventMap(NumButtonIDs),
00072 m_isPrimary(isPrimary),
00073 m_isOnScreen(m_isPrimary),
00074 m_cursorPosValid(false),
00075 m_cursorHidden(false),
00076 m_dragNumButtonsDown(0),
00077 m_dragTimer(NULL),
00078 m_keyState(NULL),
00079 m_sequenceNumber(0),
00080 m_screensaver(NULL),
00081 m_screensaverNotify(false),
00082 m_ownClipboard(false),
00083 m_clipboardTimer(NULL),
00084 m_hiddenWindow(NULL),
00085 m_userInputWindow(NULL),
00086 m_switchEventHandlerRef(0),
00087 m_pmMutex(new CMutex),
00088 m_pmWatchThread(NULL),
00089 m_pmThreadReady(new CCondVar<bool>(m_pmMutex, false)),
00090 m_activeModifierHotKey(0),
00091 m_activeModifierHotKeyMask(0),
00092 m_lastSingleClick(0),
00093 m_lastDoubleClick(0),
00094 m_lastSingleClickXCursor(0),
00095 m_lastSingleClickYCursor(0),
00096 m_autoShowHideCursor(autoShowHideCursor),
00097 m_eventTapRLSR(nullptr),
00098 m_eventTapPort(nullptr),
00099 m_pmRootPort(0)
00100 {
00101 try {
00102 m_displayID = CGMainDisplayID();
00103 updateScreenShape(m_displayID, 0);
00104 m_screensaver = new COSXScreenSaver(getEventTarget());
00105 m_keyState = new COSXKeyState();
00106
00107
00108 if (m_isPrimary && !AXAPIEnabled())
00109 throw XArch("system setting not enabled: \"Enable access for assistive devices\"");
00110
00111
00112 #if defined(MAC_OS_X_VERSION_10_5)
00113 CGDisplayRegisterReconfigurationCallback(displayReconfigurationCallback, this);
00114 #else
00115 m_displayManagerNotificationUPP =
00116 NewDMExtendedNotificationUPP(displayManagerCallback);
00117 OSStatus err = GetCurrentProcess(&m_PSN);
00118 err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP,
00119 this, 0, &m_PSN);
00120 #endif
00121
00122
00123 EventTypeSpec switchEventTypes[2];
00124 switchEventTypes[0].eventClass = kEventClassSystem;
00125 switchEventTypes[0].eventKind = kEventSystemUserSessionDeactivated;
00126 switchEventTypes[1].eventClass = kEventClassSystem;
00127 switchEventTypes[1].eventKind = kEventSystemUserSessionActivated;
00128 EventHandlerUPP switchEventHandler =
00129 NewEventHandlerUPP(userSwitchCallback);
00130 InstallApplicationEventHandler(switchEventHandler, 2, switchEventTypes,
00131 this, &m_switchEventHandlerRef);
00132 DisposeEventHandlerUPP(switchEventHandler);
00133
00134 constructMouseButtonEventMap();
00135
00136
00137 EVENTQUEUE->adoptHandler(COSXScreen::getConfirmSleepEvent(),
00138 getEventTarget(),
00139 new TMethodEventJob<COSXScreen>(this,
00140 &COSXScreen::handleConfirmSleep));
00141
00142
00143 *m_pmThreadReady = false;
00144 LOG((CLOG_DEBUG "starting watchSystemPowerThread"));
00145 m_pmWatchThread = new CThread(new TMethodJob<COSXScreen>
00146 (this, &COSXScreen::watchSystemPowerThread));
00147 }
00148 catch (...) {
00149 EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(),
00150 getEventTarget());
00151 if (m_switchEventHandlerRef != 0) {
00152 RemoveEventHandler(m_switchEventHandlerRef);
00153 }
00154 #if defined(MAC_OS_X_VERSION_10_5)
00155 CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
00156 #else
00157 if (m_displayManagerNotificationUPP != NULL) {
00158 DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP,
00159 NULL, &m_PSN, 0);
00160 }
00161
00162 if (m_hiddenWindow) {
00163 ReleaseWindow(m_hiddenWindow);
00164 m_hiddenWindow = NULL;
00165 }
00166
00167 if (m_userInputWindow) {
00168 ReleaseWindow(m_userInputWindow);
00169 m_userInputWindow = NULL;
00170 }
00171 #endif
00172
00173 delete m_keyState;
00174 delete m_screensaver;
00175 throw;
00176 }
00177
00178
00179 EVENTQUEUE->adoptHandler(CEvent::kSystem, IEventQueue::getSystemTarget(),
00180 new TMethodEventJob<COSXScreen>(this,
00181 &COSXScreen::handleSystemEvent));
00182
00183
00184 EVENTQUEUE->adoptBuffer(new COSXEventQueueBuffer);
00185 }
00186
00187 COSXScreen::~COSXScreen()
00188 {
00189 disable();
00190 EVENTQUEUE->adoptBuffer(NULL);
00191 EVENTQUEUE->removeHandler(CEvent::kSystem, IEventQueue::getSystemTarget());
00192
00193 if (m_pmWatchThread) {
00194
00195 {
00196 CLock lock(m_pmMutex);
00197 while (!(bool)*m_pmThreadReady) {
00198 m_pmThreadReady->wait();
00199 }
00200 }
00201
00202
00203 LOG((CLOG_DEBUG "stopping watchSystemPowerThread"));
00204 CFRunLoopStop(m_pmRunloop);
00205 m_pmWatchThread->wait();
00206 delete m_pmWatchThread;
00207 m_pmWatchThread = NULL;
00208 }
00209 delete m_pmThreadReady;
00210 delete m_pmMutex;
00211
00212 EVENTQUEUE->removeHandler(COSXScreen::getConfirmSleepEvent(),
00213 getEventTarget());
00214
00215 RemoveEventHandler(m_switchEventHandlerRef);
00216
00217 #if defined(MAC_OS_X_VERSION_10_5)
00218 CGDisplayRemoveReconfigurationCallback(displayReconfigurationCallback, this);
00219 #else
00220 DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP,
00221 NULL, &m_PSN, 0);
00222
00223 if (m_hiddenWindow) {
00224 ReleaseWindow(m_hiddenWindow);
00225 m_hiddenWindow = NULL;
00226 }
00227
00228 if (m_userInputWindow) {
00229 ReleaseWindow(m_userInputWindow);
00230 m_userInputWindow = NULL;
00231 }
00232 #endif
00233
00234 delete m_keyState;
00235 delete m_screensaver;
00236 }
00237
00238 void*
00239 COSXScreen::getEventTarget() const
00240 {
00241 return const_cast<COSXScreen*>(this);
00242 }
00243
00244 bool
00245 COSXScreen::getClipboard(ClipboardID, IClipboard* dst) const
00246 {
00247 CClipboard::copy(dst, &m_pasteboard);
00248 return true;
00249 }
00250
00251 void
00252 COSXScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const
00253 {
00254 x = m_x;
00255 y = m_y;
00256 w = m_w;
00257 h = m_h;
00258 }
00259
00260 void
00261 COSXScreen::getCursorPos(SInt32& x, SInt32& y) const
00262 {
00263 Point mouse;
00264 GetGlobalMouse(&mouse);
00265 x = mouse.h;
00266 y = mouse.v;
00267 m_cursorPosValid = true;
00268 m_xCursor = x;
00269 m_yCursor = y;
00270 }
00271
00272 void
00273 COSXScreen::reconfigure(UInt32)
00274 {
00275
00276 }
00277
00278 void
00279 COSXScreen::warpCursor(SInt32 x, SInt32 y)
00280 {
00281
00282 CGPoint pos;
00283 pos.x = x;
00284 pos.y = y;
00285 CGWarpMouseCursorPosition(pos);
00286
00287
00288 m_xCursor = x;
00289 m_yCursor = y;
00290 m_cursorPosValid = true;
00291 }
00292
00293 void
00294 COSXScreen::fakeInputBegin()
00295 {
00296
00297 }
00298
00299 void
00300 COSXScreen::fakeInputEnd()
00301 {
00302
00303 }
00304
00305 SInt32
00306 COSXScreen::getJumpZoneSize() const
00307 {
00308 return 1;
00309 }
00310
00311 bool
00312 COSXScreen::isAnyMouseButtonDown() const
00313 {
00314 return (GetCurrentButtonState() != 0);
00315 }
00316
00317 void
00318 COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const
00319 {
00320 x = m_xCenter;
00321 y = m_yCenter;
00322 }
00323
00324 UInt32
00325 COSXScreen::registerHotKey(KeyID key, KeyModifierMask mask)
00326 {
00327
00328 UInt32 macKey, macMask;
00329 if (!m_keyState->mapSynergyHotKeyToMac(key, mask, macKey, macMask)) {
00330 LOG((CLOG_DEBUG "could not map hotkey id=%04x mask=%04x", key, mask));
00331 return 0;
00332 }
00333
00334
00335 UInt32 id;
00336 if (!m_oldHotKeyIDs.empty()) {
00337 id = m_oldHotKeyIDs.back();
00338 m_oldHotKeyIDs.pop_back();
00339 }
00340 else {
00341 id = m_hotKeys.size() + 1;
00342 }
00343
00344
00345 EventHotKeyRef ref = NULL;
00346 bool okay;
00347 if (key == kKeyNone) {
00348 if (m_modifierHotKeys.count(mask) > 0) {
00349
00350 okay = false;
00351 }
00352 else {
00353 m_modifierHotKeys[mask] = id;
00354 okay = true;
00355 }
00356 }
00357 else {
00358 EventHotKeyID hkid = { 'SNRG', (UInt32)id };
00359 OSStatus status = RegisterEventHotKey(macKey, macMask, hkid,
00360 GetApplicationEventTarget(), 0,
00361 &ref);
00362 okay = (status == noErr);
00363 m_hotKeyToIDMap[CHotKeyItem(macKey, macMask)] = id;
00364 }
00365
00366 if (!okay) {
00367 m_oldHotKeyIDs.push_back(id);
00368 m_hotKeyToIDMap.erase(CHotKeyItem(macKey, macMask));
00369 LOG((CLOG_WARN "failed to register hotkey %s (id=%04x mask=%04x)", CKeyMap::formatKey(key, mask).c_str(), key, mask));
00370 return 0;
00371 }
00372
00373 m_hotKeys.insert(std::make_pair(id, CHotKeyItem(ref, macKey, macMask)));
00374
00375 LOG((CLOG_DEBUG "registered hotkey %s (id=%04x mask=%04x) as id=%d", CKeyMap::formatKey(key, mask).c_str(), key, mask, id));
00376 return id;
00377 }
00378
00379 void
00380 COSXScreen::unregisterHotKey(UInt32 id)
00381 {
00382
00383 HotKeyMap::iterator i = m_hotKeys.find(id);
00384 if (i == m_hotKeys.end()) {
00385 return;
00386 }
00387
00388
00389 bool okay;
00390 if (i->second.getRef() != NULL) {
00391 okay = (UnregisterEventHotKey(i->second.getRef()) == noErr);
00392 }
00393 else {
00394 okay = false;
00395
00396 for (ModifierHotKeyMap::iterator j = m_modifierHotKeys.begin();
00397 j != m_modifierHotKeys.end(); ++j) {
00398 if (j->second == id) {
00399 m_modifierHotKeys.erase(j);
00400 okay = true;
00401 break;
00402 }
00403 }
00404 }
00405 if (!okay) {
00406 LOG((CLOG_WARN "failed to unregister hotkey id=%d", id));
00407 }
00408 else {
00409 LOG((CLOG_DEBUG "unregistered hotkey id=%d", id));
00410 }
00411
00412
00413 m_hotKeyToIDMap.erase(i->second);
00414 m_hotKeys.erase(i);
00415 m_oldHotKeyIDs.push_back(id);
00416 if (m_activeModifierHotKey == id) {
00417 m_activeModifierHotKey = 0;
00418 m_activeModifierHotKeyMask = 0;
00419 }
00420 }
00421
00422 void
00423 COSXScreen::constructMouseButtonEventMap()
00424 {
00425 const CGEventType source[NumButtonIDs][3] = {
00426 kCGEventLeftMouseUp,kCGEventLeftMouseDragged,kCGEventLeftMouseDown,
00427 kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown,
00428 kCGEventRightMouseUp,kCGEventRightMouseDragged,kCGEventRightMouseDown,
00429 kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown,
00430 kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown};
00431
00432 for (UInt16 button = 0; button < NumButtonIDs; button++) {
00433 MouseButtonEventMapType new_map;
00434 for (UInt16 state = (UInt32) kMouseButtonUp; state < kMouseButtonStateMax; state++) {
00435 CGEventType curEvent = source[button][state];
00436 new_map[state] = curEvent;
00437 }
00438 MouseButtonEventMap[button] = new_map;
00439 }
00440 }
00441
00442 void
00443 COSXScreen::postMouseEvent(CGPoint& pos) const
00444 {
00445
00446
00447 CGDisplayCount displayCount = 0;
00448 CGGetDisplaysWithPoint(pos, 0, NULL, &displayCount);
00449 if (displayCount == 0) {
00450
00451
00452 displayCount = 0;
00453 CGDirectDisplayID displayID;
00454 CGGetDisplaysWithPoint(CGPointMake(m_xCursor, m_yCursor), 1,
00455 &displayID, &displayCount);
00456 if (displayCount != 0) {
00457 CGRect displayRect = CGDisplayBounds(displayID);
00458 if (pos.x < displayRect.origin.x) {
00459 pos.x = displayRect.origin.x;
00460 }
00461 else if (pos.x > displayRect.origin.x +
00462 displayRect.size.width - 1) {
00463 pos.x = displayRect.origin.x + displayRect.size.width - 1;
00464 }
00465 if (pos.y < displayRect.origin.y) {
00466 pos.y = displayRect.origin.y;
00467 }
00468 else if (pos.y > displayRect.origin.y +
00469 displayRect.size.height - 1) {
00470 pos.y = displayRect.origin.y + displayRect.size.height - 1;
00471 }
00472 }
00473 }
00474
00475 CGEventType type = kCGEventMouseMoved;
00476
00477 SInt8 button = m_buttonState.getFirstButtonDown();
00478 if (button != -1) {
00479 MouseButtonEventMapType thisButtonType = MouseButtonEventMap[button];
00480 type = thisButtonType[kMouseButtonDragged];
00481 }
00482
00483 CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, button);
00484 CGEventPost(kCGHIDEventTap, event);
00485
00486 CFRelease(event);
00487 }
00488
00489 void
00490 COSXScreen::fakeMouseButton(ButtonID id, bool press)
00491 {
00492 NXEventHandle handle = NXOpenEventStatus();
00493 double clickTime = NXClickTime(handle);
00494
00495 if ((ARCH->time() - m_lastDoubleClick) <= clickTime) {
00496
00497
00498
00499
00500
00501 LOG((CLOG_DEBUG1 "dropping mouse button %s",
00502 press ? "press" : "release"));
00503 return;
00504 }
00505
00506
00507 UInt32 index = id - kButtonLeft;
00508 if (index >= NumButtonIDs) {
00509 return;
00510 }
00511
00512 CGPoint pos;
00513 if (!m_cursorPosValid) {
00514 SInt32 x, y;
00515 getCursorPos(x, y);
00516 }
00517 pos.x = m_xCursor;
00518 pos.y = m_yCursor;
00519
00520
00521
00522 SInt32 xDiff = m_xCursor - m_lastSingleClickXCursor;
00523 SInt32 yDiff = m_yCursor - m_lastSingleClickYCursor;
00524 double diff = sqrt(xDiff * xDiff + yDiff * yDiff);
00525
00526
00527
00528 const double maxDiff = sqrt(2) + 0.0001;
00529
00530 if (press && (id == kButtonLeft) &&
00531 ((ARCH->time() - m_lastSingleClick) <= clickTime) &&
00532 diff <= maxDiff) {
00533
00534 LOG((CLOG_DEBUG1 "faking mouse left double click"));
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545
00546
00547 CGEventRef event = CGEventCreateMouseEvent(
00548 NULL, kCGEventLeftMouseDown, pos, kCGMouseButtonLeft);
00549
00550 CGEventSetIntegerValueField(event, kCGMouseEventClickState, 2);
00551 m_buttonState.set(index, kMouseButtonDown);
00552 CGEventPost(kCGHIDEventTap, event);
00553
00554 CGEventSetType(event, kCGEventLeftMouseUp);
00555 m_buttonState.set(index, kMouseButtonUp);
00556 CGEventPost(kCGHIDEventTap, event);
00557
00558 CFRelease(event);
00559
00560 m_lastDoubleClick = ARCH->time();
00561 }
00562 else {
00563
00564
00565
00566 MouseButtonState state = press ? kMouseButtonDown : kMouseButtonUp;
00567
00568 LOG((CLOG_DEBUG1 "faking mouse button %s", press ? "press" : "release"));
00569
00570 MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index];
00571 CGEventType type = thisButtonMap[state];
00572
00573 CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, index);
00574
00575 m_buttonState.set(index, state);
00576 CGEventPost(kCGHIDEventTap, event);
00577
00578 CFRelease(event);
00579
00580 m_lastSingleClick = ARCH->time();
00581 m_lastSingleClickXCursor = m_xCursor;
00582 m_lastSingleClickYCursor = m_yCursor;
00583 }
00584 }
00585
00586 void
00587 COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const
00588 {
00589
00590 CGPoint pos;
00591 pos.x = x;
00592 pos.y = y;
00593 postMouseEvent(pos);
00594
00595
00596 m_xCursor = static_cast<SInt32>(pos.x);
00597 m_yCursor = static_cast<SInt32>(pos.y);
00598 m_cursorPosValid = true;
00599 }
00600
00601 void
00602 COSXScreen::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
00603 {
00604
00605
00606
00607
00608
00609
00610 Point oldPos;
00611 GetGlobalMouse(&oldPos);
00612
00613
00614 CGPoint pos;
00615 m_xCursor = static_cast<SInt32>(oldPos.h);
00616 m_yCursor = static_cast<SInt32>(oldPos.v);
00617 pos.x = oldPos.h + dx;
00618 pos.y = oldPos.v + dy;
00619 postMouseEvent(pos);
00620
00621
00622 m_cursorPosValid = false;
00623 }
00624
00625 void
00626 COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
00627 {
00628 if (xDelta != 0 || yDelta != 0) {
00629 #if defined(MAC_OS_X_VERSION_10_5)
00630
00631
00632 CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(
00633 NULL, kCGScrollEventUnitLine, 2,
00634 mapScrollWheelFromSynergy(yDelta),
00635 -mapScrollWheelFromSynergy(xDelta));
00636
00637 CGEventPost(kCGHIDEventTap, scrollEvent);
00638 CFRelease(scrollEvent);
00639 #else
00640
00641 CGPostScrollWheelEvent(
00642 2, mapScrollWheelFromSynergy(yDelta),
00643 -mapScrollWheelFromSynergy(xDelta));
00644 #endif
00645 }
00646 }
00647
00648 void
00649 COSXScreen::showCursor()
00650 {
00651 LOG((CLOG_DEBUG "showing cursor"));
00652
00653 CFStringRef propertyString = CFStringCreateWithCString(
00654 NULL, "SetsCursorInBackground", kCFStringEncodingMacRoman);
00655
00656 CGSSetConnectionProperty(
00657 _CGSDefaultConnection(), _CGSDefaultConnection(),
00658 propertyString, kCFBooleanTrue);
00659
00660 CFRelease(propertyString);
00661
00662 CGError error = CGDisplayShowCursor(m_displayID);
00663 if (error != kCGErrorSuccess) {
00664 LOG((CLOG_ERR "failed to show cursor, error=%d", error));
00665 }
00666
00667
00668 CGAssociateMouseAndMouseCursorPosition(true);
00669
00670 if (!CGCursorIsVisible()) {
00671 LOG((CLOG_WARN "cursor may not be visible"));
00672 }
00673
00674 m_cursorHidden = false;
00675 }
00676
00677 void
00678 COSXScreen::hideCursor()
00679 {
00680 LOG((CLOG_DEBUG "hiding cursor"));
00681
00682 CFStringRef propertyString = CFStringCreateWithCString(
00683 NULL, "SetsCursorInBackground", kCFStringEncodingMacRoman);
00684
00685 CGSSetConnectionProperty(
00686 _CGSDefaultConnection(), _CGSDefaultConnection(),
00687 propertyString, kCFBooleanTrue);
00688
00689 CFRelease(propertyString);
00690
00691 CGError error = CGDisplayHideCursor(m_displayID);
00692 if (error != kCGErrorSuccess) {
00693 LOG((CLOG_ERR "failed to hide cursor, error=%d", error));
00694 }
00695
00696
00697 CGAssociateMouseAndMouseCursorPosition(true);
00698
00699 if (CGCursorIsVisible()) {
00700 LOG((CLOG_WARN "cursor may be still visible"));
00701 }
00702
00703 m_cursorHidden = true;
00704 }
00705
00706 void
00707 COSXScreen::enable()
00708 {
00709
00710 m_clipboardTimer = EVENTQUEUE->newTimer(1.0, NULL);
00711 EVENTQUEUE->adoptHandler(CEvent::kTimer, m_clipboardTimer,
00712 new TMethodEventJob<COSXScreen>(this,
00713 &COSXScreen::handleClipboardCheck));
00714
00715 if (m_isPrimary) {
00716
00717
00718
00719 m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0,
00720 kCGEventMaskForAllEvents,
00721 handleCGInputEvent,
00722 this);
00723 }
00724 else {
00725
00726
00727 if (m_autoShowHideCursor) {
00728 hideCursor();
00729 }
00730
00731
00732 fakeMouseMove(m_xCenter, m_yCenter);
00733
00734
00735
00736
00737 m_eventTapPort = CGEventTapCreate(kCGHIDEventTap, kCGHeadInsertEventTap, 0,
00738 kCGEventMaskForAllEvents,
00739 handleCGInputEventSecondary,
00740 this);
00741 }
00742
00743 if (!m_eventTapPort) {
00744 LOG((CLOG_ERR "failed to create quartz event tap"));
00745 }
00746
00747 m_eventTapRLSR = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, m_eventTapPort, 0);
00748 if (!m_eventTapRLSR) {
00749 LOG((CLOG_ERR "failed to create a CFRunLoopSourceRef for the quartz event tap"));
00750 }
00751
00752 CFRunLoopAddSource(CFRunLoopGetCurrent(), m_eventTapRLSR, kCFRunLoopDefaultMode);
00753 }
00754
00755 void
00756 COSXScreen::disable()
00757 {
00758 if (m_autoShowHideCursor) {
00759 showCursor();
00760 }
00761
00762
00763
00764 if (m_eventTapRLSR) {
00765 CFRelease(m_eventTapRLSR);
00766 m_eventTapRLSR = nullptr;
00767 }
00768
00769 if (m_eventTapPort) {
00770 CFRelease(m_eventTapPort);
00771 m_eventTapPort = nullptr;
00772 }
00773
00774
00775
00776 m_dragNumButtonsDown = 0;
00777 enableDragTimer(false);
00778
00779
00780 if (m_clipboardTimer != NULL) {
00781 EVENTQUEUE->removeHandler(CEvent::kTimer, m_clipboardTimer);
00782 EVENTQUEUE->deleteTimer(m_clipboardTimer);
00783 m_clipboardTimer = NULL;
00784 }
00785
00786 m_isOnScreen = m_isPrimary;
00787 }
00788
00789 void
00790 COSXScreen::enter()
00791 {
00792 showCursor();
00793
00794 if (m_isPrimary) {
00795 CGSetLocalEventsSuppressionInterval(0.0);
00796
00797
00798
00799 }
00800 else {
00801
00802 m_buttonState.reset();
00803
00804
00805
00806 CGSetLocalEventsFilterDuringSupressionState(
00807 kCGEventFilterMaskPermitAllEvents,
00808 kCGEventSupressionStateSupressionInterval);
00809 CGSetLocalEventsFilterDuringSupressionState(
00810 (kCGEventFilterMaskPermitLocalKeyboardEvents |
00811 kCGEventFilterMaskPermitSystemDefinedEvents),
00812 kCGEventSupressionStateRemoteMouseDrag);
00813 }
00814
00815
00816 m_isOnScreen = true;
00817 }
00818
00819 bool
00820 COSXScreen::leave()
00821 {
00822 hideCursor();
00823
00824 if (m_isPrimary) {
00825
00826
00827
00828
00829
00830
00831 CGSetLocalEventsSuppressionInterval(0.0001);
00832
00833
00834
00835 }
00836 else {
00837
00838
00839
00840
00841
00842
00843
00844 }
00845
00846
00847 m_isOnScreen = false;
00848
00849 return true;
00850 }
00851
00852 bool
00853 COSXScreen::setClipboard(ClipboardID, const IClipboard* src)
00854 {
00855 if(src != NULL) {
00856 LOG((CLOG_DEBUG "setting clipboard"));
00857 CClipboard::copy(&m_pasteboard, src);
00858 }
00859 return true;
00860 }
00861
00862 void
00863 COSXScreen::checkClipboards()
00864 {
00865 LOG((CLOG_DEBUG2 "checking clipboard"));
00866 if (m_pasteboard.synchronize()) {
00867 LOG((CLOG_DEBUG "clipboard changed"));
00868 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
00869 sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
00870 }
00871 }
00872
00873 void
00874 COSXScreen::openScreensaver(bool notify)
00875 {
00876 m_screensaverNotify = notify;
00877 if (!m_screensaverNotify) {
00878 m_screensaver->disable();
00879 }
00880 }
00881
00882 void
00883 COSXScreen::closeScreensaver()
00884 {
00885 if (!m_screensaverNotify) {
00886 m_screensaver->enable();
00887 }
00888 }
00889
00890 void
00891 COSXScreen::screensaver(bool activate)
00892 {
00893 if (activate) {
00894 m_screensaver->activate();
00895 }
00896 else {
00897 m_screensaver->deactivate();
00898 }
00899 }
00900
00901 void
00902 COSXScreen::resetOptions()
00903 {
00904
00905 }
00906
00907 void
00908 COSXScreen::setOptions(const COptionsList&)
00909 {
00910
00911 }
00912
00913 void
00914 COSXScreen::setSequenceNumber(UInt32 seqNum)
00915 {
00916 m_sequenceNumber = seqNum;
00917 }
00918
00919 bool
00920 COSXScreen::isPrimary() const
00921 {
00922 return m_isPrimary;
00923 }
00924
00925 void
00926 COSXScreen::sendEvent(CEvent::Type type, void* data) const
00927 {
00928 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
00929 }
00930
00931 void
00932 COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) const
00933 {
00934 CClipboardInfo* info = (CClipboardInfo*)malloc(sizeof(CClipboardInfo));
00935 info->m_id = id;
00936 info->m_sequenceNumber = m_sequenceNumber;
00937 sendEvent(type, info);
00938 }
00939
00940 void
00941 COSXScreen::handleSystemEvent(const CEvent& event, void*)
00942 {
00943 EventRef* carbonEvent = reinterpret_cast<EventRef*>(event.getData());
00944 assert(carbonEvent != NULL);
00945
00946 UInt32 eventClass = GetEventClass(*carbonEvent);
00947
00948 switch (eventClass) {
00949 case kEventClassMouse:
00950 switch (GetEventKind(*carbonEvent)) {
00951 case kSynergyEventMouseScroll:
00952 {
00953 OSStatus r;
00954 long xScroll;
00955 long yScroll;
00956
00957
00958 r = GetEventParameter(*carbonEvent,
00959 kSynergyMouseScrollAxisX,
00960 typeLongInteger,
00961 NULL,
00962 sizeof(xScroll),
00963 NULL,
00964 &xScroll);
00965 if (r != noErr) {
00966 xScroll = 0;
00967 }
00968 r = GetEventParameter(*carbonEvent,
00969 kSynergyMouseScrollAxisY,
00970 typeLongInteger,
00971 NULL,
00972 sizeof(yScroll),
00973 NULL,
00974 &yScroll);
00975 if (r != noErr) {
00976 yScroll = 0;
00977 }
00978
00979 if (xScroll != 0 || yScroll != 0) {
00980 onMouseWheel(-mapScrollWheelToSynergy(xScroll),
00981 mapScrollWheelToSynergy(yScroll));
00982 }
00983 }
00984 }
00985 break;
00986
00987 case kEventClassKeyboard:
00988 switch (GetEventKind(*carbonEvent)) {
00989 case kEventHotKeyPressed:
00990 case kEventHotKeyReleased:
00991 onHotKey(*carbonEvent);
00992 break;
00993 }
00994
00995 break;
00996
00997 case kEventClassWindow:
00998 SendEventToWindow(*carbonEvent, m_userInputWindow);
00999 switch (GetEventKind(*carbonEvent)) {
01000 case kEventWindowActivated:
01001 LOG((CLOG_DEBUG1 "window activated"));
01002 break;
01003
01004 case kEventWindowDeactivated:
01005 LOG((CLOG_DEBUG1 "window deactivated"));
01006 break;
01007
01008 case kEventWindowFocusAcquired:
01009 LOG((CLOG_DEBUG1 "focus acquired"));
01010 break;
01011
01012 case kEventWindowFocusRelinquish:
01013 LOG((CLOG_DEBUG1 "focus released"));
01014 break;
01015 }
01016 break;
01017
01018 default:
01019 SendEventToEventTarget(*carbonEvent, GetEventDispatcherTarget());
01020 break;
01021 }
01022 }
01023
01024 bool
01025 COSXScreen::onMouseMove(SInt32 mx, SInt32 my)
01026 {
01027 LOG((CLOG_DEBUG2 "mouse move %+d,%+d", mx, my));
01028
01029 SInt32 x = mx - m_xCursor;
01030 SInt32 y = my - m_yCursor;
01031
01032 if ((x == 0 && y == 0) || (mx == m_xCenter && mx == m_yCenter)) {
01033 return true;
01034 }
01035
01036
01037 m_xCursor = mx;
01038 m_yCursor = my;
01039
01040 if (m_isOnScreen) {
01041
01042 sendEvent(getMotionOnPrimaryEvent(),
01043 CMotionInfo::alloc(m_xCursor, m_yCursor));
01044 }
01045 else {
01046
01047
01048 warpCursor(m_xCenter, m_yCenter);
01049
01050
01051
01052
01053
01054
01055 static SInt32 bogusZoneSize = 10;
01056 if (-x + bogusZoneSize > m_xCenter - m_x ||
01057 x + bogusZoneSize > m_x + m_w - m_xCenter ||
01058 -y + bogusZoneSize > m_yCenter - m_y ||
01059 y + bogusZoneSize > m_y + m_h - m_yCenter) {
01060 LOG((CLOG_DEBUG "dropped bogus motion %+d,%+d", x, y));
01061 }
01062 else {
01063
01064 sendEvent(getMotionOnSecondaryEvent(), CMotionInfo::alloc(x, y));
01065 }
01066 }
01067
01068 return true;
01069 }
01070
01071 bool
01072 COSXScreen::onMouseButton(bool pressed, UInt16 macButton)
01073 {
01074
01075 ButtonID button = mapMacButtonToSynergy(macButton);
01076
01077 if (pressed) {
01078 LOG((CLOG_DEBUG1 "event: button press button=%d", button));
01079 if (button != kButtonNone) {
01080 KeyModifierMask mask = m_keyState->getActiveModifiers();
01081 sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button, mask));
01082 }
01083 }
01084 else {
01085 LOG((CLOG_DEBUG1 "event: button release button=%d", button));
01086 if (button != kButtonNone) {
01087 KeyModifierMask mask = m_keyState->getActiveModifiers();
01088 sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button, mask));
01089 }
01090 }
01091
01092
01093 if (macButton > 2) {
01094 if (pressed) {
01095
01096 if (m_dragNumButtonsDown++ == 0) {
01097 enableDragTimer(true);
01098 }
01099 }
01100 else {
01101
01102 if (--m_dragNumButtonsDown == 0) {
01103 enableDragTimer(false);
01104 }
01105 }
01106 }
01107
01108 return true;
01109 }
01110
01111 bool
01112 COSXScreen::onMouseWheel(SInt32 xDelta, SInt32 yDelta) const
01113 {
01114 LOG((CLOG_DEBUG1 "event: button wheel delta=%+d,%+d", xDelta, yDelta));
01115 sendEvent(getWheelEvent(), CWheelInfo::alloc(xDelta, yDelta));
01116 return true;
01117 }
01118
01119 void
01120 COSXScreen::handleClipboardCheck(const CEvent&, void*)
01121 {
01122 checkClipboards();
01123 }
01124
01125 #if !defined(MAC_OS_X_VERSION_10_5)
01126 pascal void
01127 COSXScreen::displayManagerCallback(void* inUserData, SInt16 inMessage, void*)
01128 {
01129 COSXScreen* screen = (COSXScreen*)inUserData;
01130
01131 if (inMessage == kDMNotifyEvent) {
01132 screen->onDisplayChange();
01133 }
01134 }
01135
01136 bool
01137 COSXScreen::onDisplayChange()
01138 {
01139
01140 SInt32 xOld = m_x, yOld = m_y, wOld = m_w, hOld = m_h;
01141
01142
01143 updateScreenShape();
01144
01145
01146 if (xOld != m_x || yOld != m_y || wOld != m_w || hOld != m_h) {
01147 if (m_isPrimary) {
01148
01149 if (!m_isOnScreen) {
01150 warpCursor(m_xCenter, m_yCenter);
01151 }
01152 }
01153
01154
01155 sendEvent(getShapeChangedEvent());
01156 }
01157
01158 return true;
01159 }
01160 #else
01161 void
01162 COSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDisplayChangeSummaryFlags flags, void* inUserData)
01163 {
01164 COSXScreen* screen = (COSXScreen*)inUserData;
01165
01166
01167
01168 CGDisplayChangeSummaryFlags mask = kCGDisplayBeginConfigurationFlag | kCGDisplayMovedFlag |
01169 kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag |
01170 kCGDisplayEnabledFlag | kCGDisplayDisabledFlag |
01171 kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag |
01172 kCGDisplayDesktopShapeChangedFlag;
01173
01174 LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask));
01175
01176 if (flags & mask) {
01177
01178 LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions"));
01179 screen->updateScreenShape(displayID, flags);
01180 }
01181 }
01182 #endif
01183
01184 bool
01185 COSXScreen::onKey(CGEventRef event)
01186 {
01187 CGEventType eventKind = CGEventGetType(event);
01188
01189
01190 UInt32 virtualKey = CGEventGetIntegerValueField(event, kCGKeyboardEventKeycode);
01191 CGEventFlags macMask = CGEventGetFlags(event);
01192 LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));
01193
01194
01195 if (eventKind == kCGEventFlagsChanged) {
01196
01197 KeyModifierMask oldMask = getActiveModifiers();
01198 KeyModifierMask newMask = m_keyState->mapModifiersFromOSX(macMask);
01199 m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);
01200
01201
01202
01203 if (m_activeModifierHotKey == 0) {
01204 if (m_modifierHotKeys.count(newMask) > 0) {
01205 m_activeModifierHotKey = m_modifierHotKeys[newMask];
01206 m_activeModifierHotKeyMask = newMask;
01207 EVENTQUEUE->addEvent(CEvent(getHotKeyDownEvent(),
01208 getEventTarget(),
01209 CHotKeyInfo::alloc(m_activeModifierHotKey)));
01210 }
01211 }
01212
01213
01214
01215 else if (m_activeModifierHotKey != 0) {
01216 KeyModifierMask mask = (newMask & m_activeModifierHotKeyMask);
01217 if (mask != m_activeModifierHotKeyMask) {
01218 EVENTQUEUE->addEvent(CEvent(getHotKeyUpEvent(),
01219 getEventTarget(),
01220 CHotKeyInfo::alloc(m_activeModifierHotKey)));
01221 m_activeModifierHotKey = 0;
01222 m_activeModifierHotKeyMask = 0;
01223 }
01224 }
01225
01226 return true;
01227 }
01228
01229
01230
01231
01232
01233 if (!m_isOnScreen) {
01234 HotKeyToIDMap::const_iterator i =
01235 m_hotKeyToIDMap.find(CHotKeyItem(virtualKey,
01236 m_keyState->mapModifiersToCarbon(macMask)
01237 & 0xff00u));
01238 if (i != m_hotKeyToIDMap.end()) {
01239 UInt32 id = i->second;
01240
01241
01242 CEvent::Type type;
01243
01244 if (eventKind == kCGEventKeyDown) {
01245 type = getHotKeyDownEvent();
01246 }
01247 else if (eventKind == kCGEventKeyUp) {
01248 type = getHotKeyUpEvent();
01249 }
01250 else {
01251 return false;
01252 }
01253
01254 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
01255 CHotKeyInfo::alloc(id)));
01256
01257 return true;
01258 }
01259 }
01260
01261
01262 bool down = (eventKind == kCGEventKeyDown);
01263 bool up = (eventKind == kCGEventKeyUp);
01264 bool isRepeat = (CGEventGetIntegerValueField(event, kCGKeyboardEventAutorepeat) == 1);
01265
01266
01267 KeyModifierMask mask;
01268 COSXKeyState::CKeyIDs keys;
01269 KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
01270 if (button == 0) {
01271 return false;
01272 }
01273
01274
01275
01276
01277 KeyModifierMask sendMask = (mask & ~KeyModifierAltGr);
01278 if ((mask & KeyModifierAltGr) != 0) {
01279 sendMask &= ~KeyModifierSuper;
01280 }
01281 mask &= ~KeyModifierAltGr;
01282
01283
01284 if (down) {
01285 m_keyState->onKey(button, true, mask);
01286 }
01287 else if (up) {
01288 if (!m_keyState->isKeyDown(button)) {
01289
01290 return false;
01291 }
01292 m_keyState->onKey(button, false, mask);
01293 }
01294
01295
01296 for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin();
01297 i != keys.end(); ++i) {
01298 m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat,
01299 *i, sendMask, 1, button);
01300 }
01301
01302 return true;
01303 }
01304
01305 bool
01306 COSXScreen::onHotKey(EventRef event) const
01307 {
01308
01309 EventHotKeyID hkid;
01310 GetEventParameter(event, kEventParamDirectObject, typeEventHotKeyID,
01311 NULL, sizeof(EventHotKeyID), NULL, &hkid);
01312 UInt32 id = hkid.id;
01313
01314
01315 CEvent::Type type;
01316 UInt32 eventKind = GetEventKind(event);
01317 if (eventKind == kEventHotKeyPressed) {
01318 type = getHotKeyDownEvent();
01319 }
01320 else if (eventKind == kEventHotKeyReleased) {
01321 type = getHotKeyUpEvent();
01322 }
01323 else {
01324 return false;
01325 }
01326
01327 EVENTQUEUE->addEvent(CEvent(type, getEventTarget(),
01328 CHotKeyInfo::alloc(id)));
01329
01330 return true;
01331 }
01332
01333 ButtonID
01334 COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const
01335 {
01336 switch (macButton) {
01337 case 1:
01338 return kButtonLeft;
01339
01340 case 2:
01341 return kButtonRight;
01342
01343 case 3:
01344 return kButtonMiddle;
01345 }
01346
01347 return static_cast<ButtonID>(macButton);
01348 }
01349
01350 SInt32
01351 COSXScreen::mapScrollWheelToSynergy(SInt32 x) const
01352 {
01353
01354
01355 double d = (1.0 + getScrollSpeed()) * x / getScrollSpeedFactor();
01356 return static_cast<SInt32>(120.0 * d);
01357 }
01358
01359 SInt32
01360 COSXScreen::mapScrollWheelFromSynergy(SInt32 x) const
01361 {
01362
01363
01364 return static_cast<SInt32>(3.0 * x / 120.0);
01365 }
01366
01367 double
01368 COSXScreen::getScrollSpeed() const
01369 {
01370 double scaling = 0.0;
01371
01372 CFPropertyListRef pref = ::CFPreferencesCopyValue(
01373 CFSTR("com.apple.scrollwheel.scaling") ,
01374 kCFPreferencesAnyApplication,
01375 kCFPreferencesCurrentUser,
01376 kCFPreferencesAnyHost);
01377 if (pref != NULL) {
01378 CFTypeID id = CFGetTypeID(pref);
01379 if (id == CFNumberGetTypeID()) {
01380 CFNumberRef value = static_cast<CFNumberRef>(pref);
01381 if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) {
01382 if (scaling < 0.0) {
01383 scaling = 0.0;
01384 }
01385 }
01386 }
01387 CFRelease(pref);
01388 }
01389
01390 return scaling;
01391 }
01392
01393 double
01394 COSXScreen::getScrollSpeedFactor() const
01395 {
01396 return pow(10.0, getScrollSpeed());
01397 }
01398
01399 void
01400 COSXScreen::enableDragTimer(bool enable)
01401 {
01402 UInt32 modifiers;
01403 MouseTrackingResult res;
01404
01405 if (enable && m_dragTimer == NULL) {
01406 m_dragTimer = EVENTQUEUE->newTimer(0.01, NULL);
01407 EVENTQUEUE->adoptHandler(CEvent::kTimer, m_dragTimer,
01408 new TMethodEventJob<COSXScreen>(this,
01409 &COSXScreen::handleDrag));
01410 TrackMouseLocationWithOptions(NULL, 0, 0, &m_dragLastPoint, &modifiers, &res);
01411 }
01412 else if (!enable && m_dragTimer != NULL) {
01413 EVENTQUEUE->removeHandler(CEvent::kTimer, m_dragTimer);
01414 EVENTQUEUE->deleteTimer(m_dragTimer);
01415 m_dragTimer = NULL;
01416 }
01417 }
01418
01419 void
01420 COSXScreen::handleDrag(const CEvent&, void*)
01421 {
01422 Point p;
01423 UInt32 modifiers;
01424 MouseTrackingResult res;
01425
01426 TrackMouseLocationWithOptions(NULL, 0, 0, &p, &modifiers, &res);
01427
01428 if (res != kMouseTrackingTimedOut && (p.h != m_dragLastPoint.h || p.v != m_dragLastPoint.v)) {
01429 m_dragLastPoint = p;
01430 onMouseMove((SInt32)p.h, (SInt32)p.v);
01431 }
01432 }
01433
01434 void
01435 COSXScreen::updateButtons()
01436 {
01437 UInt32 buttons = GetCurrentButtonState();
01438
01439 m_buttonState.overwrite(buttons);
01440 }
01441
01442 IKeyState*
01443 COSXScreen::getKeyState() const
01444 {
01445 return m_keyState;
01446 }
01447
01448 void
01449 COSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags flags)
01450 {
01451 updateScreenShape();
01452 }
01453
01454 void
01455 COSXScreen::updateScreenShape()
01456 {
01457
01458 CGDisplayCount displayCount = 0;
01459
01460 if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) {
01461 return;
01462 }
01463
01464 if (displayCount == 0) {
01465 return;
01466 }
01467
01468 CGDirectDisplayID* displays = new CGDirectDisplayID[displayCount];
01469 if (displays == NULL) {
01470 return;
01471 }
01472
01473 if (CGGetActiveDisplayList(displayCount,
01474 displays, &displayCount) != CGDisplayNoErr) {
01475 delete[] displays;
01476 return;
01477 }
01478
01479
01480 CGRect totalBounds = CGRectZero;
01481 for (CGDisplayCount i = 0; i < displayCount; ++i) {
01482 CGRect bounds = CGDisplayBounds(displays[i]);
01483 totalBounds = CGRectUnion(totalBounds, bounds);
01484 }
01485
01486
01487 m_x = (SInt32)totalBounds.origin.x;
01488 m_y = (SInt32)totalBounds.origin.y;
01489 m_w = (SInt32)totalBounds.size.width;
01490 m_h = (SInt32)totalBounds.size.height;
01491
01492
01493 CGDirectDisplayID main = CGMainDisplayID();
01494 const CGRect rect = CGDisplayBounds(main);
01495 m_xCenter = (rect.origin.x + rect.size.width) / 2;
01496 m_yCenter = (rect.origin.y + rect.size.height) / 2;
01497
01498 delete[] displays;
01499
01500 sendEvent(getShapeChangedEvent());
01501
01502 LOG((CLOG_DEBUG "screen shape: center=%d,%d size=%dx%d on %u %s",
01503 m_x, m_y, m_w, m_h, displayCount,
01504 (displayCount == 1) ? "display" : "displays"));
01505 }
01506
01507 #pragma mark -
01508
01509
01510
01511
01512
01513
01514
01515
01516
01517 pascal OSStatus
01518 COSXScreen::userSwitchCallback(EventHandlerCallRef nextHandler,
01519 EventRef theEvent,
01520 void* inUserData)
01521 {
01522 COSXScreen* screen = (COSXScreen*)inUserData;
01523 UInt32 kind = GetEventKind(theEvent);
01524
01525 if (kind == kEventSystemUserSessionDeactivated) {
01526 LOG((CLOG_DEBUG "user session deactivated"));
01527 EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(),
01528 screen->getEventTarget()));
01529 }
01530 else if (kind == kEventSystemUserSessionActivated) {
01531 LOG((CLOG_DEBUG "user session activated"));
01532 EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(),
01533 screen->getEventTarget()));
01534 }
01535 return (CallNextEventHandler(nextHandler, theEvent));
01536 }
01537
01538 #pragma mark -
01539
01540
01541
01542
01543
01544
01545
01546
01547
01548 void
01549 COSXScreen::watchSystemPowerThread(void*)
01550 {
01551 io_object_t notifier;
01552 IONotificationPortRef notificationPortRef;
01553 CFRunLoopSourceRef runloopSourceRef = 0;
01554
01555 m_pmRunloop = CFRunLoopGetCurrent();
01556
01557 m_pmRootPort = IORegisterForSystemPower(this, ¬ificationPortRef,
01558 powerChangeCallback, ¬ifier);
01559 if (m_pmRootPort == 0) {
01560 LOG((CLOG_WARN "IORegisterForSystemPower failed"));
01561 }
01562 else {
01563 runloopSourceRef =
01564 IONotificationPortGetRunLoopSource(notificationPortRef);
01565 CFRunLoopAddSource(m_pmRunloop, runloopSourceRef,
01566 kCFRunLoopCommonModes);
01567 }
01568
01569
01570 {
01571 CLock lock(m_pmMutex);
01572 *m_pmThreadReady = true;
01573 m_pmThreadReady->signal();
01574 }
01575
01576
01577
01578
01579 if (m_pmRootPort == 0) {
01580 return;
01581 }
01582
01583
01584 LOG((CLOG_DEBUG "started watchSystemPowerThread"));
01585 CFRunLoopRun();
01586
01587
01588 if (notificationPortRef) {
01589 CFRunLoopRemoveSource(m_pmRunloop,
01590 runloopSourceRef, kCFRunLoopDefaultMode);
01591 CFRunLoopSourceInvalidate(runloopSourceRef);
01592 CFRelease(runloopSourceRef);
01593 }
01594
01595 CLock lock(m_pmMutex);
01596 IODeregisterForSystemPower(¬ifier);
01597 m_pmRootPort = 0;
01598 LOG((CLOG_DEBUG "stopped watchSystemPowerThread"));
01599 }
01600
01601 void
01602 COSXScreen::powerChangeCallback(void* refcon, io_service_t service,
01603 natural_t messageType, void* messageArg)
01604 {
01605 ((COSXScreen*)refcon)->handlePowerChangeRequest(messageType, messageArg);
01606 }
01607
01608 void
01609 COSXScreen::handlePowerChangeRequest(natural_t messageType, void* messageArg)
01610 {
01611
01612 switch (messageType) {
01613 case kIOMessageSystemWillSleep:
01614
01615
01616
01617 EVENTQUEUE->addEvent(CEvent(COSXScreen::getConfirmSleepEvent(),
01618 getEventTarget(), messageArg,
01619 CEvent::kDontFreeData));
01620 return;
01621
01622 case kIOMessageSystemHasPoweredOn:
01623 LOG((CLOG_DEBUG "system wakeup"));
01624 EVENTQUEUE->addEvent(CEvent(IScreen::getResumeEvent(),
01625 getEventTarget()));
01626 break;
01627
01628 default:
01629 break;
01630 }
01631
01632 CLock lock(m_pmMutex);
01633 if (m_pmRootPort != 0) {
01634 IOAllowPowerChange(m_pmRootPort, (long)messageArg);
01635 }
01636 }
01637
01638 CEvent::Type
01639 COSXScreen::getConfirmSleepEvent()
01640 {
01641 return EVENTQUEUE->registerTypeOnce(s_confirmSleepEvent,
01642 "COSXScreen::confirmSleep");
01643 }
01644
01645 void
01646 COSXScreen::handleConfirmSleep(const CEvent& event, void*)
01647 {
01648 long messageArg = (long)event.getData();
01649 if (messageArg != 0) {
01650 CLock lock(m_pmMutex);
01651 if (m_pmRootPort != 0) {
01652
01653 EVENTQUEUE->addEvent(CEvent(IScreen::getSuspendEvent(),
01654 getEventTarget(), NULL,
01655 CEvent::kDeliverImmediately));
01656
01657 LOG((CLOG_DEBUG "system will sleep"));
01658 IOAllowPowerChange(m_pmRootPort, messageArg);
01659 }
01660 }
01661 }
01662
01663 #pragma mark -
01664
01665
01666
01667
01668
01669
01670
01671
01672
01673
01674
01675
01676
01677
01678
01679 #if 0
01680
01681 #ifdef __cplusplus
01682 extern "C" {
01683 #endif
01684
01685 typedef int CGSConnection;
01686 typedef enum {
01687 CGSGlobalHotKeyEnable = 0,
01688 CGSGlobalHotKeyDisable = 1,
01689 } CGSGlobalHotKeyOperatingMode;
01690
01691 extern CGSConnection _CGSDefaultConnection(void) WEAK_IMPORT_ATTRIBUTE;
01692 extern CGError CGSGetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode) WEAK_IMPORT_ATTRIBUTE;
01693 extern CGError CGSSetGlobalHotKeyOperatingMode(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode) WEAK_IMPORT_ATTRIBUTE;
01694
01695 typedef CGSConnection (*_CGSDefaultConnection_t)(void);
01696 typedef CGError (*CGSGetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode *mode);
01697 typedef CGError (*CGSSetGlobalHotKeyOperatingMode_t)(CGSConnection connection, CGSGlobalHotKeyOperatingMode mode);
01698
01699 static _CGSDefaultConnection_t s__CGSDefaultConnection;
01700 static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode;
01701 static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode;
01702
01703 #ifdef __cplusplus
01704 }
01705 #endif
01706
01707 #define LOOKUP(name_) \
01708 s_ ## name_ = NULL; \
01709 if (NSIsSymbolNameDefinedWithHint("_" #name_, "CoreGraphics")) { \
01710 s_ ## name_ = (name_ ## _t)NSAddressOfSymbol( \
01711 NSLookupAndBindSymbolWithHint( \
01712 "_" #name_, "CoreGraphics")); \
01713 }
01714
01715 bool
01716 COSXScreen::isGlobalHotKeyOperatingModeAvailable()
01717 {
01718 if (!s_testedForGHOM) {
01719 s_testedForGHOM = true;
01720 LOOKUP(_CGSDefaultConnection);
01721 LOOKUP(CGSGetGlobalHotKeyOperatingMode);
01722 LOOKUP(CGSSetGlobalHotKeyOperatingMode);
01723 s_hasGHOM = (s__CGSDefaultConnection != NULL &&
01724 s_CGSGetGlobalHotKeyOperatingMode != NULL &&
01725 s_CGSSetGlobalHotKeyOperatingMode != NULL);
01726 }
01727 return s_hasGHOM;
01728 }
01729
01730 void
01731 COSXScreen::setGlobalHotKeysEnabled(bool enabled)
01732 {
01733 if (isGlobalHotKeyOperatingModeAvailable()) {
01734 CGSConnection conn = s__CGSDefaultConnection();
01735
01736 CGSGlobalHotKeyOperatingMode mode;
01737 s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
01738
01739 if (enabled && mode == CGSGlobalHotKeyDisable) {
01740 s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyEnable);
01741 }
01742 else if (!enabled && mode == CGSGlobalHotKeyEnable) {
01743 s_CGSSetGlobalHotKeyOperatingMode(conn, CGSGlobalHotKeyDisable);
01744 }
01745 }
01746 }
01747
01748 bool
01749 COSXScreen::getGlobalHotKeysEnabled()
01750 {
01751 CGSGlobalHotKeyOperatingMode mode;
01752 if (isGlobalHotKeyOperatingModeAvailable()) {
01753 CGSConnection conn = s__CGSDefaultConnection();
01754 s_CGSGetGlobalHotKeyOperatingMode(conn, &mode);
01755 }
01756 else {
01757 mode = CGSGlobalHotKeyEnable;
01758 }
01759 return (mode == CGSGlobalHotKeyEnable);
01760 }
01761
01762 #endif
01763
01764
01765
01766
01767
01768 COSXScreen::CHotKeyItem::CHotKeyItem(UInt32 keycode, UInt32 mask) :
01769 m_ref(NULL),
01770 m_keycode(keycode),
01771 m_mask(mask)
01772 {
01773
01774 }
01775
01776 COSXScreen::CHotKeyItem::CHotKeyItem(EventHotKeyRef ref,
01777 UInt32 keycode, UInt32 mask) :
01778 m_ref(ref),
01779 m_keycode(keycode),
01780 m_mask(mask)
01781 {
01782
01783 }
01784
01785 EventHotKeyRef
01786 COSXScreen::CHotKeyItem::getRef() const
01787 {
01788 return m_ref;
01789 }
01790
01791 bool
01792 COSXScreen::CHotKeyItem::operator<(const CHotKeyItem& x) const
01793 {
01794 return (m_keycode < x.m_keycode ||
01795 (m_keycode == x.m_keycode && m_mask < x.m_mask));
01796 }
01797
01798
01799
01800
01801 CGEventRef
01802 COSXScreen::handleCGInputEventSecondary(
01803 CGEventTapProxy proxy,
01804 CGEventType type,
01805 CGEventRef event,
01806 void* refcon)
01807 {
01808
01809
01810 return event;
01811
01812 COSXScreen* screen = (COSXScreen*)refcon;
01813 if (screen->m_cursorHidden && type == kCGEventMouseMoved) {
01814
01815 CGPoint pos = CGEventGetLocation(event);
01816 if (pos.x != screen->m_xCenter || pos.y != screen->m_yCenter) {
01817
01818 LOG((CLOG_DEBUG "show cursor on secondary, type=%d pos=%d,%d",
01819 type, pos.x, pos.y));
01820 screen->showCursor();
01821 }
01822 }
01823 return event;
01824 }
01825
01826
01827 CGEventRef
01828 COSXScreen::handleCGInputEvent(CGEventTapProxy proxy,
01829 CGEventType type,
01830 CGEventRef event,
01831 void* refcon)
01832 {
01833 COSXScreen* screen = (COSXScreen*)refcon;
01834 CGPoint pos;
01835
01836 switch(type) {
01837 case kCGEventLeftMouseDown:
01838 case kCGEventRightMouseDown:
01839 case kCGEventOtherMouseDown:
01840 screen->onMouseButton(true, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
01841 break;
01842 case kCGEventLeftMouseUp:
01843 case kCGEventRightMouseUp:
01844 case kCGEventOtherMouseUp:
01845 screen->onMouseButton(false, CGEventGetIntegerValueField(event, kCGMouseEventButtonNumber) + 1);
01846 break;
01847 case kCGEventMouseMoved:
01848 case kCGEventLeftMouseDragged:
01849 case kCGEventRightMouseDragged:
01850 case kCGEventOtherMouseDragged:
01851 pos = CGEventGetLocation(event);
01852 screen->onMouseMove(pos.x, pos.y);
01853
01854
01855
01856
01857
01858 return event;
01859 break;
01860 case kCGEventScrollWheel:
01861 screen->onMouseWheel(screen->mapScrollWheelToSynergy(
01862 CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis2)),
01863 screen->mapScrollWheelToSynergy(
01864 CGEventGetIntegerValueField(event, kCGScrollWheelEventDeltaAxis1)));
01865 break;
01866 case kCGEventKeyDown:
01867 case kCGEventKeyUp:
01868 case kCGEventFlagsChanged:
01869 screen->onKey(event);
01870 break;
01871 case kCGEventTapDisabledByTimeout:
01872
01873 CGEventTapEnable(screen->m_eventTapPort, true);
01874 LOG((CLOG_NOTE "quartz event tap was disabled by timeout, re-enabling"));
01875 break;
01876 case kCGEventTapDisabledByUserInput:
01877 LOG((CLOG_ERR "quartz event tap was disabled by user input"));
01878 break;
01879 case NX_NULLEVENT:
01880 break;
01881 case NX_SYSDEFINED:
01882
01883 return event;
01884 break;
01885 case NX_NUMPROCS:
01886 break;
01887 default:
01888 LOG((CLOG_NOTE "unknown quartz event type: 0x%02x", type));
01889 }
01890
01891 if(screen->m_isOnScreen) {
01892 return event;
01893 } else {
01894 return NULL;
01895 }
01896 }
01897
01898 void
01899 COSXScreen::CMouseButtonState::set(UInt32 button, MouseButtonState state)
01900 {
01901 bool newState = (state == kMouseButtonDown);
01902 m_buttons.set(button, newState);
01903 }
01904
01905 bool
01906 COSXScreen::CMouseButtonState::any()
01907 {
01908 return m_buttons.any();
01909 }
01910
01911 void
01912 COSXScreen::CMouseButtonState::reset()
01913 {
01914 m_buttons.reset();
01915 }
01916
01917 void
01918 COSXScreen::CMouseButtonState::overwrite(UInt32 buttons)
01919 {
01920 m_buttons = std::bitset<NumButtonIDs>(buttons);
01921 }
01922
01923 bool
01924 COSXScreen::CMouseButtonState::test(UInt32 button) const
01925 {
01926 return m_buttons.test(button);
01927 }
01928
01929 SInt8
01930 COSXScreen::CMouseButtonState::getFirstButtonDown() const
01931 {
01932 if (m_buttons.any()) {
01933 for (unsigned short button = 0; button < m_buttons.size(); button++) {
01934 if (m_buttons.test(button)) {
01935 return button;
01936 }
01937 }
01938 }
01939 return -1;
01940 }