2004-04-11 18:58:08 +04:00
|
|
|
/*
|
|
|
|
* synergy -- mouse and keyboard sharing utility
|
|
|
|
* Copyright (C) 2004 Chris Schoeneman
|
|
|
|
*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "COSXScreen.h"
|
|
|
|
#include "COSXClipboard.h"
|
|
|
|
#include "COSXEventQueueBuffer.h"
|
|
|
|
#include "COSXKeyState.h"
|
|
|
|
#include "COSXScreenSaver.h"
|
|
|
|
#include "CClipboard.h"
|
|
|
|
#include "CLog.h"
|
|
|
|
#include "IEventQueue.h"
|
|
|
|
#include "TMethodEventJob.h"
|
|
|
|
|
|
|
|
//
|
|
|
|
// COSXScreen
|
|
|
|
//
|
|
|
|
|
|
|
|
COSXScreen::COSXScreen(bool isPrimary) :
|
|
|
|
m_isPrimary(isPrimary),
|
|
|
|
m_isOnScreen(m_isPrimary),
|
2004-05-02 20:13:11 +04:00
|
|
|
m_cursorPosValid(false),
|
2004-04-11 18:58:08 +04:00
|
|
|
m_cursorHidden(false),
|
|
|
|
m_keyState(NULL),
|
|
|
|
m_sequenceNumber(0),
|
|
|
|
m_screensaver(NULL),
|
2004-05-15 23:41:46 +04:00
|
|
|
m_screensaverNotify(false),
|
2004-05-26 23:23:32 +04:00
|
|
|
m_ownClipboard(false),
|
|
|
|
m_hiddenWindow(NULL),
|
|
|
|
m_userInputWindow(NULL),
|
|
|
|
m_displayManagerNotificationUPP(NULL)
|
2004-04-11 18:58:08 +04:00
|
|
|
{
|
|
|
|
try {
|
|
|
|
m_displayID = CGMainDisplayID();
|
|
|
|
updateScreenShape();
|
|
|
|
m_screensaver = new COSXScreenSaver();
|
|
|
|
m_keyState = new COSXKeyState();
|
2004-05-26 23:23:32 +04:00
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// 1x1 window (to minimze the back buffer allocated for this
|
|
|
|
// window.
|
|
|
|
Rect bounds = { 100, 100, 101, 101 };
|
|
|
|
|
|
|
|
// m_hiddenWindow is a window meant to let us get mouse moves
|
2004-10-28 01:22:36 +04:00
|
|
|
// when the focus is on another computer. If you get your event
|
2004-05-26 23:23:32 +04:00
|
|
|
// from the application event target you'll get every mouse
|
|
|
|
// moves. On the other hand the Window event target will only
|
|
|
|
// get events when the mouse moves over the window.
|
|
|
|
|
|
|
|
// The ignoreClicks attributes makes it impossible for the
|
|
|
|
// user to click on our invisible window.
|
|
|
|
CreateNewWindow(kUtilityWindowClass,
|
|
|
|
kWindowNoShadowAttribute |
|
|
|
|
kWindowIgnoreClicksAttribute |
|
|
|
|
kWindowNoActivatesAttribute,
|
|
|
|
&bounds, &m_hiddenWindow);
|
|
|
|
|
|
|
|
// Make it invisible
|
|
|
|
SetWindowAlpha(m_hiddenWindow, 0);
|
|
|
|
ShowWindow(m_hiddenWindow);
|
|
|
|
|
|
|
|
// m_userInputWindow is a window meant to let us get mouse moves
|
|
|
|
// when the focus is on this computer.
|
|
|
|
Rect inputBounds = { 100, 100, 200, 200 };
|
|
|
|
CreateNewWindow(kUtilityWindowClass,
|
|
|
|
kWindowNoShadowAttribute |
|
|
|
|
kWindowOpaqueForEventsAttribute |
|
|
|
|
kWindowStandardHandlerAttribute,
|
|
|
|
&inputBounds, &m_userInputWindow);
|
|
|
|
|
|
|
|
SetWindowAlpha(m_userInputWindow, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
m_displayManagerNotificationUPP =
|
|
|
|
NewDMExtendedNotificationUPP(displayManagerCallback);
|
|
|
|
|
|
|
|
OSStatus err = GetCurrentProcess(&m_PSN);
|
|
|
|
|
|
|
|
err = DMRegisterExtendedNotifyProc(m_displayManagerNotificationUPP,
|
|
|
|
this, 0, &m_PSN);
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
catch (...) {
|
2004-05-26 23:23:32 +04:00
|
|
|
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;
|
|
|
|
}
|
2004-04-11 18:58:08 +04: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());
|
2004-05-26 23:23:32 +04:00
|
|
|
|
|
|
|
if (m_hiddenWindow) {
|
|
|
|
ReleaseWindow(m_hiddenWindow);
|
|
|
|
m_hiddenWindow = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m_userInputWindow) {
|
|
|
|
ReleaseWindow(m_userInputWindow);
|
|
|
|
m_userInputWindow = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
DMRemoveExtendedNotifyProc(m_displayManagerNotificationUPP,
|
|
|
|
NULL, &m_PSN, 0);
|
|
|
|
|
2004-04-11 18:58:08 +04:00
|
|
|
delete m_keyState;
|
|
|
|
delete m_screensaver;
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
COSXScreen::getEventTarget() const
|
|
|
|
{
|
|
|
|
return const_cast<COSXScreen*>(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
COSXScreen::getClipboard(ClipboardID, IClipboard* dst) const
|
|
|
|
{
|
|
|
|
COSXClipboard src;
|
|
|
|
CClipboard::copy(dst, &src);
|
|
|
|
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);
|
2004-05-02 20:13:11 +04:00
|
|
|
x = mouse.h;
|
|
|
|
y = mouse.v;
|
|
|
|
m_cursorPosValid = true;
|
|
|
|
m_xCursor = x;
|
|
|
|
m_yCursor = y;
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::reconfigure(UInt32 activeSides)
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
(void)activeSides;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::warpCursor(SInt32 x, SInt32 y)
|
|
|
|
{
|
|
|
|
// move cursor without generating events
|
|
|
|
CGPoint pos;
|
|
|
|
pos.x = x;
|
|
|
|
pos.y = y;
|
|
|
|
CGWarpMouseCursorPosition(pos);
|
|
|
|
|
|
|
|
// save new cursor position
|
2004-05-02 20:13:11 +04:00
|
|
|
m_xCursor = x;
|
|
|
|
m_yCursor = y;
|
|
|
|
m_cursorPosValid = true;
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
SInt32
|
|
|
|
COSXScreen::getJumpZoneSize() const
|
|
|
|
{
|
|
|
|
// FIXME -- is this correct?
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
COSXScreen::isAnyMouseButtonDown() const
|
|
|
|
{
|
|
|
|
// FIXME
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::getCursorCenter(SInt32& x, SInt32& y) const
|
|
|
|
{
|
|
|
|
x = m_xCenter;
|
|
|
|
y = m_yCenter;
|
|
|
|
}
|
|
|
|
|
2004-05-15 23:41:46 +04:00
|
|
|
void
|
|
|
|
COSXScreen::postMouseEvent(const 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.
|
2004-05-26 23:23:32 +04:00
|
|
|
//
|
2004-05-15 23:41:46 +04:00
|
|
|
// 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]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2004-04-11 18:58:08 +04:00
|
|
|
void
|
|
|
|
COSXScreen::fakeMouseButton(ButtonID id, bool press) const
|
|
|
|
{
|
|
|
|
// get button index
|
|
|
|
UInt32 index = id - kButtonLeft;
|
|
|
|
if (index >= sizeof(m_buttons) / sizeof(m_buttons[0])) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update state
|
|
|
|
m_buttons[index] = press;
|
|
|
|
|
|
|
|
CGPoint pos;
|
2004-05-02 20:13:11 +04:00
|
|
|
if (!m_cursorPosValid) {
|
|
|
|
SInt32 x, y;
|
|
|
|
getCursorPos(x, y);
|
|
|
|
}
|
2004-04-11 18:58:08 +04:00
|
|
|
pos.x = m_xCursor;
|
|
|
|
pos.y = m_yCursor;
|
2004-05-15 23:41:46 +04:00
|
|
|
postMouseEvent(pos);
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::fakeMouseMove(SInt32 x, SInt32 y) const
|
|
|
|
{
|
|
|
|
// synthesize event
|
|
|
|
CGPoint pos;
|
|
|
|
pos.x = x;
|
|
|
|
pos.y = y;
|
2004-05-15 23:41:46 +04:00
|
|
|
postMouseEvent(pos);
|
2004-04-11 18:58:08 +04:00
|
|
|
|
|
|
|
// save new cursor position
|
2004-05-02 20:13:11 +04:00
|
|
|
m_xCursor = x;
|
|
|
|
m_yCursor = 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;
|
|
|
|
pos.x = oldPos.h + dx;
|
|
|
|
pos.y = oldPos.v + dy;
|
2004-05-15 23:41:46 +04:00
|
|
|
postMouseEvent(pos);
|
2004-05-02 20:13:11 +04:00
|
|
|
|
|
|
|
// we now assume we don't know the current cursor position
|
|
|
|
m_cursorPosValid = false;
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::fakeMouseWheel(SInt32 delta) const
|
|
|
|
{
|
2004-07-30 02:11:27 +04:00
|
|
|
// synergy uses a wheel step size of 120. the mac uses a step size of 1.
|
|
|
|
delta /= 120;
|
|
|
|
if (delta == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-05-15 23:41:46 +04:00
|
|
|
CFPropertyListRef pref = ::CFPreferencesCopyValue(
|
2004-05-26 23:23:32 +04:00
|
|
|
CFSTR("com.apple.scrollwheel.scaling") ,
|
|
|
|
kCFPreferencesAnyApplication,
|
2004-05-15 23:41:46 +04:00
|
|
|
kCFPreferencesCurrentUser,
|
|
|
|
kCFPreferencesAnyHost);
|
|
|
|
|
2004-05-26 23:23:32 +04:00
|
|
|
int32_t wheelIncr = 1;
|
2004-05-15 23:41:46 +04:00
|
|
|
|
|
|
|
if (pref != NULL) {
|
|
|
|
CFTypeID id = CFGetTypeID(pref);
|
|
|
|
if (id == CFNumberGetTypeID()) {
|
|
|
|
CFNumberRef value = static_cast<CFNumberRef>(pref);
|
|
|
|
|
|
|
|
double scaling;
|
|
|
|
if (CFNumberGetValue(value, kCFNumberDoubleType, &scaling)) {
|
|
|
|
wheelIncr = (int32_t)(8 * scaling);
|
2004-05-26 23:23:32 +04:00
|
|
|
if (wheelIncr == 0) {
|
|
|
|
wheelIncr = 1;
|
|
|
|
}
|
2004-05-15 23:41:46 +04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
CFRelease(pref);
|
|
|
|
}
|
|
|
|
|
2004-07-30 02:11:27 +04:00
|
|
|
// note that we ignore the magnitude of the delta. i think this is to
|
|
|
|
// avoid local wheel acceleration.
|
2004-05-15 23:41:46 +04:00
|
|
|
if (delta < 0) {
|
|
|
|
wheelIncr = -wheelIncr;
|
|
|
|
}
|
|
|
|
|
|
|
|
CGPostScrollWheelEvent(1, wheelIncr);
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::enable()
|
|
|
|
{
|
|
|
|
// FIXME -- install clipboard snooper (if we need one)
|
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// FIXME -- start watching jump zones
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// FIXME -- prevent system from entering power save mode
|
|
|
|
|
|
|
|
// hide cursor
|
|
|
|
if (!m_cursorHidden) {
|
|
|
|
CGDisplayHideCursor(m_displayID);
|
|
|
|
m_cursorHidden = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// warp the mouse to the cursor center
|
|
|
|
fakeMouseMove(m_xCenter, m_yCenter);
|
|
|
|
|
|
|
|
// FIXME -- prepare to show cursor if it moves
|
|
|
|
}
|
|
|
|
|
|
|
|
updateKeys();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::disable()
|
|
|
|
{
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// FIXME -- stop watching jump zones, stop capturing input
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// show cursor
|
|
|
|
if (m_cursorHidden) {
|
|
|
|
CGDisplayShowCursor(m_displayID);
|
|
|
|
m_cursorHidden = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME -- allow system to enter power saving mode
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME -- uninstall clipboard snooper (if we needed one)
|
|
|
|
|
|
|
|
m_isOnScreen = m_isPrimary;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::enter()
|
|
|
|
{
|
|
|
|
if (m_isPrimary) {
|
2004-05-26 23:23:32 +04:00
|
|
|
// stop capturing input, watch jump zones
|
|
|
|
HideWindow( m_userInputWindow );
|
|
|
|
ShowWindow( m_hiddenWindow );
|
|
|
|
|
|
|
|
SetMouseCoalescingEnabled(true, NULL);
|
|
|
|
|
|
|
|
CGSetLocalEventsSuppressionInterval(HUGE_VAL);
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// show cursor
|
|
|
|
if (m_cursorHidden) {
|
|
|
|
CGDisplayShowCursor(m_displayID);
|
|
|
|
m_cursorHidden = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset buttons
|
|
|
|
for (UInt32 i = 0; i < sizeof(m_buttons) / sizeof(m_buttons[0]); ++i) {
|
|
|
|
m_buttons[i] = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now on screen
|
|
|
|
m_isOnScreen = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
COSXScreen::leave()
|
|
|
|
{
|
|
|
|
// FIXME -- choose keyboard layout if per-process and activate it here
|
|
|
|
|
|
|
|
if (m_isPrimary) {
|
|
|
|
// update key and button state
|
|
|
|
updateKeys();
|
|
|
|
|
|
|
|
// warp to center
|
|
|
|
warpCursor(m_xCenter, m_yCenter);
|
|
|
|
|
|
|
|
// capture events
|
2004-05-26 23:23:32 +04:00
|
|
|
HideWindow(m_hiddenWindow);
|
|
|
|
ShowWindow(m_userInputWindow);
|
|
|
|
RepositionWindow(m_userInputWindow,
|
|
|
|
m_userInputWindow, kWindowCenterOnMainScreen);
|
|
|
|
SetUserFocusWindow(m_userInputWindow);
|
|
|
|
|
|
|
|
// The OS will coalesce some events if they are similar enough in a
|
|
|
|
// short period of time this is bad for us since we need every event
|
|
|
|
// to send it over to other machines. So disable it.
|
|
|
|
SetMouseCoalescingEnabled(false, NULL);
|
|
|
|
CGSetLocalEventsSuppressionInterval(0.0001);
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// hide cursor
|
|
|
|
if (!m_cursorHidden) {
|
|
|
|
CGDisplayHideCursor(m_displayID);
|
|
|
|
m_cursorHidden = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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)
|
|
|
|
{
|
|
|
|
COSXClipboard dst;
|
2004-05-15 23:41:46 +04:00
|
|
|
m_ownClipboard = true;
|
2004-04-11 18:58:08 +04:00
|
|
|
if (src != NULL) {
|
|
|
|
// save clipboard data
|
|
|
|
return CClipboard::copy(&dst, src);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// assert clipboard ownership
|
|
|
|
if (!dst.open(0)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
dst.empty();
|
|
|
|
dst.close();
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::checkClipboards()
|
|
|
|
{
|
2004-05-15 23:41:46 +04:00
|
|
|
if (m_ownClipboard && !COSXClipboard::isOwnedBySynergy()) {
|
|
|
|
static ScrapRef sScrapbook = NULL;
|
|
|
|
ScrapRef currentScrap;
|
|
|
|
GetCurrentScrap(¤tScrap);
|
|
|
|
|
|
|
|
if (sScrapbook != currentScrap) {
|
|
|
|
m_ownClipboard = false;
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardClipboard);
|
|
|
|
sendClipboardEvent(getClipboardGrabbedEvent(), kClipboardSelection);
|
|
|
|
sScrapbook = currentScrap;
|
|
|
|
}
|
|
|
|
}
|
2004-04-11 18:58:08 +04: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
|
2004-05-26 23:23:32 +04:00
|
|
|
COSXScreen::sendEvent(CEvent::Type type, void* data) const
|
2004-04-11 18:58:08 +04:00
|
|
|
{
|
2004-05-15 23:41:46 +04:00
|
|
|
EVENTQUEUE->addEvent(CEvent(type, getEventTarget(), data));
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2004-05-26 23:23:32 +04:00
|
|
|
COSXScreen::sendClipboardEvent(CEvent::Type type, ClipboardID id) const
|
2004-05-15 23:41:46 +04:00
|
|
|
{
|
|
|
|
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*)
|
|
|
|
{
|
2004-05-26 23:23:32 +04:00
|
|
|
EventRef* carbonEvent = reinterpret_cast<EventRef*>(event.getData());
|
2004-05-15 23:41:46 +04:00
|
|
|
assert(carbonEvent != NULL);
|
|
|
|
|
2004-05-26 23:23:32 +04:00
|
|
|
UInt32 eventClass = GetEventClass(*carbonEvent);
|
2004-05-15 23:41:46 +04:00
|
|
|
|
2004-05-26 23:23:32 +04:00
|
|
|
switch (eventClass) {
|
|
|
|
case kEventClassMouse:
|
|
|
|
switch (GetEventKind(*carbonEvent)) {
|
|
|
|
case kEventMouseDown:
|
2004-05-15 23:41:46 +04:00
|
|
|
{
|
2004-05-26 23:23:32 +04:00
|
|
|
UInt16 myButton;
|
|
|
|
GetEventParameter(*carbonEvent,
|
|
|
|
kEventParamMouseButton,
|
|
|
|
typeMouseButton,
|
|
|
|
NULL,
|
|
|
|
sizeof(myButton),
|
|
|
|
NULL,
|
|
|
|
&myButton);
|
|
|
|
onMouseButton(true, myButton);
|
|
|
|
break;
|
|
|
|
}
|
2004-05-15 23:41:46 +04:00
|
|
|
|
2004-05-26 23:23:32 +04:00
|
|
|
case kEventMouseUp:
|
2004-05-15 23:41:46 +04:00
|
|
|
{
|
2004-05-26 23:23:32 +04:00
|
|
|
UInt16 myButton;
|
|
|
|
GetEventParameter(*carbonEvent,
|
|
|
|
kEventParamMouseButton,
|
|
|
|
typeMouseButton,
|
|
|
|
NULL,
|
|
|
|
sizeof(myButton),
|
|
|
|
NULL,
|
|
|
|
&myButton);
|
|
|
|
onMouseButton(false, myButton);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
case kEventMouseDragged:
|
|
|
|
case kEventMouseMoved:
|
|
|
|
{
|
|
|
|
HIPoint point;
|
|
|
|
GetEventParameter(*carbonEvent,
|
|
|
|
kEventParamMouseLocation,
|
|
|
|
typeHIPoint,
|
|
|
|
NULL,
|
|
|
|
sizeof(point),
|
|
|
|
NULL,
|
|
|
|
&point);
|
|
|
|
onMouseMove((SInt32)point.x, (SInt32)point.y);
|
|
|
|
break;
|
2004-05-15 23:41:46 +04:00
|
|
|
}
|
|
|
|
|
2004-05-26 23:23:32 +04:00
|
|
|
case kEventMouseWheelMoved:
|
2004-05-15 23:41:46 +04:00
|
|
|
{
|
2004-05-26 23:23:32 +04:00
|
|
|
EventMouseWheelAxis axis;
|
|
|
|
SInt32 delta;
|
|
|
|
GetEventParameter(*carbonEvent,
|
|
|
|
kEventParamMouseWheelAxis,
|
|
|
|
typeMouseWheelAxis,
|
|
|
|
NULL,
|
|
|
|
sizeof(axis),
|
|
|
|
NULL,
|
|
|
|
&axis);
|
|
|
|
if (axis == kEventMouseWheelAxisY) {
|
|
|
|
GetEventParameter(*carbonEvent,
|
|
|
|
kEventParamMouseWheelDelta,
|
|
|
|
typeLongInteger,
|
|
|
|
NULL,
|
|
|
|
sizeof(delta),
|
|
|
|
NULL,
|
|
|
|
&delta);
|
|
|
|
onMouseWheel(120 * (SInt32)delta);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case kEventClassKeyboard:
|
|
|
|
switch (GetEventKind(*carbonEvent)) {
|
|
|
|
case kEventRawKeyUp:
|
|
|
|
case kEventRawKeyDown:
|
|
|
|
case kEventRawKeyRepeat:
|
|
|
|
case kEventRawKeyModifiersChanged:
|
|
|
|
// case kEventHotKeyPressed:
|
|
|
|
// case kEventHotKeyReleased:
|
|
|
|
onKey(*carbonEvent);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
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:
|
|
|
|
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) const
|
|
|
|
{
|
|
|
|
// 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) {
|
|
|
|
sendEvent(getButtonDownEvent(), CButtonInfo::alloc(button));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
LOG((CLOG_DEBUG1 "event: button release button=%d", button));
|
|
|
|
if (button != kButtonNone) {
|
|
|
|
sendEvent(getButtonUpEvent(), CButtonInfo::alloc(button));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool
|
|
|
|
COSXScreen::onMouseWheel(SInt32 delta) const
|
|
|
|
{
|
|
|
|
LOG((CLOG_DEBUG1 "event: button wheel delta=%d", delta));
|
|
|
|
sendEvent(getWheelEvent(), CWheelInfo::alloc(delta));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool
|
|
|
|
COSXScreen::onKey(EventRef event) const
|
|
|
|
{
|
|
|
|
UInt32 eventKind = GetEventKind(event);
|
|
|
|
|
2004-07-30 02:11:27 +04:00
|
|
|
// get the key
|
2004-10-24 22:15:33 +04:00
|
|
|
UInt32 virtualKey;
|
2004-05-26 23:23:32 +04:00
|
|
|
GetEventParameter(event, kEventParamKeyCode, typeUInt32,
|
2004-10-24 22:15:33 +04:00
|
|
|
NULL, sizeof(virtualKey), NULL, &virtualKey);
|
|
|
|
LOG((CLOG_DEBUG1 "event: Key event kind: %d, keycode=%d", eventKind, virtualKey));
|
|
|
|
|
|
|
|
// sadly, OS X doesn't report the virtualKey for modifier keys.
|
|
|
|
// virtualKey will be zero for modifier keys. since that's not good
|
|
|
|
// enough we'll have to figure out what the key was.
|
|
|
|
if (virtualKey == 0 && eventKind == kEventRawKeyModifiersChanged) {
|
2004-07-30 02:11:27 +04:00
|
|
|
// get old and new modifier state
|
|
|
|
KeyModifierMask oldMask = getActiveModifiers();
|
|
|
|
KeyModifierMask newMask = mapMacModifiersToSynergy(event);
|
|
|
|
m_keyState->handleModifierKeys(getEventTarget(), oldMask, newMask);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2004-10-24 22:15:33 +04:00
|
|
|
// decode event type
|
2004-05-26 23:23:32 +04:00
|
|
|
bool down = (eventKind == kEventRawKeyDown);
|
|
|
|
bool up = (eventKind == kEventRawKeyUp);
|
|
|
|
bool isRepeat = (eventKind == kEventRawKeyRepeat);
|
2004-10-24 22:15:33 +04:00
|
|
|
|
|
|
|
// map event to keys
|
|
|
|
KeyModifierMask mask;
|
|
|
|
COSXKeyState::CKeyIDs keys;
|
|
|
|
KeyButton button = m_keyState->mapKeyFromEvent(keys, &mask, event);
|
|
|
|
if (button == 0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// update button state
|
2004-05-26 23:23:32 +04:00
|
|
|
if (down) {
|
|
|
|
m_keyState->setKeyDown(button, true);
|
|
|
|
}
|
|
|
|
else if (up) {
|
2004-10-24 22:15:33 +04:00
|
|
|
if (!isKeyDown(button)) {
|
|
|
|
// up event for a dead key. throw it away.
|
|
|
|
return false;
|
|
|
|
}
|
2004-05-26 23:23:32 +04:00
|
|
|
m_keyState->setKeyDown(button, false);
|
|
|
|
}
|
|
|
|
|
2004-10-24 22:15:33 +04:00
|
|
|
// send key events
|
|
|
|
for (COSXKeyState::CKeyIDs::const_iterator i = keys.begin();
|
|
|
|
i != keys.end(); ++i) {
|
|
|
|
m_keyState->sendKeyEvent(getEventTarget(), down, isRepeat,
|
|
|
|
*i, mask, 1, button);
|
|
|
|
}
|
2004-05-26 23:23:32 +04:00
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
ButtonID
|
|
|
|
COSXScreen::mapMacButtonToSynergy(UInt16 macButton) const
|
|
|
|
{
|
|
|
|
switch (macButton) {
|
|
|
|
case 1:
|
|
|
|
return kButtonLeft;
|
|
|
|
|
|
|
|
case 2:
|
|
|
|
return kButtonRight;
|
|
|
|
|
|
|
|
case 3:
|
|
|
|
return kButtonMiddle;
|
2004-05-15 23:41:46 +04:00
|
|
|
}
|
2004-05-26 23:23:32 +04:00
|
|
|
|
|
|
|
return kButtonNone;
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|
|
|
|
|
2004-07-30 02:11:27 +04:00
|
|
|
KeyModifierMask
|
|
|
|
COSXScreen::mapMacModifiersToSynergy(EventRef event) const
|
|
|
|
{
|
|
|
|
// get native bit mask
|
|
|
|
UInt32 macMask;
|
|
|
|
GetEventParameter(event, kEventParamKeyModifiers, typeUInt32,
|
|
|
|
NULL, sizeof(macMask), NULL, &macMask);
|
|
|
|
|
|
|
|
// convert
|
|
|
|
KeyModifierMask outMask = 0;
|
|
|
|
if ((macMask & shiftKey) != 0) {
|
|
|
|
outMask |= KeyModifierShift;
|
|
|
|
}
|
|
|
|
if ((macMask & rightShiftKey) != 0) {
|
|
|
|
outMask |= KeyModifierShift;
|
|
|
|
}
|
|
|
|
if ((macMask & controlKey) != 0) {
|
|
|
|
outMask |= KeyModifierControl;
|
|
|
|
}
|
|
|
|
if ((macMask & rightControlKey) != 0) {
|
|
|
|
outMask |= KeyModifierControl;
|
|
|
|
}
|
|
|
|
if ((macMask & cmdKey) != 0) {
|
|
|
|
outMask |= KeyModifierAlt;
|
|
|
|
}
|
|
|
|
if ((macMask & optionKey) != 0) {
|
|
|
|
outMask |= KeyModifierSuper;
|
|
|
|
}
|
|
|
|
if ((macMask & rightOptionKey) != 0) {
|
|
|
|
outMask |= KeyModifierSuper;
|
|
|
|
}
|
|
|
|
if ((macMask & alphaLock) != 0) {
|
|
|
|
outMask |= KeyModifierCapsLock;
|
|
|
|
}
|
|
|
|
|
|
|
|
return outMask;
|
|
|
|
}
|
|
|
|
|
2004-04-11 18:58:08 +04:00
|
|
|
void
|
|
|
|
COSXScreen::updateButtons()
|
|
|
|
{
|
|
|
|
// FIXME -- get current button state into m_buttons[]
|
|
|
|
}
|
|
|
|
|
|
|
|
IKeyState*
|
|
|
|
COSXScreen::getKeyState() const
|
|
|
|
{
|
|
|
|
return m_keyState;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
COSXScreen::updateScreenShape()
|
|
|
|
{
|
2004-05-15 23:41:46 +04:00
|
|
|
// get info for each display
|
|
|
|
CGDisplayCount displayCount = 0;
|
|
|
|
|
|
|
|
if (CGGetActiveDisplayList(0, NULL, &displayCount) != CGDisplayNoErr) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (displayCount == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2004-10-28 01:22:36 +04:00
|
|
|
CGDirectDisplayID* displays = new CGDirectDisplayID[displayCount];
|
2004-05-15 23:41:46 +04:00
|
|
|
if (displays == NULL) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (CGGetActiveDisplayList(displayCount,
|
|
|
|
displays, &displayCount) != CGDisplayNoErr) {
|
2004-10-28 01:22:36 +04:00
|
|
|
delete[] displays;
|
2004-05-15 23:41:46 +04:00
|
|
|
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);
|
|
|
|
}
|
2004-04-11 18:58:08 +04:00
|
|
|
|
|
|
|
// get shape of default screen
|
2004-05-15 23:41:46 +04:00
|
|
|
m_x = (SInt32)totalBounds.origin.x;
|
|
|
|
m_y = (SInt32)totalBounds.origin.y;
|
|
|
|
m_w = (SInt32)totalBounds.size.width;
|
|
|
|
m_h = (SInt32)totalBounds.size.height;
|
2004-04-11 18:58:08 +04:00
|
|
|
|
|
|
|
// get center of default screen
|
2004-10-28 01:22:36 +04:00
|
|
|
GDHandle mainScreen = GetMainDevice();
|
|
|
|
if (mainScreen != NULL) {
|
|
|
|
const Rect& rect = (*mainScreen)->gdRect;
|
|
|
|
m_xCenter = (rect.left + rect.right) / 2;
|
|
|
|
m_yCenter = (rect.top + rect.bottom) / 2;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
m_xCenter = m_x + (m_w >> 1);
|
|
|
|
m_yCenter = m_y + (m_h >> 1);
|
|
|
|
}
|
2004-05-15 23:41:46 +04:00
|
|
|
|
2004-10-28 01:22:36 +04:00
|
|
|
delete[] displays;
|
2004-05-26 23:23:32 +04: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"));
|
2004-04-11 18:58:08 +04:00
|
|
|
}
|