2009-02-27 11:54:59 +00:00
/*
2011-01-15 04:01:31 +00:00
* synergy - - mouse and keyboard sharing utility
* Copyright ( C ) 2004 Chris Schoeneman , Nick Bolton , Sorin Sbarnea
2009-02-27 11:54:59 +00:00
*
* 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 COPYING 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 .
2010-06-20 17:38:51 +00:00
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
2009-02-27 11:54:59 +00:00
*/
# include "COSXScreen.h"
# include "COSXClipboard.h"
# include "COSXEventQueueBuffer.h"
# include "COSXKeyState.h"
# include "COSXScreenSaver.h"
# include "CClipboard.h"
# include "CKeyMap.h"
# include "CCondVar.h"
# include "CLock.h"
# include "CMutex.h"
# include "CThread.h"
# include "CLog.h"
# include "IEventQueue.h"
# include "TMethodEventJob.h"
# include "TMethodJob.h"
# include "XArch.h"
# include <mach-o/dyld.h>
# include <AvailabilityMacros.h>
// Set some enums for fast user switching if we're building with an SDK
// from before such support was added.
# if !defined(MAC_OS_X_VERSION_10_3) || \
( MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3 )
enum {
2010-06-01 10:49:22 +00:00
kEventClassSystem = ' macs ' ,
2009-02-27 11:54:59 +00:00
kEventSystemUserSessionActivated = 10 ,
kEventSystemUserSessionDeactivated = 11
} ;
# endif
// This isn't in any Apple SDK that I know of as of yet.
enum {
kSynergyEventMouseScroll = 11 ,
kSynergyMouseScrollAxisX = ' saxx ' ,
kSynergyMouseScrollAxisY = ' saxy '
} ;
//
// COSXScreen
//
2010-06-01 10:49:22 +00:00
bool COSXScreen : : s_testedForGHOM = false ;
bool COSXScreen : : s_hasGHOM = false ;
2009-02-27 11:54:59 +00:00
CEvent : : Type COSXScreen : : s_confirmSleepEvent = CEvent : : kUnknown ;
COSXScreen : : COSXScreen ( bool isPrimary ) :
2010-06-01 10:49:22 +00:00
MouseButtonEventMap ( NumButtonIDs ) ,
2009-02-27 11:54:59 +00:00
m_isPrimary ( isPrimary ) ,
m_isOnScreen ( m_isPrimary ) ,
m_cursorPosValid ( false ) ,
m_cursorHidden ( false ) ,
m_dragNumButtonsDown ( 0 ) ,
m_dragTimer ( NULL ) ,
m_keyState ( NULL ) ,
m_sequenceNumber ( 0 ) ,
m_screensaver ( NULL ) ,
m_screensaverNotify ( false ) ,
m_ownClipboard ( false ) ,
m_clipboardTimer ( NULL ) ,
m_hiddenWindow ( NULL ) ,
m_userInputWindow ( NULL ) ,
m_switchEventHandlerRef ( 0 ) ,
m_pmMutex ( new CMutex ) ,
m_pmWatchThread ( NULL ) ,
m_pmThreadReady ( new CCondVar < bool > ( m_pmMutex , false ) ) ,
m_activeModifierHotKey ( 0 ) ,
m_activeModifierHotKeyMask ( 0 )
{
try {
m_displayID = CGMainDisplayID ( ) ;
2009-04-24 09:14:03 +00:00
updateScreenShape ( m_displayID , 0 ) ;
2009-02-27 11:54:59 +00:00
m_screensaver = new COSXScreenSaver ( getEventTarget ( ) ) ;
m_keyState = new COSXKeyState ( ) ;
2009-11-01 19:15:10 +00:00
2009-02-27 11:54:59 +00:00
if ( m_isPrimary ) {
2009-11-01 19:15:10 +00:00
if ( ! AXAPIEnabled ( ) ) {
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. " ) ) ;
}
2009-02-27 11:54:59 +00:00
}
2011-01-23 19:48:38 +00:00
2009-02-27 11:54:59 +00:00
// install display manager notification handler
2011-01-23 19:48:38 +00:00
# if defined(MAC_OS_X_VERSION_10_5)
2009-10-21 16:25:08 +00:00
CGDisplayRegisterReconfigurationCallback ( displayReconfigurationCallback , this ) ;
2011-01-23 19:48:38 +00:00
# else
m_displayManagerNotificationUPP =
NewDMExtendedNotificationUPP ( displayManagerCallback ) ;
OSStatus err = GetCurrentProcess ( & m_PSN ) ;
err = DMRegisterExtendedNotifyProc ( m_displayManagerNotificationUPP ,
this , 0 , & m_PSN ) ;
# endif
2009-02-27 11:54:59 +00:00
// install fast user switching event handler
EventTypeSpec switchEventTypes [ 2 ] ;
switchEventTypes [ 0 ] . eventClass = kEventClassSystem ;
switchEventTypes [ 0 ] . eventKind = kEventSystemUserSessionDeactivated ;
switchEventTypes [ 1 ] . eventClass = kEventClassSystem ;
switchEventTypes [ 1 ] . eventKind = kEventSystemUserSessionActivated ;
EventHandlerUPP switchEventHandler =
NewEventHandlerUPP ( userSwitchCallback ) ;
InstallApplicationEventHandler ( switchEventHandler , 2 , switchEventTypes ,
this , & m_switchEventHandlerRef ) ;
DisposeEventHandlerUPP ( switchEventHandler ) ;
2010-06-01 10:49:22 +00:00
constructMouseButtonEventMap ( ) ;
2009-02-27 11:54:59 +00:00
// watch for requests to sleep
EVENTQUEUE - > adoptHandler ( COSXScreen : : getConfirmSleepEvent ( ) ,
getEventTarget ( ) ,
new TMethodEventJob < COSXScreen > ( this ,
& COSXScreen : : handleConfirmSleep ) ) ;
// create thread for monitoring system power state.
LOG ( ( CLOG_DEBUG " starting watchSystemPowerThread " ) ) ;
m_pmWatchThread = new CThread ( new TMethodJob < COSXScreen >
( this , & COSXScreen : : watchSystemPowerThread ) ) ;
}
catch ( . . . ) {
EVENTQUEUE - > removeHandler ( COSXScreen : : getConfirmSleepEvent ( ) ,
getEventTarget ( ) ) ;
if ( m_switchEventHandlerRef ! = 0 ) {
RemoveEventHandler ( m_switchEventHandlerRef ) ;
}
2011-01-23 19:48:38 +00:00
# if defined(MAC_OS_X_VERSION_10_5)
2009-10-21 16:25:08 +00:00
CGDisplayRemoveReconfigurationCallback ( displayReconfigurationCallback , this ) ;
2011-01-23 19:48:38 +00:00
# else
if ( m_displayManagerNotificationUPP ! = NULL ) {
DMRemoveExtendedNotifyProc ( m_displayManagerNotificationUPP ,
NULL , & m_PSN , 0 ) ;
}
if ( m_hiddenWindow ) {
ReleaseWindow ( m_hiddenWindow ) ;
m_hiddenWindow = NULL ;
}
if ( m_userInputWindow ) {
ReleaseWindow ( m_userInputWindow ) ;
m_userInputWindow = NULL ;
}
# endif
2009-02-27 11:54:59 +00:00
delete m_keyState ;
delete m_screensaver ;
throw ;
}
// install event handlers
EVENTQUEUE - > adoptHandler ( CEvent : : kSystem , IEventQueue : : getSystemTarget ( ) ,
new TMethodEventJob < COSXScreen > ( this ,
& COSXScreen : : handleSystemEvent ) ) ;
// install the platform event queue
EVENTQUEUE - > adoptBuffer ( new COSXEventQueueBuffer ) ;
}
COSXScreen : : ~ COSXScreen ( )
{
disable ( ) ;
EVENTQUEUE - > adoptBuffer ( NULL ) ;
EVENTQUEUE - > removeHandler ( CEvent : : kSystem , IEventQueue : : getSystemTarget ( ) ) ;
if ( m_pmWatchThread ) {
// make sure the thread has setup the runloop.
{
CLock lock ( m_pmMutex ) ;
while ( ! ( bool ) * m_pmThreadReady ) {
m_pmThreadReady - > wait ( ) ;
}
}
// now exit the thread's runloop and wait for it to exit
LOG ( ( CLOG_DEBUG " stopping watchSystemPowerThread " ) ) ;
CFRunLoopStop ( m_pmRunloop ) ;
m_pmWatchThread - > wait ( ) ;
delete m_pmWatchThread ;
m_pmWatchThread = NULL ;
}
delete m_pmThreadReady ;
delete m_pmMutex ;
EVENTQUEUE - > removeHandler ( COSXScreen : : getConfirmSleepEvent ( ) ,
getEventTarget ( ) ) ;
RemoveEventHandler ( m_switchEventHandlerRef ) ;
2011-01-23 19:48:38 +00:00
# if defined(MAC_OS_X_VERSION_10_5)
2009-10-21 16:25:08 +00:00
CGDisplayRemoveReconfigurationCallback ( displayReconfigurationCallback , this ) ;
2011-01-23 19:48:38 +00:00
# else
DMRemoveExtendedNotifyProc ( m_displayManagerNotificationUPP ,
NULL , & m_PSN , 0 ) ;
if ( m_hiddenWindow ) {
ReleaseWindow ( m_hiddenWindow ) ;
m_hiddenWindow = NULL ;
}
if ( m_userInputWindow ) {
ReleaseWindow ( m_userInputWindow ) ;
m_userInputWindow = NULL ;
}
# endif
2009-02-27 11:54:59 +00:00
delete m_keyState ;
delete m_screensaver ;
}
void *
COSXScreen : : getEventTarget ( ) const
{
return const_cast < COSXScreen * > ( this ) ;
}
bool
COSXScreen : : getClipboard ( ClipboardID , IClipboard * dst ) const
{
2009-11-25 11:09:37 +00:00
CClipboard : : copy ( dst , & m_pasteboard ) ;
2009-02-27 11:54:59 +00:00
return true ;
}
void
COSXScreen : : getShape ( SInt32 & x , SInt32 & y , SInt32 & w , SInt32 & h ) const
{
x = m_x ;
y = m_y ;
w = m_w ;
h = m_h ;
}
void
COSXScreen : : getCursorPos ( SInt32 & x , SInt32 & y ) const
{
Point mouse ;
GetGlobalMouse ( & mouse ) ;
x = mouse . h ;
y = mouse . v ;
m_cursorPosValid = true ;
m_xCursor = x ;
m_yCursor = y ;
}
void
COSXScreen : : reconfigure ( UInt32 )
{
// do nothing
}
void
COSXScreen : : warpCursor ( SInt32 x , SInt32 y )
{
// move cursor without generating events
CGPoint pos ;
pos . x = x ;
pos . y = y ;
CGWarpMouseCursorPosition ( pos ) ;
2009-11-01 19:15:10 +00:00
2009-02-27 11:54:59 +00:00
// save new cursor position
m_xCursor = x ;
m_yCursor = y ;
m_cursorPosValid = true ;
}
void
COSXScreen : : fakeInputBegin ( )
{
// FIXME -- not implemented
}
void
COSXScreen : : fakeInputEnd ( )
{
// FIXME -- not implemented
}
SInt32
COSXScreen : : getJumpZoneSize ( ) const
{
return 1 ;
}
bool
COSXScreen : : isAnyMouseButtonDown ( ) const
{
return ( GetCurrentButtonState ( ) ! = 0 ) ;
}
void
COSXScreen : : getCursorCenter ( SInt32 & x , SInt32 & y ) const
{
x = m_xCenter ;
y = m_yCenter ;
}
UInt32
COSXScreen : : registerHotKey ( KeyID key , KeyModifierMask mask )
{
// get mac virtual key and modifier mask matching synergy key and mask
UInt32 macKey , macMask ;
if ( ! m_keyState - > mapSynergyHotKeyToMac ( key , mask , macKey , macMask ) ) {
LOG ( ( CLOG_WARN " could not map hotkey id=%04x mask=%04x " , key , mask ) ) ;
return 0 ;
}
// choose hotkey id
UInt32 id ;
if ( ! m_oldHotKeyIDs . empty ( ) ) {
id = m_oldHotKeyIDs . back ( ) ;
m_oldHotKeyIDs . pop_back ( ) ;
}
else {
id = m_hotKeys . size ( ) + 1 ;
}
// if this hot key has modifiers only then we'll handle it specially
EventHotKeyRef ref = NULL ;
bool okay ;
if ( key = = kKeyNone ) {
if ( m_modifierHotKeys . count ( mask ) > 0 ) {
// already registered
okay = false ;
}
else {
m_modifierHotKeys [ mask ] = id ;
okay = true ;
}
}
else {
EventHotKeyID hkid = { ' SNRG ' , ( UInt32 ) id } ;
OSStatus status = RegisterEventHotKey ( macKey , macMask , hkid ,
GetApplicationEventTarget ( ) , 0 ,
& ref ) ;
okay = ( status = = noErr ) ;
m_hotKeyToIDMap [ CHotKeyItem ( macKey , macMask ) ] = id ;
}
if ( ! okay ) {
m_oldHotKeyIDs . push_back ( id ) ;
m_hotKeyToIDMap . erase ( CHotKeyItem ( macKey , macMask ) ) ;
LOG ( ( CLOG_WARN " failed to register hotkey %s (id=%04x mask=%04x) " , CKeyMap : : formatKey ( key , mask ) . c_str ( ) , key , mask ) ) ;
return 0 ;
}
m_hotKeys . insert ( std : : make_pair ( id , CHotKeyItem ( ref , macKey , macMask ) ) ) ;
LOG ( ( CLOG_DEBUG " registered hotkey %s (id=%04x mask=%04x) as id=%d " , CKeyMap : : formatKey ( key , mask ) . c_str ( ) , key , mask , id ) ) ;
return id ;
}
void
COSXScreen : : unregisterHotKey ( UInt32 id )
{
// look up hotkey
HotKeyMap : : iterator i = m_hotKeys . find ( id ) ;
if ( i = = m_hotKeys . end ( ) ) {
return ;
}
// unregister with OS
bool okay ;
if ( i - > second . getRef ( ) ! = NULL ) {
okay = ( UnregisterEventHotKey ( i - > second . getRef ( ) ) = = noErr ) ;
}
else {
okay = false ;
// XXX -- this is inefficient
for ( ModifierHotKeyMap : : iterator j = m_modifierHotKeys . begin ( ) ;
j ! = m_modifierHotKeys . end ( ) ; + + j ) {
if ( j - > second = = id ) {
m_modifierHotKeys . erase ( j ) ;
okay = true ;
break ;
}
}
}
if ( ! okay ) {
LOG ( ( CLOG_WARN " failed to unregister hotkey id=%d " , id ) ) ;
}
else {
LOG ( ( CLOG_DEBUG " unregistered hotkey id=%d " , id ) ) ;
}
// discard hot key from map and record old id for reuse
m_hotKeyToIDMap . erase ( i - > second ) ;
m_hotKeys . erase ( i ) ;
m_oldHotKeyIDs . push_back ( id ) ;
if ( m_activeModifierHotKey = = id ) {
m_activeModifierHotKey = 0 ;
m_activeModifierHotKeyMask = 0 ;
}
}
2010-06-01 10:49:22 +00:00
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 ;
}
}
2009-02-27 11:54:59 +00:00
void
COSXScreen : : postMouseEvent ( CGPoint & pos ) const
{
// check if cursor position is valid on the client display configuration
// stkamp@users.sourceforge.net
CGDisplayCount displayCount = 0 ;
CGGetDisplaysWithPoint ( pos , 0 , NULL , & displayCount ) ;
if ( displayCount = = 0 ) {
// cursor position invalid - clamp to bounds of last valid display.
// find the last valid display using the last cursor position.
displayCount = 0 ;
CGDirectDisplayID displayID ;
CGGetDisplaysWithPoint ( CGPointMake ( m_xCursor , m_yCursor ) , 1 ,
& displayID , & displayCount ) ;
if ( displayCount ! = 0 ) {
CGRect displayRect = CGDisplayBounds ( displayID ) ;
if ( pos . x < displayRect . origin . x ) {
pos . x = displayRect . origin . x ;
}
else if ( pos . x > displayRect . origin . x +
displayRect . size . width - 1 ) {
pos . x = displayRect . origin . x + displayRect . size . width - 1 ;
}
if ( pos . y < displayRect . origin . y ) {
pos . y = displayRect . origin . y ;
}
else if ( pos . y > displayRect . origin . y +
displayRect . size . height - 1 ) {
pos . y = displayRect . origin . y + displayRect . size . height - 1 ;
}
}
}
2010-06-01 10:49:22 +00:00
CGEventType type = kCGEventMouseMoved ;
SInt8 button = m_buttonState . getFirstButtonDown ( ) ;
if ( button ! = - 1 ) {
MouseButtonEventMapType thisButtonType = MouseButtonEventMap [ button ] ;
type = thisButtonType [ kMouseButtonDragged ] ;
}
2009-02-27 11:54:59 +00:00
2010-06-01 10:49:22 +00:00
CGEventRef event = CGEventCreateMouseEvent ( NULL , type , pos , button ) ;
CGEventPost ( kCGHIDEventTap , event ) ;
CFRelease ( event ) ;
}
2009-02-27 11:54:59 +00:00
void
COSXScreen : : fakeMouseButton ( ButtonID id , bool press ) const
{
2010-06-01 10:49:22 +00:00
// Buttons are indexed from one, but the button down array is indexed from zero
2009-02-27 11:54:59 +00:00
UInt32 index = id - kButtonLeft ;
2010-06-01 10:49:22 +00:00
if ( index > = NumButtonIDs ) {
2009-02-27 11:54:59 +00:00
return ;
}
2010-06-01 10:49:22 +00:00
2009-02-27 11:54:59 +00:00
CGPoint pos ;
if ( ! m_cursorPosValid ) {
SInt32 x , y ;
getCursorPos ( x , y ) ;
}
pos . x = m_xCursor ;
pos . y = m_yCursor ;
2010-06-01 10:49:22 +00:00
// 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 ) ;
2009-02-27 11:54:59 +00:00
}
void
COSXScreen : : fakeMouseMove ( SInt32 x , SInt32 y ) const
{
// synthesize event
CGPoint pos ;
pos . x = x ;
pos . y = y ;
postMouseEvent ( pos ) ;
// save new cursor position
m_xCursor = static_cast < SInt32 > ( pos . x ) ;
m_yCursor = static_cast < SInt32 > ( pos . y ) ;
m_cursorPosValid = true ;
}
void
COSXScreen : : fakeMouseRelativeMove ( SInt32 dx , SInt32 dy ) const
{
// OS X does not appear to have a fake relative mouse move function.
// simulate it by getting the current mouse position and adding to
// that. this can yield the wrong answer but there's not much else
// we can do.
// get current position
Point oldPos ;
GetGlobalMouse ( & oldPos ) ;
// synthesize event
CGPoint pos ;
m_xCursor = static_cast < SInt32 > ( oldPos . h ) ;
m_yCursor = static_cast < SInt32 > ( oldPos . v ) ;
pos . x = oldPos . h + dx ;
pos . y = oldPos . v + dy ;
postMouseEvent ( pos ) ;
// we now assume we don't know the current cursor position
m_cursorPosValid = false ;
}
void
COSXScreen : : fakeMouseWheel ( SInt32 xDelta , SInt32 yDelta ) const
{
if ( xDelta ! = 0 | | yDelta ! = 0 ) {
2011-01-23 19:48:38 +00:00
# if defined(MAC_OS_X_VERSION_10_5)
2010-06-01 10:49:22 +00:00
// create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine
// is the right choice here over kCGScrollEventUnitPixel
2011-01-23 19:48:38 +00:00
CGEventRef scrollEvent = CGEventCreateScrollWheelEvent (
NULL , kCGScrollEventUnitLine , 2 ,
mapScrollWheelFromSynergy ( yDelta ) ,
- mapScrollWheelFromSynergy ( xDelta ) ) ;
2010-06-01 10:49:22 +00:00
CGEventPost ( kCGHIDEventTap , scrollEvent ) ;
CFRelease ( scrollEvent ) ;
2011-01-23 19:48:38 +00:00
# else
CGPostScrollWheelEvent (
2 , mapScrollWheelFromSynergy ( yDelta ) ,
- mapScrollWheelFromSynergy ( xDelta ) ) ;
# endif
2009-02-27 11:54:59 +00:00
}
}
2011-01-10 07:55:41 +00:00
void
COSXScreen : : showCursor ( )
{
CGDisplayShowCursor ( m_displayID ) ;
CFStringRef propertyString = CFStringCreateWithCString ( NULL , " SetsCursorInBackground " , kCFStringEncodingMacRoman ) ;
CGSSetConnectionProperty ( _CGSDefaultConnection ( ) , _CGSDefaultConnection ( ) , propertyString , kCFBooleanFalse ) ;
CFRelease ( propertyString ) ;
LOG ( ( CLOG_DEBUG " Trying to show cursor. " ) ) ;
}
void
COSXScreen : : hideCursor ( )
{
CFStringRef propertyString = CFStringCreateWithCString ( NULL , " SetsCursorInBackground " , kCFStringEncodingMacRoman ) ;
CGSSetConnectionProperty ( _CGSDefaultConnection ( ) , _CGSDefaultConnection ( ) , propertyString , kCFBooleanTrue ) ;
CFRelease ( propertyString ) ;
CGDisplayHideCursor ( m_displayID ) ;
LOG ( ( CLOG_DEBUG " Trying to hide cursor. " ) ) ;
}
2009-02-27 11:54:59 +00:00
void
COSXScreen : : enable ( )
{
// watch the clipboard
m_clipboardTimer = EVENTQUEUE - > newTimer ( 1.0 , NULL ) ;
EVENTQUEUE - > adoptHandler ( CEvent : : kTimer , m_clipboardTimer ,
new TMethodEventJob < COSXScreen > ( this ,
& COSXScreen : : handleClipboardCheck ) ) ;
if ( m_isPrimary ) {
// FIXME -- start watching jump zones
2009-11-01 19:15:10 +00:00
// kCGEventTapOptionDefault = 0x00000000 (Missing in 10.4, so specified literally)
2011-04-02 17:27:45 +00:00
m_eventTapPort = CGEventTapCreate ( kCGHIDEventTap , kCGHeadInsertEventTap , 0 ,
2009-11-01 19:15:10 +00:00
kCGEventMaskForAllEvents ,
handleCGInputEvent ,
this ) ;
2009-02-27 11:54:59 +00:00
}
else {
// FIXME -- prevent system from entering power save mode
// hide cursor
if ( ! m_cursorHidden ) {
2011-01-10 07:55:41 +00:00
hideCursor ( ) ;
2009-02-27 11:54:59 +00:00
m_cursorHidden = true ;
}
// warp the mouse to the cursor center
fakeMouseMove ( m_xCenter , m_yCenter ) ;
2011-01-10 07:55:41 +00:00
// there may be a better way to do this, but we register an event handler even if we're
// not on the primary display (acting as a client). This way, if a local event comes in
// (either keyboard or mouse), we can make sure to show the cursor if we've hidden it.
2011-04-02 17:27:45 +00:00
m_eventTapPort = CGEventTapCreate ( kCGHIDEventTap , kCGHeadInsertEventTap , 0 ,
2011-01-10 07:55:41 +00:00
kCGEventMaskForAllEvents ,
handleCGInputEventSecondary ,
this ) ;
2009-02-27 11:54:59 +00:00
}
2011-01-10 07:55:41 +00:00
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 ) ;
2009-02-27 11:54:59 +00:00
}
void
COSXScreen : : disable ( )
{
2011-01-10 07:55:41 +00:00
// show cursor if hidden
if ( m_cursorHidden ) {
showCursor ( ) ;
m_cursorHidden = false ;
2009-02-27 11:54:59 +00:00
}
2011-01-10 07:55:41 +00:00
// 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 ;
2009-02-27 11:54:59 +00:00
}
2011-01-10 07:55:41 +00:00
// FIXME -- allow system to enter power saving mode
2009-02-27 11:54:59 +00:00
// disable drag handling
m_dragNumButtonsDown = 0 ;
enableDragTimer ( false ) ;
// uninstall clipboard timer
if ( m_clipboardTimer ! = NULL ) {
EVENTQUEUE - > removeHandler ( CEvent : : kTimer , m_clipboardTimer ) ;
EVENTQUEUE - > deleteTimer ( m_clipboardTimer ) ;
m_clipboardTimer = NULL ;
}
m_isOnScreen = m_isPrimary ;
}
void
COSXScreen : : enter ( )
{
2011-01-10 07:55:41 +00:00
// show cursor
if ( m_cursorHidden ) {
showCursor ( ) ;
m_cursorHidden = false ;
}
2009-02-27 11:54:59 +00:00
if ( m_isPrimary ) {
CGSetLocalEventsSuppressionInterval ( 0.0 ) ;
2009-11-01 19:15:10 +00:00
2009-02-27 11:54:59 +00:00
// enable global hotkeys
2009-04-24 09:14:03 +00:00
//setGlobalHotKeysEnabled(true);
2009-02-27 11:54:59 +00:00
}
else {
// reset buttons
2010-06-01 10:49:22 +00:00
m_buttonState . reset ( ) ;
2009-02-27 11:54:59 +00:00
// avoid suppression of local hardware events
// stkamp@users.sourceforge.net
CGSetLocalEventsFilterDuringSupressionState (
kCGEventFilterMaskPermitAllEvents ,
kCGEventSupressionStateSupressionInterval ) ;
CGSetLocalEventsFilterDuringSupressionState (
( kCGEventFilterMaskPermitLocalKeyboardEvents |
kCGEventFilterMaskPermitSystemDefinedEvents ) ,
kCGEventSupressionStateRemoteMouseDrag ) ;
}
// now on screen
m_isOnScreen = true ;
}
bool
COSXScreen : : leave ( )
{
2011-01-10 07:55:41 +00:00
// hide cursor
if ( ! m_cursorHidden ) {
hideCursor ( ) ;
m_cursorHidden = true ;
}
2009-02-27 11:54:59 +00:00
if ( m_isPrimary ) {
// warp to center
warpCursor ( m_xCenter , m_yCenter ) ;
2009-11-01 19:15:10 +00:00
// This used to be necessary to get smooth mouse motion on other screens,
// but now is just to avoid a hesitating cursor when transitioning to
// the primary (this) screen.
2009-02-27 11:54:59 +00:00
CGSetLocalEventsSuppressionInterval ( 0.0001 ) ;
2009-11-01 19:15:10 +00:00
2009-02-27 11:54:59 +00:00
// disable global hotkeys
2009-04-24 09:14:03 +00:00
//setGlobalHotKeysEnabled(false);
2009-02-27 11:54:59 +00:00
}
else {
// warp the mouse to the cursor center
fakeMouseMove ( m_xCenter , m_yCenter ) ;
// FIXME -- prepare to show cursor if it moves
// take keyboard focus
// FIXME
}
// now off screen
m_isOnScreen = false ;
return true ;
}
bool
COSXScreen : : setClipboard ( ClipboardID , const IClipboard * src )
{
2010-06-01 10:49:22 +00:00
if ( src ! = NULL ) {
LOG ( ( CLOG_DEBUG " setting clipboard " ) ) ;
CClipboard : : copy ( & m_pasteboard , src ) ;
}
return true ;
2009-02-27 11:54:59 +00:00
}
void
COSXScreen : : checkClipboards ( )
{
2010-06-01 10:49:22 +00:00
LOG ( ( CLOG_DEBUG1 " checking clipboard " ) ) ;
if ( m_pasteboard . synchronize ( ) ) {
LOG ( ( CLOG_DEBUG " clipboard changed " ) ) ;
sendClipboardEvent ( getClipboardGrabbedEvent ( ) , kClipboardClipboard ) ;
sendClipboardEvent ( getClipboardGrabbedEvent ( ) , kClipboardSelection ) ;
}
2009-02-27 11:54:59 +00:00
}
void
COSXScreen : : openScreensaver ( bool notify )
{
m_screensaverNotify = notify ;
if ( ! m_screensaverNotify ) {
m_screensaver - > disable ( ) ;
}
}
void
COSXScreen : : closeScreensaver ( )
{
if ( ! m_screensaverNotify ) {
m_screensaver - > enable ( ) ;
}
}
void
COSXScreen : : screensaver ( bool activate )
{
if ( activate ) {
m_screensaver - > activate ( ) ;
}
else {
m_screensaver - > deactivate ( ) ;
}
}
void
COSXScreen : : resetOptions ( )
{
// no options
}
void
COSXScreen : : setOptions ( const COptionsList & )
{
// no options
}
void
COSXScreen : : setSequenceNumber ( UInt32 seqNum )
{
m_sequenceNumber = seqNum ;
}
bool
COSXScreen : : isPrimary ( ) const
{
return m_isPrimary ;
}
void
COSXScreen : : sendEvent ( CEvent : : Type type , void * data ) const
{
EVENTQUEUE - > addEvent ( CEvent ( type , getEventTarget ( ) , data ) ) ;
}
void
COSXScreen : : sendClipboardEvent ( CEvent : : Type type , ClipboardID id ) const
{
CClipboardInfo * info = ( CClipboardInfo * ) malloc ( sizeof ( CClipboardInfo ) ) ;
info - > m_id = id ;
info - > m_sequenceNumber = m_sequenceNumber ;
sendEvent ( type , info ) ;
}
void
COSXScreen : : handleSystemEvent ( const CEvent & event , void * )
{
EventRef * carbonEvent = reinterpret_cast < EventRef * > ( event . getData ( ) ) ;
assert ( carbonEvent ! = NULL ) ;
UInt32 eventClass = GetEventClass ( * carbonEvent ) ;
switch ( eventClass ) {
case kEventClassMouse :
switch ( GetEventKind ( * carbonEvent ) ) {
case kSynergyEventMouseScroll :
{
OSStatus r ;
long xScroll ;
long yScroll ;
// get scroll amount
r = GetEventParameter ( * carbonEvent ,
kSynergyMouseScrollAxisX ,
typeLongInteger ,
NULL ,
sizeof ( xScroll ) ,
NULL ,
& xScroll ) ;
if ( r ! = noErr ) {
xScroll = 0 ;
}
r = GetEventParameter ( * carbonEvent ,
kSynergyMouseScrollAxisY ,
typeLongInteger ,
NULL ,
sizeof ( yScroll ) ,
NULL ,
& yScroll ) ;
if ( r ! = noErr ) {
yScroll = 0 ;
}
if ( xScroll ! = 0 | | yScroll ! = 0 ) {
onMouseWheel ( - mapScrollWheelToSynergy ( xScroll ) ,
mapScrollWheelToSynergy ( yScroll ) ) ;
}
}
}
break ;
case kEventClassKeyboard :
2009-11-01 19:15:10 +00:00
switch ( GetEventKind ( * carbonEvent ) ) {
case kEventHotKeyPressed :
case kEventHotKeyReleased :
onHotKey ( * carbonEvent ) ;
break ;
}
2009-02-27 11:54:59 +00:00
break ;
2009-11-01 19:15:10 +00:00
2009-02-27 11:54:59 +00:00
case kEventClassWindow :
SendEventToWindow ( * carbonEvent , m_userInputWindow ) ;
switch ( GetEventKind ( * carbonEvent ) ) {
case kEventWindowActivated :
LOG ( ( CLOG_DEBUG1 " window activated " ) ) ;
break ;
case kEventWindowDeactivated :
LOG ( ( CLOG_DEBUG1 " window deactivated " ) ) ;
break ;
case kEventWindowFocusAcquired :
LOG ( ( CLOG_DEBUG1 " focus acquired " ) ) ;
break ;
case kEventWindowFocusRelinquish :
LOG ( ( CLOG_DEBUG1 " focus released " ) ) ;
break ;
}
break ;
default :
SendEventToEventTarget ( * carbonEvent , GetEventDispatcherTarget ( ) ) ;
break ;
}
}
bool
COSXScreen : : onMouseMove ( SInt32 mx , SInt32 my )
{
LOG ( ( CLOG_DEBUG2 " mouse move %+d,%+d " , mx , my ) ) ;
SInt32 x = mx - m_xCursor ;
SInt32 y = my - m_yCursor ;
if ( ( x = = 0 & & y = = 0 ) | | ( mx = = m_xCenter & & mx = = m_yCenter ) ) {
return true ;
}
// save position to compute delta of next motion
m_xCursor = mx ;
m_yCursor = my ;
if ( m_isOnScreen ) {
// motion on primary screen
sendEvent ( getMotionOnPrimaryEvent ( ) ,
CMotionInfo : : alloc ( m_xCursor , m_yCursor ) ) ;
}
else {
// motion on secondary screen. warp mouse back to
// center.
warpCursor ( m_xCenter , m_yCenter ) ;
// examine the motion. if it's about the distance
// from the center of the screen to an edge then
// it's probably a bogus motion that we want to
// ignore (see warpCursorNoFlush() for a further
// description).
static SInt32 bogusZoneSize = 10 ;
if ( - x + bogusZoneSize > m_xCenter - m_x | |
x + bogusZoneSize > m_x + m_w - m_xCenter | |
- y + bogusZoneSize > m_yCenter - m_y | |
y + bogusZoneSize > m_y + m_h - m_yCenter ) {
LOG ( ( CLOG_DEBUG " dropped bogus motion %+d,%+d " , x , y ) ) ;
}
else {
// send motion
sendEvent ( getMotionOnSecondaryEvent ( ) , CMotionInfo : : alloc ( x , y ) ) ;
}
}
return true ;
}
bool
COSXScreen : : onMouseButton ( bool pressed , UInt16 macButton )
{
// Buttons 2 and 3 are inverted on the mac
ButtonID button = mapMacButtonToSynergy ( macButton ) ;
if ( pressed ) {
LOG ( ( CLOG_DEBUG1 " event: button press button=%d " , button ) ) ;
if ( button ! = kButtonNone ) {
KeyModifierMask mask = m_keyState - > getActiveModifiers ( ) ;
sendEvent ( getButtonDownEvent ( ) , CButtonInfo : : alloc ( button , mask ) ) ;
}
}
else {
LOG ( ( CLOG_DEBUG1 " event: button release button=%d " , button ) ) ;
if ( button ! = kButtonNone ) {
KeyModifierMask mask = m_keyState - > getActiveModifiers ( ) ;
sendEvent ( getButtonUpEvent ( ) , CButtonInfo : : alloc ( button , mask ) ) ;
}
}
// handle drags with any button other than button 1 or 2
if ( macButton > 2 ) {
if ( pressed ) {
// one more button
if ( m_dragNumButtonsDown + + = = 0 ) {
enableDragTimer ( true ) ;
}
}
else {
// one less button
if ( - - m_dragNumButtonsDown = = 0 ) {
enableDragTimer ( false ) ;
}
}
}
return true ;
}
bool
COSXScreen : : onMouseWheel ( SInt32 xDelta , SInt32 yDelta ) const
{
LOG ( ( CLOG_DEBUG1 " event: button wheel delta=%+d,%+d " , xDelta , yDelta ) ) ;
sendEvent ( getWheelEvent ( ) , CWheelInfo : : alloc ( xDelta , yDelta ) ) ;
return true ;
}
void
COSXScreen : : handleClipboardCheck ( const CEvent & , void * )
{
checkClipboards ( ) ;
}
2011-01-23 19:48:38 +00:00
# if !defined(MAC_OS_X_VERSION_10_5)
pascal void
COSXScreen : : displayManagerCallback ( void * inUserData , SInt16 inMessage , void * )
{
COSXScreen * screen = ( COSXScreen * ) inUserData ;
if ( inMessage = = kDMNotifyEvent ) {
screen - > onDisplayChange ( ) ;
}
}
bool
COSXScreen : : onDisplayChange ( )
{
// screen resolution may have changed. save old shape.
SInt32 xOld = m_x , yOld = m_y , wOld = m_w , hOld = m_h ;
// update shape
updateScreenShape ( ) ;
// do nothing if resolution hasn't changed
if ( xOld ! = m_x | | yOld ! = m_y | | wOld ! = m_w | | hOld ! = m_h ) {
if ( m_isPrimary ) {
// warp mouse to center if off screen
if ( ! m_isOnScreen ) {
warpCursor ( m_xCenter , m_yCenter ) ;
}
}
// send new screen info
sendEvent ( getShapeChangedEvent ( ) ) ;
}
return true ;
}
# else
2009-10-21 16:25:08 +00:00
void
2009-04-24 09:14:03 +00:00
COSXScreen : : displayReconfigurationCallback ( CGDirectDisplayID displayID , CGDisplayChangeSummaryFlags flags , void * inUserData )
2009-02-27 11:54:59 +00:00
{
COSXScreen * screen = ( COSXScreen * ) inUserData ;
2009-10-21 16:25:08 +00:00
CGDisplayChangeSummaryFlags mask = kCGDisplayMovedFlag |
kCGDisplaySetModeFlag | kCGDisplayAddFlag | kCGDisplayRemoveFlag |
kCGDisplayEnabledFlag | kCGDisplayDisabledFlag |
kCGDisplayMirrorFlag | kCGDisplayUnMirrorFlag |
kCGDisplayDesktopShapeChangedFlag ;
LOG ( ( CLOG_DEBUG1 " event: display was reconfigured: %x %x %x " , flags , mask , flags & mask ) ) ;
2009-02-27 11:54:59 +00:00
2009-10-21 16:25:08 +00:00
if ( flags & mask ) { /* Something actually did change */
2010-06-01 10:49:22 +00:00
2009-10-21 16:25:08 +00:00
LOG ( ( CLOG_DEBUG1 " event: screen changed shape; refreshing dimensions " ) ) ;
screen - > updateScreenShape ( displayID , flags ) ;
2009-02-27 11:54:59 +00:00
}
}
2011-01-23 19:48:38 +00:00
# endif
2009-02-27 11:54:59 +00:00
bool
2009-11-01 19:15:10 +00:00
COSXScreen : : onKey ( CGEventRef event )
2009-02-27 11:54:59 +00:00
{
2009-11-01 19:15:10 +00:00
CGEventType eventKind = CGEventGetType ( event ) ;
2009-02-27 11:54:59 +00:00
// get the key and active modifiers
2009-11-01 19:15:10 +00:00
UInt32 virtualKey = CGEventGetIntegerValueField ( event , kCGKeyboardEventKeycode ) ;
CGEventFlags macMask = CGEventGetFlags ( event ) ;
2009-02-27 11:54:59 +00:00
LOG ( ( CLOG_DEBUG1 " event: Key event kind: %d, keycode=%d " , eventKind , virtualKey ) ) ;
2009-11-01 19:15:10 +00:00
// Special handling to track state of modifiers
if ( eventKind = = kCGEventFlagsChanged ) {
2009-02-27 11:54:59 +00:00
// get old and new modifier state
KeyModifierMask oldMask = getActiveModifiers ( ) ;
KeyModifierMask newMask = m_keyState - > mapModifiersFromOSX ( macMask ) ;
m_keyState - > handleModifierKeys ( getEventTarget ( ) , oldMask , newMask ) ;
// if the current set of modifiers exactly matches a modifiers-only
// hot key then generate a hot key down event.
if ( m_activeModifierHotKey = = 0 ) {
if ( m_modifierHotKeys . count ( newMask ) > 0 ) {
m_activeModifierHotKey = m_modifierHotKeys [ newMask ] ;
m_activeModifierHotKeyMask = newMask ;
EVENTQUEUE - > addEvent ( CEvent ( getHotKeyDownEvent ( ) ,
getEventTarget ( ) ,
CHotKeyInfo : : alloc ( m_activeModifierHotKey ) ) ) ;
}
}
// if a modifiers-only hot key is active and should no longer be
// then generate a hot key up event.
else if ( m_activeModifierHotKey ! = 0 ) {
KeyModifierMask mask = ( newMask & m_activeModifierHotKeyMask ) ;
if ( mask ! = m_activeModifierHotKeyMask ) {
EVENTQUEUE - > addEvent ( CEvent ( getHotKeyUpEvent ( ) ,
getEventTarget ( ) ,
CHotKeyInfo : : alloc ( m_activeModifierHotKey ) ) ) ;
m_activeModifierHotKey = 0 ;
m_activeModifierHotKeyMask = 0 ;
}
}
return true ;
}
// check for hot key. when we're on a secondary screen we disable
// all hotkeys so we can capture the OS defined hot keys as regular
// keystrokes but that means we don't get our own hot keys either.
// so we check for a key/modifier match in our hot key map.
if ( ! m_isOnScreen ) {
HotKeyToIDMap : : const_iterator i =
2009-11-01 19:15:10 +00:00
m_hotKeyToIDMap . find ( CHotKeyItem ( virtualKey ,
m_keyState - > mapModifiersToCarbon ( macMask )
& 0xff00u ) ) ;
2009-02-27 11:54:59 +00:00
if ( i ! = m_hotKeyToIDMap . end ( ) ) {
UInt32 id = i - > second ;
// determine event type
CEvent : : Type type ;
2009-11-01 19:15:10 +00:00
//UInt32 eventKind = GetEventKind(event);
if ( eventKind = = kCGEventKeyDown ) {
2009-02-27 11:54:59 +00:00
type = getHotKeyDownEvent ( ) ;
}
2009-11-01 19:15:10 +00:00
else if ( eventKind = = kCGEventKeyUp ) {
2009-02-27 11:54:59 +00:00
type = getHotKeyUpEvent ( ) ;
}
else {
return false ;
}
EVENTQUEUE - > addEvent ( CEvent ( type , getEventTarget ( ) ,
CHotKeyInfo : : alloc ( id ) ) ) ;
return true ;
}
}
// decode event type
2009-11-01 19:15:10 +00:00
bool down = ( eventKind = = kCGEventKeyDown ) ;
bool up = ( eventKind = = kCGEventKeyUp ) ;
bool isRepeat = ( CGEventGetIntegerValueField ( event , kCGKeyboardEventAutorepeat ) = = 1 ) ;
2009-02-27 11:54:59 +00:00
// map event to keys
KeyModifierMask mask ;
COSXKeyState : : CKeyIDs keys ;
KeyButton button = m_keyState - > mapKeyFromEvent ( keys , & mask , event ) ;
if ( button = = 0 ) {
return false ;
}
// check for AltGr in mask. if set we send neither the AltGr nor
// the super modifiers to clients then remove AltGr before passing
// the modifiers to onKey.
KeyModifierMask sendMask = ( mask & ~ KeyModifierAltGr ) ;
if ( ( mask & KeyModifierAltGr ) ! = 0 ) {
sendMask & = ~ KeyModifierSuper ;
}
mask & = ~ KeyModifierAltGr ;
// update button state
if ( down ) {
m_keyState - > onKey ( button , true , mask ) ;
}
else if ( up ) {
if ( ! m_keyState - > isKeyDown ( button ) ) {
// up event for a dead key. throw it away.
return false ;
}
m_keyState - > onKey ( button , false , mask ) ;
}
// send key events
for ( COSXKeyState : : CKeyIDs : : const_iterator i = keys . begin ( ) ;
i ! = keys . end ( ) ; + + i ) {
m_keyState - > sendKeyEvent ( getEventTarget ( ) , down , isRepeat ,
* i , sendMask , 1 , button ) ;
}
return true ;
}
bool
COSXScreen : : onHotKey ( EventRef event ) const
{
// get the hotkey id
EventHotKeyID hkid ;
GetEventParameter ( event , kEventParamDirectObject , typeEventHotKeyID ,
NULL , sizeof ( EventHotKeyID ) , NULL , & hkid ) ;
UInt32 id = hkid . id ;
// determine event type
CEvent : : Type type ;
UInt32 eventKind = GetEventKind ( event ) ;
if ( eventKind = = kEventHotKeyPressed ) {
type = getHotKeyDownEvent ( ) ;
}
else if ( eventKind = = kEventHotKeyReleased ) {
type = getHotKeyUpEvent ( ) ;
}
else {
return false ;
}
EVENTQUEUE - > addEvent ( CEvent ( type , getEventTarget ( ) ,
CHotKeyInfo : : alloc ( id ) ) ) ;
return true ;
}
ButtonID
COSXScreen : : mapMacButtonToSynergy ( UInt16 macButton ) const
{
switch ( macButton ) {
case 1 :
return kButtonLeft ;
case 2 :
return kButtonRight ;
case 3 :
return kButtonMiddle ;
}
return static_cast < ButtonID > ( macButton ) ;
}
SInt32
COSXScreen : : mapScrollWheelToSynergy ( SInt32 x ) const
{
// return accelerated scrolling but not exponentially scaled as it is
// on the mac.
double d = ( 1.0 + getScrollSpeed ( ) ) * x / getScrollSpeedFactor ( ) ;
return static_cast < SInt32 > ( 120.0 * d ) ;
}
SInt32
COSXScreen : : mapScrollWheelFromSynergy ( SInt32 x ) const
{
// use server's acceleration with a little boost since other platforms
// take one wheel step as a larger step than the mac does.
return static_cast < SInt32 > ( 3.0 * x / 120.0 ) ;
}
double
COSXScreen : : getScrollSpeed ( ) const
{
double scaling = 0.0 ;
CFPropertyListRef pref = : : CFPreferencesCopyValue (
CFSTR ( " com.apple.scrollwheel.scaling " ) ,
kCFPreferencesAnyApplication ,
kCFPreferencesCurrentUser ,
kCFPreferencesAnyHost ) ;
if ( pref ! = NULL ) {
CFTypeID id = CFGetTypeID ( pref ) ;
if ( id = = CFNumberGetTypeID ( ) ) {
CFNumberRef value = static_cast < CFNumberRef > ( pref ) ;
if ( CFNumberGetValue ( value , kCFNumberDoubleType , & scaling ) ) {
if ( scaling < 0.0 ) {
scaling = 0.0 ;
}
}
}
CFRelease ( pref ) ;
}
return scaling ;
}
double
COSXScreen : : getScrollSpeedFactor ( ) const
{
return pow ( 10.0 , getScrollSpeed ( ) ) ;
}
void
COSXScreen : : enableDragTimer ( bool enable )
{
2009-04-24 09:14:03 +00:00
UInt32 modifiers ;
MouseTrackingResult res ;
2009-02-27 11:54:59 +00:00
if ( enable & & m_dragTimer = = NULL ) {
m_dragTimer = EVENTQUEUE - > newTimer ( 0.01 , NULL ) ;
EVENTQUEUE - > adoptHandler ( CEvent : : kTimer , m_dragTimer ,
new TMethodEventJob < COSXScreen > ( this ,
& COSXScreen : : handleDrag ) ) ;
2010-06-01 10:49:22 +00:00
TrackMouseLocationWithOptions ( NULL , 0 , 0 , & m_dragLastPoint , & modifiers , & res ) ;
2009-02-27 11:54:59 +00:00
}
else if ( ! enable & & m_dragTimer ! = NULL ) {
EVENTQUEUE - > removeHandler ( CEvent : : kTimer , m_dragTimer ) ;
EVENTQUEUE - > deleteTimer ( m_dragTimer ) ;
m_dragTimer = NULL ;
}
}
void
COSXScreen : : handleDrag ( const CEvent & , void * )
{
Point p ;
2009-04-24 09:14:03 +00:00
UInt32 modifiers ;
MouseTrackingResult res ;
TrackMouseLocationWithOptions ( NULL , 0 , 0 , & p , & modifiers , & res ) ;
if ( res ! = kMouseTrackingTimedOut & & ( p . h ! = m_dragLastPoint . h | | p . v ! = m_dragLastPoint . v ) ) {
2009-02-27 11:54:59 +00:00
m_dragLastPoint = p ;
onMouseMove ( ( SInt32 ) p . h , ( SInt32 ) p . v ) ;
}
}
void
COSXScreen : : updateButtons ( )
{
UInt32 buttons = GetCurrentButtonState ( ) ;
2010-06-01 10:49:22 +00:00
m_buttonState . overwrite ( buttons ) ;
2009-02-27 11:54:59 +00:00
}
IKeyState *
COSXScreen : : getKeyState ( ) const
{
return m_keyState ;
}
void
2009-04-24 09:14:03 +00:00
COSXScreen : : updateScreenShape ( const CGDirectDisplayID , const CGDisplayChangeSummaryFlags flags )
2009-02-27 11:54:59 +00:00
{
2011-01-23 19:48:38 +00:00
updateScreenShape ( ) ;
}
void
COSXScreen : : updateScreenShape ( )
{
2009-02-27 11:54:59 +00:00
// get info for each display
CGDisplayCount displayCount = 0 ;
if ( CGGetActiveDisplayList ( 0 , NULL , & displayCount ) ! = CGDisplayNoErr ) {
return ;
}
if ( displayCount = = 0 ) {
return ;
}
CGDirectDisplayID * displays = new CGDirectDisplayID [ displayCount ] ;
if ( displays = = NULL ) {
return ;
}
if ( CGGetActiveDisplayList ( displayCount ,
displays , & displayCount ) ! = CGDisplayNoErr ) {
delete [ ] displays ;
return ;
}
// get smallest rect enclosing all display rects
CGRect totalBounds = CGRectZero ;
for ( CGDisplayCount i = 0 ; i < displayCount ; + + i ) {
CGRect bounds = CGDisplayBounds ( displays [ i ] ) ;
totalBounds = CGRectUnion ( totalBounds , bounds ) ;
}
// get shape of default screen
m_x = ( SInt32 ) totalBounds . origin . x ;
m_y = ( SInt32 ) totalBounds . origin . y ;
m_w = ( SInt32 ) totalBounds . size . width ;
m_h = ( SInt32 ) totalBounds . size . height ;
// get center of default screen
2009-04-24 09:14:03 +00:00
CGDirectDisplayID main = CGMainDisplayID ( ) ;
const CGRect rect = CGDisplayBounds ( main ) ;
m_xCenter = ( rect . origin . x + rect . size . width ) / 2 ;
m_yCenter = ( rect . origin . y + rect . size . height ) / 2 ;
2009-02-27 11:54:59 +00:00
delete [ ] displays ;
2010-06-01 10:49:22 +00:00
if ( m_isPrimary & & ! m_isOnScreen ) {
sendEvent ( getShapeChangedEvent ( ) ) ;
}
2009-02-27 11:54:59 +00:00
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 " ) ) ;
}
# pragma mark -
//
// FAST USER SWITCH NOTIFICATION SUPPORT
//
// COSXScreen::userSwitchCallback(void*)
//
// gets called if a fast user switch occurs
//
pascal OSStatus
COSXScreen : : userSwitchCallback ( EventHandlerCallRef nextHandler ,
EventRef theEvent ,
void * inUserData )
{
COSXScreen * screen = ( COSXScreen * ) inUserData ;
UInt32 kind = GetEventKind ( theEvent ) ;
if ( kind = = kEventSystemUserSessionDeactivated ) {
LOG ( ( CLOG_DEBUG " user session deactivated " ) ) ;
EVENTQUEUE - > addEvent ( CEvent ( IScreen : : getSuspendEvent ( ) ,
screen - > getEventTarget ( ) ) ) ;
}
else if ( kind = = kEventSystemUserSessionActivated ) {
LOG ( ( CLOG_DEBUG " user session activated " ) ) ;
EVENTQUEUE - > addEvent ( CEvent ( IScreen : : getResumeEvent ( ) ,
screen - > getEventTarget ( ) ) ) ;
}
return ( CallNextEventHandler ( nextHandler , theEvent ) ) ;
}
# pragma mark -
//
// SLEEP/WAKEUP NOTIFICATION SUPPORT
//
// COSXScreen::watchSystemPowerThread(void*)
//
// main of thread monitoring system power (sleep/wakup) using a CFRunLoop
//
void
COSXScreen : : watchSystemPowerThread ( void * )
{
io_object_t notifier ;
IONotificationPortRef notificationPortRef ;
CFRunLoopSourceRef runloopSourceRef = 0 ;
m_pmRunloop = CFRunLoopGetCurrent ( ) ;
// install system power change callback
m_pmRootPort = IORegisterForSystemPower ( this , & notificationPortRef ,
powerChangeCallback , & notifier ) ;
if ( m_pmRootPort = = 0 ) {
LOG ( ( CLOG_WARN " IORegisterForSystemPower failed " ) ) ;
}
else {
runloopSourceRef =
IONotificationPortGetRunLoopSource ( notificationPortRef ) ;
CFRunLoopAddSource ( m_pmRunloop , runloopSourceRef ,
kCFRunLoopCommonModes ) ;
}
// thread is ready
{
CLock lock ( m_pmMutex ) ;
* m_pmThreadReady = true ;
m_pmThreadReady - > signal ( ) ;
}
// if we were unable to initialize then exit. we must do this after
// setting m_pmThreadReady to true otherwise the parent thread will
// block waiting for it.
if ( m_pmRootPort = = 0 ) {
return ;
}
// start the run loop
LOG ( ( CLOG_DEBUG " started watchSystemPowerThread " ) ) ;
CFRunLoopRun ( ) ;
// cleanup
if ( notificationPortRef ) {
CFRunLoopRemoveSource ( m_pmRunloop ,
runloopSourceRef , kCFRunLoopDefaultMode ) ;
CFRunLoopSourceInvalidate ( runloopSourceRef ) ;
CFRelease ( runloopSourceRef ) ;
}
CLock lock ( m_pmMutex ) ;
IODeregisterForSystemPower ( & notifier ) ;
m_pmRootPort = 0 ;
LOG ( ( CLOG_DEBUG " stopped watchSystemPowerThread " ) ) ;
}
void
COSXScreen : : powerChangeCallback ( void * refcon , io_service_t service ,
natural_t messageType , void * messageArg )
{
( ( COSXScreen * ) refcon ) - > handlePowerChangeRequest ( messageType , messageArg ) ;
}
void
COSXScreen : : handlePowerChangeRequest ( natural_t messageType , void * messageArg )
{
// we've received a power change notification
switch ( messageType ) {
case kIOMessageSystemWillSleep :
// COSXScreen has to handle this in the main thread so we have to
// queue a confirm sleep event here. we actually don't allow the
// system to sleep until the event is handled.
EVENTQUEUE - > addEvent ( CEvent ( COSXScreen : : getConfirmSleepEvent ( ) ,
getEventTarget ( ) , messageArg ,
CEvent : : kDontFreeData ) ) ;
return ;
case kIOMessageSystemHasPoweredOn :
LOG ( ( CLOG_DEBUG " system wakeup " ) ) ;
EVENTQUEUE - > addEvent ( CEvent ( IScreen : : getResumeEvent ( ) ,
getEventTarget ( ) ) ) ;
break ;
default :
break ;
}
CLock lock ( m_pmMutex ) ;
if ( m_pmRootPort ! = 0 ) {
IOAllowPowerChange ( m_pmRootPort , ( long ) messageArg ) ;
}
}
CEvent : : Type
COSXScreen : : getConfirmSleepEvent ( )
{
return CEvent : : registerTypeOnce ( s_confirmSleepEvent ,
" COSXScreen::confirmSleep " ) ;
}
void
COSXScreen : : handleConfirmSleep ( const CEvent & event , void * )
{
long messageArg = ( long ) event . getData ( ) ;
if ( messageArg ! = 0 ) {
CLock lock ( m_pmMutex ) ;
if ( m_pmRootPort ! = 0 ) {
// deliver suspend event immediately.
EVENTQUEUE - > addEvent ( CEvent ( IScreen : : getSuspendEvent ( ) ,
getEventTarget ( ) , NULL ,
CEvent : : kDeliverImmediately ) ) ;
LOG ( ( CLOG_DEBUG " system will sleep " ) ) ;
IOAllowPowerChange ( m_pmRootPort , messageArg ) ;
}
}
}
# pragma mark -
//
// GLOBAL HOTKEY OPERATING MODE SUPPORT (10.3)
//
// CoreGraphics private API (OSX 10.3)
// Source: http://ichiro.nnip.org/osx/Cocoa/GlobalHotkey.html
//
// We load the functions dynamically because they're not available in
// older SDKs. We don't use weak linking because we want users of
// older SDKs to build an app that works on newer systems and older
// SDKs will not provide the symbols.
//
2009-04-24 09:14:03 +00:00
// FIXME: This is hosed as of OS 10.5; patches to repair this are
// a good thing.
//
#if 0
2009-02-27 11:54:59 +00:00
# ifdef __cplusplus
extern " C " {
# endif
typedef int CGSConnection ;
typedef enum {
CGSGlobalHotKeyEnable = 0 ,
CGSGlobalHotKeyDisable = 1 ,
} CGSGlobalHotKeyOperatingMode ;
extern CGSConnection _CGSDefaultConnection ( void ) WEAK_IMPORT_ATTRIBUTE ;
extern CGError CGSGetGlobalHotKeyOperatingMode ( CGSConnection connection , CGSGlobalHotKeyOperatingMode * mode ) WEAK_IMPORT_ATTRIBUTE ;
extern CGError CGSSetGlobalHotKeyOperatingMode ( CGSConnection connection , CGSGlobalHotKeyOperatingMode mode ) WEAK_IMPORT_ATTRIBUTE ;
typedef CGSConnection ( * _CGSDefaultConnection_t ) ( void ) ;
typedef CGError ( * CGSGetGlobalHotKeyOperatingMode_t ) ( CGSConnection connection , CGSGlobalHotKeyOperatingMode * mode ) ;
typedef CGError ( * CGSSetGlobalHotKeyOperatingMode_t ) ( CGSConnection connection , CGSGlobalHotKeyOperatingMode mode ) ;
static _CGSDefaultConnection_t s__CGSDefaultConnection ;
static CGSGetGlobalHotKeyOperatingMode_t s_CGSGetGlobalHotKeyOperatingMode ;
static CGSSetGlobalHotKeyOperatingMode_t s_CGSSetGlobalHotKeyOperatingMode ;
# ifdef __cplusplus
}
# endif
# define LOOKUP(name_) \
s_ # # name_ = NULL ; \
if ( NSIsSymbolNameDefinedWithHint ( " _ " # name_ , " CoreGraphics " ) ) { \
s_ # # name_ = ( name_ # # _t ) NSAddressOfSymbol ( \
NSLookupAndBindSymbolWithHint ( \
" _ " # name_ , " CoreGraphics " ) ) ; \
}
bool
COSXScreen : : isGlobalHotKeyOperatingModeAvailable ( )
{
if ( ! s_testedForGHOM ) {
s_testedForGHOM = true ;
LOOKUP ( _CGSDefaultConnection ) ;
LOOKUP ( CGSGetGlobalHotKeyOperatingMode ) ;
LOOKUP ( CGSSetGlobalHotKeyOperatingMode ) ;
s_hasGHOM = ( s__CGSDefaultConnection ! = NULL & &
s_CGSGetGlobalHotKeyOperatingMode ! = NULL & &
s_CGSSetGlobalHotKeyOperatingMode ! = NULL ) ;
}
return s_hasGHOM ;
}
void
COSXScreen : : setGlobalHotKeysEnabled ( bool enabled )
{
if ( isGlobalHotKeyOperatingModeAvailable ( ) ) {
CGSConnection conn = s__CGSDefaultConnection ( ) ;
CGSGlobalHotKeyOperatingMode mode ;
s_CGSGetGlobalHotKeyOperatingMode ( conn , & mode ) ;
if ( enabled & & mode = = CGSGlobalHotKeyDisable ) {
s_CGSSetGlobalHotKeyOperatingMode ( conn , CGSGlobalHotKeyEnable ) ;
}
else if ( ! enabled & & mode = = CGSGlobalHotKeyEnable ) {
s_CGSSetGlobalHotKeyOperatingMode ( conn , CGSGlobalHotKeyDisable ) ;
}
}
}
bool
COSXScreen : : getGlobalHotKeysEnabled ( )
{
CGSGlobalHotKeyOperatingMode mode ;
if ( isGlobalHotKeyOperatingModeAvailable ( ) ) {
CGSConnection conn = s__CGSDefaultConnection ( ) ;
s_CGSGetGlobalHotKeyOperatingMode ( conn , & mode ) ;
}
else {
mode = CGSGlobalHotKeyEnable ;
}
return ( mode = = CGSGlobalHotKeyEnable ) ;
}
2009-04-24 09:14:03 +00:00
# endif
2009-02-27 11:54:59 +00:00
//
// COSXScreen::CHotKeyItem
//
COSXScreen : : CHotKeyItem : : CHotKeyItem ( UInt32 keycode , UInt32 mask ) :
m_ref ( NULL ) ,
m_keycode ( keycode ) ,
m_mask ( mask )
{
// do nothing
}
COSXScreen : : CHotKeyItem : : CHotKeyItem ( EventHotKeyRef ref ,
UInt32 keycode , UInt32 mask ) :
m_ref ( ref ) ,
m_keycode ( keycode ) ,
m_mask ( mask )
{
// do nothing
}
EventHotKeyRef
COSXScreen : : CHotKeyItem : : getRef ( ) const
{
return m_ref ;
}
bool
COSXScreen : : CHotKeyItem : : operator < ( const CHotKeyItem & x ) const
{
return ( m_keycode < x . m_keycode | |
( m_keycode = = x . m_keycode & & m_mask < x . m_mask ) ) ;
}
2009-11-01 19:15:10 +00:00
2011-01-10 07:55:41 +00:00
// Quartz event tap support for the secondary display. This make sure that we
// will show the cursor if a local event comes in while synergy has the cursor off the screen.
CGEventRef
COSXScreen : : handleCGInputEventSecondary ( CGEventTapProxy proxy ,
CGEventType type ,
CGEventRef event ,
void * refcon )
{
COSXScreen * screen = ( COSXScreen * ) refcon ;
if ( screen - > m_cursorHidden ) {
CGPoint pos ;
bool showCursor = true ;
if ( type = = kCGEventMouseMoved ) {
pos = CGEventGetLocation ( event ) ;
if ( pos . x = = screen - > m_xCenter & & pos . y = = screen - > m_yCenter ) {
showCursor = false ;
}
}
if ( showCursor ) {
LOG ( ( CLOG_DEBUG " Trying to show cursor from local event. (type = %d) " , type ) ) ;
screen - > showCursor ( ) ;
screen - > m_cursorHidden = false ;
}
}
LOG ( ( CLOG_DEBUG2 " Local event? (type = %d) " , type ) ) ;
return event ;
}
2009-11-01 19:15:10 +00:00
// 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 ;
2011-01-10 07:55:41 +00:00
case NX_NUMPROCS :
break ;
2009-11-01 19:15:10 +00:00
default :
LOG ( ( CLOG_NOTE " Unknown Quartz Event type: 0x%02x " , type ) ) ;
}
if ( screen - > m_isOnScreen ) {
return event ;
} else {
return NULL ;
}
2009-11-25 11:09:37 +00:00
}
2010-06-01 10:49:22 +00:00
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 < NumButtonIDs > ( 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 ;
}