|
|
|
|
@ -37,7 +37,7 @@
|
|
|
|
|
#if !defined(MAC_OS_X_VERSION_10_3) || \
|
|
|
|
|
(MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_3)
|
|
|
|
|
enum {
|
|
|
|
|
kEventClassSystem = 'macs',
|
|
|
|
|
kEventClassSystem = 'macs',
|
|
|
|
|
kEventSystemUserSessionActivated = 10,
|
|
|
|
|
kEventSystemUserSessionDeactivated = 11
|
|
|
|
|
};
|
|
|
|
|
@ -54,11 +54,14 @@ enum {
|
|
|
|
|
// COSXScreen
|
|
|
|
|
//
|
|
|
|
|
|
|
|
|
|
bool COSXScreen::s_testedForGHOM = false;
|
|
|
|
|
bool COSXScreen::s_hasGHOM = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool COSXScreen::s_testedForGHOM = false;
|
|
|
|
|
bool COSXScreen::s_hasGHOM = false;
|
|
|
|
|
CEvent::Type COSXScreen::s_confirmSleepEvent = CEvent::kUnknown;
|
|
|
|
|
|
|
|
|
|
COSXScreen::COSXScreen(bool isPrimary) :
|
|
|
|
|
MouseButtonEventMap(NumButtonIDs),
|
|
|
|
|
m_isPrimary(isPrimary),
|
|
|
|
|
m_isOnScreen(m_isPrimary),
|
|
|
|
|
m_cursorPosValid(false),
|
|
|
|
|
@ -107,6 +110,8 @@ COSXScreen::COSXScreen(bool isPrimary) :
|
|
|
|
|
this, &m_switchEventHandlerRef);
|
|
|
|
|
DisposeEventHandlerUPP(switchEventHandler);
|
|
|
|
|
|
|
|
|
|
constructMouseButtonEventMap();
|
|
|
|
|
|
|
|
|
|
// watch for requests to sleep
|
|
|
|
|
EVENTQUEUE->adoptHandler(COSXScreen::getConfirmSleepEvent(),
|
|
|
|
|
getEventTarget(),
|
|
|
|
|
@ -360,6 +365,26 @@ COSXScreen::unregisterHotKey(UInt32 id)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::constructMouseButtonEventMap()
|
|
|
|
|
{
|
|
|
|
|
const CGEventType source[NumButtonIDs][3] = {
|
|
|
|
|
kCGEventLeftMouseUp,kCGEventLeftMouseDragged,kCGEventLeftMouseDown,
|
|
|
|
|
kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown,
|
|
|
|
|
kCGEventRightMouseUp,kCGEventRightMouseDragged,kCGEventRightMouseDown,
|
|
|
|
|
kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown,
|
|
|
|
|
kCGEventOtherMouseUp,kCGEventOtherMouseDragged,kCGEventOtherMouseDown};
|
|
|
|
|
|
|
|
|
|
for (UInt16 button = 0; button < NumButtonIDs; button++) {
|
|
|
|
|
MouseButtonEventMapType new_map;
|
|
|
|
|
for (UInt16 state = (UInt32) kMouseButtonUp; state < kMouseButtonStateMax; state++) {
|
|
|
|
|
CGEventType curEvent = source[button][state];
|
|
|
|
|
new_map[state] = curEvent;
|
|
|
|
|
}
|
|
|
|
|
MouseButtonEventMap[button] = new_map;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::postMouseEvent(CGPoint& pos) const
|
|
|
|
|
{
|
|
|
|
|
@ -392,41 +417,30 @@ COSXScreen::postMouseEvent(CGPoint& pos) const
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// synthesize event. CGPostMouseEvent is a particularly good
|
|
|
|
|
// example of a bad API. we have to shadow the mouse state to
|
|
|
|
|
// use this API and if we want to support more buttons we have
|
|
|
|
|
// to recompile.
|
|
|
|
|
//
|
|
|
|
|
// the order of buttons on the mac is:
|
|
|
|
|
// 1 - Left
|
|
|
|
|
// 2 - Right
|
|
|
|
|
// 3 - Middle
|
|
|
|
|
// Whatever the USB device defined.
|
|
|
|
|
//
|
|
|
|
|
// It is a bit weird that the behaviour of buttons over 3 are dependent
|
|
|
|
|
// on currently plugged in USB devices.
|
|
|
|
|
CGPostMouseEvent(pos, true, sizeof(m_buttons) / sizeof(m_buttons[0]),
|
|
|
|
|
m_buttons[0],
|
|
|
|
|
m_buttons[2],
|
|
|
|
|
m_buttons[1],
|
|
|
|
|
m_buttons[3],
|
|
|
|
|
m_buttons[4]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CGEventType type = kCGEventMouseMoved;
|
|
|
|
|
|
|
|
|
|
SInt8 button = m_buttonState.getFirstButtonDown();
|
|
|
|
|
if (button != -1) {
|
|
|
|
|
MouseButtonEventMapType thisButtonType = MouseButtonEventMap[button];
|
|
|
|
|
type = thisButtonType[kMouseButtonDragged];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CGEventRef event = CGEventCreateMouseEvent(NULL, type, pos, button);
|
|
|
|
|
CGEventPost(kCGHIDEventTap, event);
|
|
|
|
|
|
|
|
|
|
CFRelease(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::fakeMouseButton(ButtonID id, bool press) const
|
|
|
|
|
{
|
|
|
|
|
// get button index
|
|
|
|
|
// Buttons are indexed from one, but the button down array is indexed from zero
|
|
|
|
|
UInt32 index = id - kButtonLeft;
|
|
|
|
|
if (index >= sizeof(m_buttons) / sizeof(m_buttons[0])) {
|
|
|
|
|
if (index >= NumButtonIDs) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// update state
|
|
|
|
|
m_buttons[index] = press;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CGPoint pos;
|
|
|
|
|
if (!m_cursorPosValid) {
|
|
|
|
|
SInt32 x, y;
|
|
|
|
|
@ -434,7 +448,36 @@ COSXScreen::fakeMouseButton(ButtonID id, bool press) const
|
|
|
|
|
}
|
|
|
|
|
pos.x = m_xCursor;
|
|
|
|
|
pos.y = m_yCursor;
|
|
|
|
|
postMouseEvent(pos);
|
|
|
|
|
|
|
|
|
|
// synthesize event. CGEventCreateMouseEvent creates a retained mouse
|
|
|
|
|
// event, which must also be posted and released. Note this is
|
|
|
|
|
// similar to the use of CGEventRef in postMouseEvent above.
|
|
|
|
|
// One of the arguments changes based on whether a button is being
|
|
|
|
|
// pressed or released, pressed corresponding to when "press" is true.
|
|
|
|
|
CGEventRef event;
|
|
|
|
|
|
|
|
|
|
// the switch statement handles which button was pressed. the left
|
|
|
|
|
// and right mouse buttons must be handled separately from any
|
|
|
|
|
// other buttons
|
|
|
|
|
|
|
|
|
|
CGEventType type;
|
|
|
|
|
MouseButtonState state;
|
|
|
|
|
if (press) {
|
|
|
|
|
state = kMouseButtonDown;
|
|
|
|
|
} else {
|
|
|
|
|
state = kMouseButtonUp;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
MouseButtonEventMapType thisButtonMap = MouseButtonEventMap[index];
|
|
|
|
|
type = thisButtonMap[state];
|
|
|
|
|
|
|
|
|
|
event = CGEventCreateMouseEvent(NULL, type, pos, index);
|
|
|
|
|
|
|
|
|
|
m_buttonState.set(index, state);
|
|
|
|
|
|
|
|
|
|
CGEventPost(kCGHIDEventTap, event);
|
|
|
|
|
|
|
|
|
|
CFRelease(event);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
@ -480,8 +523,17 @@ void
|
|
|
|
|
COSXScreen::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
|
|
|
|
|
{
|
|
|
|
|
if (xDelta != 0 || yDelta != 0) {
|
|
|
|
|
CGPostScrollWheelEvent(2, mapScrollWheelFromSynergy(yDelta),
|
|
|
|
|
-mapScrollWheelFromSynergy(xDelta));
|
|
|
|
|
// create a scroll event, post it and release it. not sure if kCGScrollEventUnitLine
|
|
|
|
|
// is the right choice here over kCGScrollEventUnitPixel
|
|
|
|
|
CGEventRef scrollEvent = CGEventCreateScrollWheelEvent(NULL,
|
|
|
|
|
kCGScrollEventUnitLine,
|
|
|
|
|
2,
|
|
|
|
|
mapScrollWheelFromSynergy(yDelta),
|
|
|
|
|
-mapScrollWheelFromSynergy(xDelta));
|
|
|
|
|
|
|
|
|
|
CGEventPost(kCGHIDEventTap, scrollEvent);
|
|
|
|
|
|
|
|
|
|
CFRelease(scrollEvent);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -583,9 +635,7 @@ COSXScreen::enter()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// reset buttons
|
|
|
|
|
for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
|
|
|
m_buttons[i] = false;
|
|
|
|
|
}
|
|
|
|
|
m_buttonState.reset();
|
|
|
|
|
|
|
|
|
|
// avoid suppression of local hardware events
|
|
|
|
|
// stkamp@users.sourceforge.net
|
|
|
|
|
@ -642,22 +692,22 @@ COSXScreen::leave()
|
|
|
|
|
bool
|
|
|
|
|
COSXScreen::setClipboard(ClipboardID, const IClipboard* src)
|
|
|
|
|
{
|
|
|
|
|
if(src != NULL) {
|
|
|
|
|
LOG((CLOG_DEBUG "setting clipboard"));
|
|
|
|
|
CClipboard::copy(&m_pasteboard, src);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
if(src != NULL) {
|
|
|
|
|
LOG((CLOG_DEBUG "setting clipboard"));
|
|
|
|
|
CClipboard::copy(&m_pasteboard, src);
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::checkClipboards()
|
|
|
|
|
{
|
|
|
|
|
LOG((CLOG_DEBUG1 "checking clipboard"));
|
|
|
|
|
if (m_pasteboard.synchronize()) {
|
|
|
|
|
LOG((CLOG_DEBUG "clipboard changed"));
|
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
|
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
|
|
|
|
|
}
|
|
|
|
|
LOG((CLOG_DEBUG1 "checking clipboard"));
|
|
|
|
|
if (m_pasteboard.synchronize()) {
|
|
|
|
|
LOG((CLOG_DEBUG "clipboard changed"));
|
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
|
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
@ -926,7 +976,7 @@ COSXScreen::displayReconfigurationCallback(CGDirectDisplayID displayID, CGDispla
|
|
|
|
|
LOG((CLOG_DEBUG1 "event: display was reconfigured: %x %x %x", flags, mask, flags & mask));
|
|
|
|
|
|
|
|
|
|
if (flags & mask) { /* Something actually did change */
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
LOG((CLOG_DEBUG1 "event: screen changed shape; refreshing dimensions"));
|
|
|
|
|
screen->updateScreenShape(displayID, flags);
|
|
|
|
|
}
|
|
|
|
|
@ -1158,7 +1208,7 @@ COSXScreen::enableDragTimer(bool enable)
|
|
|
|
|
EVENTQUEUE->adoptHandler(CEvent::kTimer, m_dragTimer,
|
|
|
|
|
new TMethodEventJob<COSXScreen>(this,
|
|
|
|
|
&COSXScreen::handleDrag));
|
|
|
|
|
TrackMouseLocationWithOptions(NULL, 0, 0, &m_dragLastPoint, &modifiers, &res);
|
|
|
|
|
TrackMouseLocationWithOptions(NULL, 0, 0, &m_dragLastPoint, &modifiers, &res);
|
|
|
|
|
}
|
|
|
|
|
else if (!enable && m_dragTimer != NULL) {
|
|
|
|
|
EVENTQUEUE->removeHandler(CEvent::kTimer, m_dragTimer);
|
|
|
|
|
@ -1186,9 +1236,8 @@ void
|
|
|
|
|
COSXScreen::updateButtons()
|
|
|
|
|
{
|
|
|
|
|
UInt32 buttons = GetCurrentButtonState();
|
|
|
|
|
for (size_t i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
|
|
|
m_buttons[i] = ((buttons & (1u << i)) != 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
m_buttonState.overwrite(buttons);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
IKeyState*
|
|
|
|
|
@ -1200,7 +1249,7 @@ COSXScreen::getKeyState() const
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSummaryFlags flags)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// get info for each display
|
|
|
|
|
CGDisplayCount displayCount = 0;
|
|
|
|
|
|
|
|
|
|
@ -1243,9 +1292,9 @@ COSXScreen::updateScreenShape(const CGDirectDisplayID, const CGDisplayChangeSumm
|
|
|
|
|
m_yCenter = (rect.origin.y + rect.size.height) / 2;
|
|
|
|
|
|
|
|
|
|
delete[] displays;
|
|
|
|
|
if (m_isPrimary && !m_isOnScreen) {
|
|
|
|
|
sendEvent(getShapeChangedEvent());
|
|
|
|
|
}
|
|
|
|
|
if (m_isPrimary && !m_isOnScreen) {
|
|
|
|
|
sendEvent(getShapeChangedEvent());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LOG((CLOG_DEBUG "screen shape: %d,%d %dx%d on %u %s", m_x, m_y, m_w, m_h, displayCount, (displayCount == 1) ? "display" : "displays"));
|
|
|
|
|
}
|
|
|
|
|
@ -1610,3 +1659,47 @@ COSXScreen::handleCGInputEvent(CGEventTapProxy proxy,
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::CMouseButtonState::set(UInt32 button, MouseButtonState state)
|
|
|
|
|
{
|
|
|
|
|
bool newState = (state == kMouseButtonDown);
|
|
|
|
|
m_buttons.set(button, newState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
|
COSXScreen::CMouseButtonState::any()
|
|
|
|
|
{
|
|
|
|
|
return m_buttons.any();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::CMouseButtonState::reset()
|
|
|
|
|
{
|
|
|
|
|
m_buttons.reset();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
|
COSXScreen::CMouseButtonState::overwrite(UInt32 buttons)
|
|
|
|
|
{
|
|
|
|
|
m_buttons = std::bitset<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;
|
|
|
|
|
}
|
|
|
|
|
|