refactoring. refactored stuff in client (with changes to server
as necessary).
This commit is contained in:
parent
ef7fe1f283
commit
52b60d5175
|
@ -50,53 +50,60 @@ CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen()
|
|||
void
|
||||
CMSWindowsSecondaryScreen::run()
|
||||
{
|
||||
assert(m_window != NULL);
|
||||
|
||||
// must call run() from same thread as open()
|
||||
assert(m_threadID == GetCurrentThreadId());
|
||||
|
||||
// change our priority
|
||||
CThread::getCurrentThread().setPriority(-7);
|
||||
|
||||
// poll input desktop to see if it changes (onPreTranslate()
|
||||
// handles WM_TIMER)
|
||||
UINT timer = 0;
|
||||
if (!m_is95Family) {
|
||||
SetTimer(NULL, 0, 200, NULL);
|
||||
}
|
||||
|
||||
// run event loop
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
doRun();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
|
||||
// remove timer
|
||||
if (!m_is95Family) {
|
||||
KillTimer(NULL, timer);
|
||||
try {
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
mainLoop();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
}
|
||||
catch (...) {
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::stop()
|
||||
{
|
||||
doStop();
|
||||
exitMainLoop();
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::open()
|
||||
{
|
||||
// open the display
|
||||
openDisplay();
|
||||
assert(m_window == NULL);
|
||||
|
||||
// update key state
|
||||
updateKeys();
|
||||
updateModifiers();
|
||||
try {
|
||||
// open the display
|
||||
openDisplay();
|
||||
|
||||
// assume primary has all clipboards
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
grabClipboard(id);
|
||||
// create and prepare our window
|
||||
createWindow();
|
||||
|
||||
// initialize the clipboards; assume primary has all clipboards
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
grabClipboard(id);
|
||||
}
|
||||
|
||||
// get keyboard state
|
||||
updateKeys();
|
||||
updateModifiers();
|
||||
|
||||
// disable the screen saver
|
||||
installScreenSaver();
|
||||
}
|
||||
catch (...) {
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
|
||||
// disable the screen saver
|
||||
getScreenSaver()->disable();
|
||||
|
||||
// hide the cursor
|
||||
m_active = true;
|
||||
|
@ -106,13 +113,8 @@ CMSWindowsSecondaryScreen::open()
|
|||
void
|
||||
CMSWindowsSecondaryScreen::close()
|
||||
{
|
||||
// release keys that are logically pressed
|
||||
releaseKeys();
|
||||
|
||||
// restore the screen saver settings
|
||||
getScreenSaver()->enable();
|
||||
|
||||
// close the display
|
||||
uninstallScreenSaver();
|
||||
destroyWindow();
|
||||
closeDisplay();
|
||||
}
|
||||
|
||||
|
@ -145,8 +147,11 @@ CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
|||
toggleKey(VK_SCROLL, KeyModifierScrollLock);
|
||||
}
|
||||
|
||||
// warp to requested location
|
||||
warpCursor(x, y);
|
||||
|
||||
// show mouse
|
||||
onEnter(x, y);
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -161,30 +166,13 @@ CMSWindowsSecondaryScreen::leave()
|
|||
syncDesktop();
|
||||
|
||||
// hide mouse
|
||||
onLeave();
|
||||
showWindow();
|
||||
|
||||
// not active anymore
|
||||
m_active = false;
|
||||
|
||||
// if we think we own the clipboard but we don't then somebody
|
||||
// grabbed the clipboard on this screen without us knowing.
|
||||
// tell the server that this screen grabbed the clipboard.
|
||||
//
|
||||
// this works around bugs in the clipboard viewer chain.
|
||||
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
||||
// messages for no apparent reason and rebooting fixes the
|
||||
// problem. since we don't want a broken clipboard until the
|
||||
// next reboot we do this double check. clipboard ownership
|
||||
// won't be reflected on other screens until we leave but at
|
||||
// least the clipboard itself will work.
|
||||
HWND clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != clipboardOwner) {
|
||||
m_clipboardOwner = clipboardOwner;
|
||||
if (m_clipboardOwner != m_window) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
}
|
||||
// make sure our idea of clipboard ownership is correct
|
||||
checkClipboard();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -402,15 +390,7 @@ CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const
|
|||
assert(m_window != NULL);
|
||||
syncDesktop();
|
||||
|
||||
POINT pos;
|
||||
if (GetCursorPos(&pos)) {
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
else {
|
||||
x = 0;
|
||||
y = 0;
|
||||
}
|
||||
getCursorPos(x, y);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -437,52 +417,18 @@ CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/,
|
|||
CClipboard::copy(dst, &src);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::onOpenDisplay()
|
||||
{
|
||||
assert(m_window == NULL);
|
||||
|
||||
// note if using multiple monitors
|
||||
m_multimon = isMultimon();
|
||||
|
||||
// save thread id. we'll need to pass this to the hook library.
|
||||
m_threadID = GetCurrentThreadId();
|
||||
|
||||
// get the input desktop and switch to it
|
||||
if (m_is95Family) {
|
||||
if (!openDesktop()) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!switchDesktop(openInputDesktop())) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::onCloseDisplay()
|
||||
{
|
||||
// disconnect from desktop
|
||||
if (m_is95Family) {
|
||||
closeDesktop();
|
||||
}
|
||||
else {
|
||||
switchDesktop(NULL);
|
||||
}
|
||||
|
||||
// clear thread id
|
||||
m_threadID = 0;
|
||||
|
||||
assert(m_window == NULL);
|
||||
assert(m_desk == NULL);
|
||||
}
|
||||
|
||||
bool
|
||||
CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
||||
CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
|
||||
// forward to superclass
|
||||
if (CMSWindowsScreen::onPreDispatch(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle event
|
||||
const MSG* msg = &event->m_msg;
|
||||
switch (msg->message) {
|
||||
case WM_TIMER:
|
||||
// if current desktop is not the input desktop then switch to it
|
||||
|
@ -503,32 +449,35 @@ CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
|||
return false;
|
||||
}
|
||||
|
||||
LRESULT
|
||||
CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
bool
|
||||
CMSWindowsSecondaryScreen::onEvent(CEvent* event)
|
||||
{
|
||||
switch (msg) {
|
||||
assert(event != NULL);
|
||||
|
||||
const MSG& msg = event->msg;
|
||||
switch (msg.message) {
|
||||
case WM_QUERYENDSESSION:
|
||||
if (m_is95Family) {
|
||||
return TRUE;
|
||||
event->m_result = TRUE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_ENDSESSION:
|
||||
if (m_is95Family) {
|
||||
if (wParam == TRUE && lParam == 0) {
|
||||
if (msg.wParam == TRUE && msg.lParam == 0) {
|
||||
stop();
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_PAINT:
|
||||
ValidateRect(hwnd, NULL);
|
||||
return 0;
|
||||
ValidateRect(msg.hwnd, NULL);
|
||||
return true;
|
||||
|
||||
case WM_ACTIVATEAPP:
|
||||
if (wParam == FALSE) {
|
||||
if (msg.wParam == FALSE) {
|
||||
// some other app activated. hide the hider window.
|
||||
ShowWindow(m_window, SW_HIDE);
|
||||
}
|
||||
|
@ -538,67 +487,265 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg,
|
|||
log((CLOG_DEBUG "clipboard was taken"));
|
||||
|
||||
// first pass it on
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow,
|
||||
msg.message, msg.wParam, msg.lParam);
|
||||
}
|
||||
|
||||
// now notify client that somebody changed the clipboard (unless
|
||||
// we're now the owner, in which case it's because we took
|
||||
// ownership, or now it's owned by nobody, which will happen if
|
||||
// we owned it and switched desktops because we destroy our
|
||||
// window to do that).
|
||||
m_clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
try {
|
||||
m_clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
catch (XBadClient&) {
|
||||
// ignore. this can happen if we receive this event
|
||||
// before we've fully started up.
|
||||
}
|
||||
return true;
|
||||
|
||||
case WM_CHANGECBCHAIN:
|
||||
if (m_nextClipboardWindow == (HWND)wParam) {
|
||||
m_nextClipboardWindow = (HWND)lParam;
|
||||
if (m_nextClipboardWindow == (HWND)msg.wParam) {
|
||||
m_nextClipboardWindow = (HWND)msg.lParam;
|
||||
}
|
||||
else {
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
else if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow,
|
||||
msg.message, msg.wParam, msg.lParam);
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case WM_DISPLAYCHANGE:
|
||||
// screen resolution has changed
|
||||
updateScreenShape();
|
||||
m_multimon = isMultimon();
|
||||
{
|
||||
// screen resolution may have changed. get old shape.
|
||||
SInt32 xOld, yOld, wOld, hOld;
|
||||
getScreenShape(xOld, yOld, wOld, hOld);
|
||||
|
||||
// send new info
|
||||
CClientInfo info;
|
||||
getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
getMousePos(info.m_mx, info.m_my);
|
||||
info.m_zoneSize = getJumpZoneSize();
|
||||
m_receiver->onInfoChanged(info);
|
||||
return 0;
|
||||
// update shape
|
||||
updateScreenShape();
|
||||
m_multimon = isMultimon();
|
||||
|
||||
// collect new screen info
|
||||
CClientInfo info;
|
||||
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
getCursorPos(info.m_mx, info.m_my);
|
||||
info.m_zoneSize = getJumpZoneSize();
|
||||
|
||||
// do nothing if resolution hasn't changed
|
||||
if (info.m_x != xOld || info.m_y != yOld ||
|
||||
info.m_w != wOld || info.m_h != hOld) {
|
||||
// send new screen info
|
||||
m_receiver->onInfoChanged(info);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
return false;
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsSecondaryScreen::getCurrentDesktopName() const
|
||||
{
|
||||
return m_deskName;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y)
|
||||
CMSWindowsSecondaryScreen::showWindow()
|
||||
{
|
||||
// warp to requested location
|
||||
warpCursor(x, y);
|
||||
// move hider window under the mouse (rather than moving the mouse
|
||||
// somewhere else on the screen)
|
||||
SInt32 x, y;
|
||||
getCursorPos(x, y);
|
||||
MoveWindow(m_window, x, y, 1, 1, FALSE);
|
||||
|
||||
// show cursor
|
||||
// raise and show the hider window. take activation.
|
||||
ShowWindow(m_window, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::hideWindow()
|
||||
{
|
||||
ShowWindow(m_window, SW_HIDE);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::onLeave()
|
||||
CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||
{
|
||||
// move hider window under the mouse (rather than moving the mouse
|
||||
// somewhere else on the screen)
|
||||
POINT point;
|
||||
GetCursorPos(&point);
|
||||
MoveWindow(m_window, point.x, point.y, 1, 1, FALSE);
|
||||
// move the mouse directly to target position on NT family or if
|
||||
// not using multiple monitors.
|
||||
if (!m_multimon || !m_is95Family) {
|
||||
SInt32 x0, y0, w, h;
|
||||
getScreenShape(x0, y0, w, h);
|
||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||
(DWORD)((65535.99 * (x - x0)) / (w - 1)),
|
||||
(DWORD)((65535.99 * (y - y0)) / (h - 1)),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
// raise and show the hider window. take activation.
|
||||
ShowWindow(m_window, SW_SHOWNORMAL);
|
||||
// windows 98 (and Me?) is broken. you cannot set the absolute
|
||||
// position of the mouse except on the primary monitor but you
|
||||
// can do relative moves onto any monitor. this is, in microsoft's
|
||||
// words, "by design." apparently the designers of windows 2000
|
||||
// we're a little less lazy and did it right.
|
||||
//
|
||||
// we use the microsoft recommendation (Q193003): set the absolute
|
||||
// position on the primary monitor, disable mouse acceleration,
|
||||
// relative move the mouse to the final location, restore mouse
|
||||
// acceleration. to avoid one kind of race condition (the user
|
||||
// clicking the mouse or pressing a key between the absolute and
|
||||
// relative move) we'll use SendInput() which guarantees that the
|
||||
// events are delivered uninterrupted. we cannot prevent changes
|
||||
// to the mouse acceleration at inopportune times, though.
|
||||
//
|
||||
// point-to-activate (x-mouse) doesn't seem to be bothered by the
|
||||
// absolute/relative combination. a window over the absolute
|
||||
// position (0,0) does *not* get activated (at least not on win2k)
|
||||
// if the relative move puts the cursor elsewhere. similarly, the
|
||||
// app under the final mouse position does *not* get deactivated
|
||||
// by the absolute move to 0,0.
|
||||
else {
|
||||
// save mouse speed & acceleration
|
||||
int oldSpeed[4];
|
||||
bool accelChanged =
|
||||
SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) &&
|
||||
SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
|
||||
// use 1:1 motion
|
||||
if (accelChanged) {
|
||||
int newSpeed[4] = { 0, 0, 0, 1 };
|
||||
accelChanged =
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) ||
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0);
|
||||
}
|
||||
|
||||
// send events
|
||||
INPUT events[2];
|
||||
events[0].type = INPUT_MOUSE;
|
||||
events[0].mi.dx = 0;
|
||||
events[0].mi.dy = 0;
|
||||
events[0].mi.mouseData = 0;
|
||||
events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[0].mi.time = GetTickCount();
|
||||
events[0].mi.dwExtraInfo = 0;
|
||||
events[1].type = INPUT_MOUSE;
|
||||
events[1].mi.dx = x;
|
||||
events[1].mi.dy = y;
|
||||
events[1].mi.mouseData = 0;
|
||||
events[1].mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
events[1].mi.time = events[0].mi.time;
|
||||
events[1].mi.dwExtraInfo = 0;
|
||||
SendInput(sizeof(events) / sizeof(events[0]),
|
||||
events, sizeof(events[0]));
|
||||
|
||||
// restore mouse speed & acceleration
|
||||
if (accelChanged) {
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::checkClipboard()
|
||||
{
|
||||
// if we think we own the clipboard but we don't then somebody
|
||||
// grabbed the clipboard on this screen without us knowing.
|
||||
// tell the server that this screen grabbed the clipboard.
|
||||
//
|
||||
// this works around bugs in the clipboard viewer chain.
|
||||
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
||||
// messages for no apparent reason and rebooting fixes the
|
||||
// problem. since we don't want a broken clipboard until the
|
||||
// next reboot we do this double check. clipboard ownership
|
||||
// won't be reflected on other screens until we leave but at
|
||||
// least the clipboard itself will work.
|
||||
HWND clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != clipboardOwner) {
|
||||
try {
|
||||
m_clipboardOwner = clipboardOwner;
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
}
|
||||
catch (XBadClient&) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::createWindow()
|
||||
{
|
||||
// save thread id
|
||||
m_threadID = GetCurrentThreadId();
|
||||
|
||||
// note if using multiple monitors
|
||||
m_multimon = isMultimon();
|
||||
|
||||
// get the input desktop and switch to it
|
||||
if (m_is95Family) {
|
||||
if (!openDesktop()) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!switchDesktop(openInputDesktop())) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// poll input desktop to see if it changes (onPreDispatch()
|
||||
// handles WM_TIMER)
|
||||
m_timer = 0;
|
||||
if (!m_is95Family) {
|
||||
m_timer = SetTimer(NULL, 0, 200, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::destroyWindow()
|
||||
{
|
||||
// remove timer
|
||||
if (m_timer != 0) {
|
||||
KillTimer(NULL, m_timer);
|
||||
}
|
||||
|
||||
// release keys that are logically pressed
|
||||
releaseKeys();
|
||||
|
||||
// disconnect from desktop
|
||||
if (m_is95Family) {
|
||||
closeDesktop();
|
||||
}
|
||||
else {
|
||||
switchDesktop(NULL);
|
||||
}
|
||||
|
||||
// clear thread id
|
||||
m_threadID = 0;
|
||||
|
||||
assert(m_window == NULL);
|
||||
assert(m_desk == NULL);
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::installScreenSaver()
|
||||
{
|
||||
getScreenSaver()->disable();
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::uninstallScreenSaver()
|
||||
{
|
||||
getScreenSaver()->enable();
|
||||
}
|
||||
|
||||
bool
|
||||
|
@ -720,7 +867,7 @@ CMSWindowsSecondaryScreen::switchDesktop(HDESK desk)
|
|||
|
||||
// get desktop up to date
|
||||
if (!m_active) {
|
||||
onLeave();
|
||||
showWindow();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
@ -750,89 +897,6 @@ CMSWindowsSecondaryScreen::syncDesktop() const
|
|||
}
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsSecondaryScreen::getCurrentDesktopName() const
|
||||
{
|
||||
return m_deskName;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||
{
|
||||
// move the mouse directly to target position on NT family or if
|
||||
// not using multiple monitors.
|
||||
if (!m_multimon || !m_is95Family) {
|
||||
SInt32 x0, y0, w, h;
|
||||
getScreenShape(x0, y0, w, h);
|
||||
mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
|
||||
(DWORD)((65535.99 * (x - x0)) / (w - 1)),
|
||||
(DWORD)((65535.99 * (y - y0)) / (h - 1)),
|
||||
0, 0);
|
||||
}
|
||||
|
||||
// windows 98 (and Me?) is broken. you cannot set the absolute
|
||||
// position of the mouse except on the primary monitor but you
|
||||
// can do relative moves onto any monitor. this is, in microsoft's
|
||||
// words, "by design." apparently the designers of windows 2000
|
||||
// we're a little less lazy and did it right.
|
||||
//
|
||||
// we use the microsoft recommendation (Q193003): set the absolute
|
||||
// position on the primary monitor, disable mouse acceleration,
|
||||
// relative move the mouse to the final location, restore mouse
|
||||
// acceleration. to avoid one kind of race condition (the user
|
||||
// clicking the mouse or pressing a key between the absolute and
|
||||
// relative move) we'll use SendInput() which guarantees that the
|
||||
// events are delivered uninterrupted. we cannot prevent changes
|
||||
// to the mouse acceleration at inopportune times, though.
|
||||
//
|
||||
// point-to-activate (x-mouse) doesn't seem to be bothered by the
|
||||
// absolute/relative combination. a window over the absolute
|
||||
// position (0,0) does *not* get activated (at least not on win2k)
|
||||
// if the relative move puts the cursor elsewhere. similarly, the
|
||||
// app under the final mouse position does *not* get deactivated
|
||||
// by the absolute move to 0,0.
|
||||
else {
|
||||
// save mouse speed & acceleration
|
||||
int oldSpeed[4];
|
||||
bool accelChanged =
|
||||
SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed,0) &&
|
||||
SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
|
||||
// use 1:1 motion
|
||||
if (accelChanged) {
|
||||
int newSpeed[4] = { 0, 0, 0, 1 };
|
||||
accelChanged =
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) ||
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0);
|
||||
}
|
||||
|
||||
// send events
|
||||
INPUT events[2];
|
||||
events[0].type = INPUT_MOUSE;
|
||||
events[0].mi.dx = 0;
|
||||
events[0].mi.dy = 0;
|
||||
events[0].mi.mouseData = 0;
|
||||
events[0].mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE;
|
||||
events[0].mi.time = GetTickCount();
|
||||
events[0].mi.dwExtraInfo = 0;
|
||||
events[1].type = INPUT_MOUSE;
|
||||
events[1].mi.dx = x;
|
||||
events[1].mi.dy = y;
|
||||
events[1].mi.mouseData = 0;
|
||||
events[1].mi.dwFlags = MOUSEEVENTF_MOVE;
|
||||
events[1].mi.time = events[0].mi.time;
|
||||
events[1].mi.dwExtraInfo = 0;
|
||||
SendInput(sizeof(events) / sizeof(events[0]),
|
||||
events, sizeof(events[0]));
|
||||
|
||||
// restore mouse speed & acceleration
|
||||
if (accelChanged) {
|
||||
SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
|
||||
SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
CMSWindowsSecondaryScreen::isMultimon() const
|
||||
{
|
||||
|
|
|
@ -46,10 +46,8 @@ public:
|
|||
|
||||
protected:
|
||||
// CMSWindowsScreen overrides
|
||||
virtual bool onPreTranslate(MSG*);
|
||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
||||
virtual void onOpenDisplay();
|
||||
virtual void onCloseDisplay();
|
||||
virtual bool onPreDispatch(const CEvent* event);
|
||||
virtual bool onEvent(CEvent* event);
|
||||
virtual CString getCurrentDesktopName() const;
|
||||
|
||||
private:
|
||||
|
@ -62,8 +60,25 @@ private:
|
|||
};
|
||||
typedef std::vector<Keystroke> Keystrokes;
|
||||
|
||||
void onEnter(SInt32 x, SInt32 y);
|
||||
void onLeave();
|
||||
void showWindow();
|
||||
void hideWindow();
|
||||
|
||||
// warp the mouse to the specified position
|
||||
void warpCursor(SInt32 x, SInt32 y);
|
||||
|
||||
// check clipboard ownership and, if necessary, tell the receiver
|
||||
// of a grab.
|
||||
void checkClipboard();
|
||||
|
||||
// create/destroy window
|
||||
// also attach to desktop; this destroys and recreates the window
|
||||
// as necessary.
|
||||
void createWindow();
|
||||
void destroyWindow();
|
||||
|
||||
// start/stop watch for screen saver changes
|
||||
void installScreenSaver();
|
||||
void uninstallScreenSaver();
|
||||
|
||||
// open/close desktop (for windows 95/98/me)
|
||||
bool openDesktop();
|
||||
|
@ -75,9 +90,6 @@ private:
|
|||
// get calling thread to use the input desktop
|
||||
void syncDesktop() const;
|
||||
|
||||
// warp the mouse to the specified position
|
||||
void warpCursor(SInt32 x, SInt32 y);
|
||||
|
||||
// returns true iff there appear to be multiple monitors
|
||||
bool isMultimon() const;
|
||||
|
||||
|
@ -108,6 +120,9 @@ private:
|
|||
// the main loop's thread id
|
||||
DWORD m_threadID;
|
||||
|
||||
// the timer used to check for desktop switching
|
||||
UINT m_timer;
|
||||
|
||||
// the thread id of the last attached thread
|
||||
mutable DWORD m_lastThreadID;
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
#include "CXWindowsSecondaryScreen.h"
|
||||
#include "CClient.h"
|
||||
#include "IScreenReceiver.h"
|
||||
#include "CXWindowsClipboard.h"
|
||||
#include "CXWindowsScreenSaver.h"
|
||||
#include "CXWindowsUtil.h"
|
||||
#include "XScreen.h"
|
||||
#include "CThread.h"
|
||||
#include "CLog.h"
|
||||
#if defined(X_DISPLAY_MISSING)
|
||||
|
@ -26,9 +27,10 @@
|
|||
|
||||
CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) :
|
||||
m_receiver(receiver),
|
||||
m_window(None)
|
||||
m_window(None),
|
||||
m_active(false)
|
||||
{
|
||||
// do nothing
|
||||
assert(m_receiver != NULL);
|
||||
}
|
||||
|
||||
CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen()
|
||||
|
@ -41,135 +43,79 @@ CXWindowsSecondaryScreen::run()
|
|||
{
|
||||
assert(m_window != None);
|
||||
|
||||
for (;;) {
|
||||
// wait for and get the next event
|
||||
XEvent xevent;
|
||||
if (!getEvent(&xevent)) {
|
||||
break;
|
||||
}
|
||||
// change our priority
|
||||
CThread::getCurrentThread().setPriority(-7);
|
||||
|
||||
// handle event
|
||||
switch (xevent.type) {
|
||||
case MappingNotify:
|
||||
{
|
||||
// keyboard mapping changed
|
||||
CDisplayLock display(this);
|
||||
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||
updateKeys(display);
|
||||
updateKeycodeMap(display);
|
||||
updateModifierMap(display);
|
||||
updateModifiers(display);
|
||||
}
|
||||
break;
|
||||
|
||||
case LeaveNotify:
|
||||
{
|
||||
// mouse moved out of hider window somehow. hide the window.
|
||||
assert(m_window != None);
|
||||
CDisplayLock display(this);
|
||||
XUnmapWindow(display, m_window);
|
||||
}
|
||||
break;
|
||||
}
|
||||
// run event loop
|
||||
try {
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
mainLoop();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
}
|
||||
catch (...) {
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::stop()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
doStop();
|
||||
exitMainLoop();
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::open()
|
||||
{
|
||||
assert(m_receiver != NULL);
|
||||
assert(m_window == None);
|
||||
|
||||
// open the display
|
||||
openDisplay();
|
||||
try {
|
||||
// open the display
|
||||
openDisplay();
|
||||
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
// create and prepare our window
|
||||
createWindow();
|
||||
|
||||
// verify the availability of the XTest extension
|
||||
int majorOpcode, firstEvent, firstError;
|
||||
if (!XQueryExtension(display, XTestExtensionName,
|
||||
&majorOpcode, &firstEvent, &firstError)) {
|
||||
throw int(6); // FIXME -- make exception for this
|
||||
// initialize the clipboards; assume primary has all clipboards
|
||||
initClipboards(m_window);
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
grabClipboard(id);
|
||||
}
|
||||
|
||||
// create the cursor hiding window. this window is used to hide the
|
||||
// cursor when it's not on the screen. the window is hidden as soon
|
||||
// as the cursor enters the screen or the display's real cursor is
|
||||
// moved.
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = LeaveWindowMask;
|
||||
attr.do_not_propagate_mask = 0;
|
||||
attr.override_redirect = True;
|
||||
attr.cursor = getBlankCursor();
|
||||
m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0,
|
||||
InputOnly, CopyFromParent,
|
||||
CWDontPropagate | CWEventMask |
|
||||
CWOverrideRedirect | CWCursor,
|
||||
&attr);
|
||||
log((CLOG_DEBUG "window is 0x%08x", m_window));
|
||||
// check for peculiarities
|
||||
// FIXME -- may have to get these from some database
|
||||
m_numLockHalfDuplex = false;
|
||||
m_capsLockHalfDuplex = false;
|
||||
// m_numLockHalfDuplex = true;
|
||||
// m_capsLockHalfDuplex = true;
|
||||
|
||||
// become impervious to server grabs
|
||||
XTestGrabControl(display, True);
|
||||
|
||||
// hide the cursor
|
||||
leaveNoLock(display);
|
||||
|
||||
// initialize the clipboards
|
||||
initClipboards(m_window);
|
||||
// get the display
|
||||
CDisplayLock display(this);
|
||||
|
||||
// update key state
|
||||
updateKeys(display);
|
||||
updateKeycodeMap(display);
|
||||
updateModifierMap(display);
|
||||
updateModifiers(display);
|
||||
|
||||
// disable the screen saver
|
||||
installScreenSaver();
|
||||
}
|
||||
catch (...) {
|
||||
close();
|
||||
throw;
|
||||
}
|
||||
|
||||
// check for peculiarities
|
||||
// FIXME -- may have to get these from some database
|
||||
m_numLockHalfDuplex = false;
|
||||
m_capsLockHalfDuplex = false;
|
||||
// m_numLockHalfDuplex = true;
|
||||
// m_capsLockHalfDuplex = true;
|
||||
|
||||
// assume primary has all clipboards
|
||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||
grabClipboard(id);
|
||||
}
|
||||
|
||||
// disable the screen saver
|
||||
getScreenSaver()->disable();
|
||||
// hide the cursor
|
||||
m_active = true;
|
||||
leave();
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::close()
|
||||
{
|
||||
// release keys that are logically pressed
|
||||
releaseKeys();
|
||||
|
||||
// restore the screen saver settings
|
||||
getScreenSaver()->enable();
|
||||
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
if (display != NULL) {
|
||||
// no longer impervious to server grabs
|
||||
XTestGrabControl(display, False);
|
||||
|
||||
// destroy window
|
||||
XDestroyWindow(display, m_window);
|
||||
}
|
||||
m_window = None;
|
||||
}
|
||||
|
||||
// close the display
|
||||
uninstallScreenSaver();
|
||||
destroyWindow();
|
||||
closeDisplay();
|
||||
}
|
||||
|
||||
|
@ -177,15 +123,14 @@ void
|
|||
CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
||||
{
|
||||
assert(m_window != None);
|
||||
assert(m_active == false);
|
||||
|
||||
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
|
||||
|
||||
CDisplayLock display(this);
|
||||
|
||||
// warp to requested location
|
||||
XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime);
|
||||
XSync(display, False);
|
||||
|
||||
// show cursor
|
||||
XUnmapWindow(display, m_window);
|
||||
// now active
|
||||
m_active = true;
|
||||
|
||||
// update our keyboard state to reflect the local state
|
||||
updateKeys(display);
|
||||
|
@ -202,14 +147,32 @@ CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
|||
if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) {
|
||||
toggleKey(display, XK_Scroll_Lock, m_scrollLockMask);
|
||||
}
|
||||
XSync(display, False);
|
||||
|
||||
// warp to requested location
|
||||
warpCursor(x, y);
|
||||
|
||||
// show mouse
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::leave()
|
||||
{
|
||||
assert(m_window != None);
|
||||
assert(m_active == true);
|
||||
|
||||
log((CLOG_INFO "leaving screen"));
|
||||
|
||||
CDisplayLock display(this);
|
||||
leaveNoLock(display);
|
||||
|
||||
// hide mouse
|
||||
showWindow();
|
||||
|
||||
// not active anymore
|
||||
m_active = false;
|
||||
|
||||
// make sure our idea of clipboard ownership is correct
|
||||
checkClipboard();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -290,8 +253,7 @@ void
|
|||
CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime);
|
||||
XSync(display, False);
|
||||
warpCursor(x, y);
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -366,6 +328,44 @@ CXWindowsSecondaryScreen::getClipboard(ClipboardID id,
|
|||
getDisplayClipboard(id, clipboard);
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsSecondaryScreen::onPreDispatch(const CEvent* event)
|
||||
{
|
||||
// forward to superclass
|
||||
return CXWindowsScreen::onPreDispatch(event);
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsSecondaryScreen::onEvent(CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
XEvent& xevent = event->m_event;
|
||||
|
||||
// handle event
|
||||
switch (xevent.type) {
|
||||
case MappingNotify:
|
||||
{
|
||||
// keyboard mapping changed
|
||||
CDisplayLock display(this);
|
||||
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||
updateKeys(display);
|
||||
updateKeycodeMap(display);
|
||||
updateModifierMap(display);
|
||||
updateModifiers(display);
|
||||
}
|
||||
return true;
|
||||
|
||||
case LeaveNotify:
|
||||
{
|
||||
// mouse moved out of hider window somehow. hide the window.
|
||||
assert(m_window != None);
|
||||
CDisplayLock display(this);
|
||||
hideWindow();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id)
|
||||
{
|
||||
|
@ -374,25 +374,110 @@ CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id)
|
|||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::leaveNoLock(Display* display)
|
||||
CXWindowsSecondaryScreen::showWindow()
|
||||
{
|
||||
assert(display != NULL);
|
||||
assert(m_window != None);
|
||||
|
||||
// move hider window under the mouse (rather than moving the mouse
|
||||
// somewhere else on the screen)
|
||||
int x, y, dummy;
|
||||
unsigned int dummyMask;
|
||||
Window dummyWindow;
|
||||
XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow,
|
||||
&x, &y, &dummy, &dummy, &dummyMask);
|
||||
XMoveWindow(display, m_window, x, y);
|
||||
SInt32 x, y;
|
||||
getCursorPos(x, y);
|
||||
XMoveWindow(getDisplay(), m_window, x, y);
|
||||
|
||||
// raise and show the hider window
|
||||
XMapRaised(display, m_window);
|
||||
// raise and show the hider window. take activation.
|
||||
// FIXME -- take focus?
|
||||
XMapRaised(getDisplay(), m_window);
|
||||
|
||||
/* XXX -- this should have no effect
|
||||
// hide cursor by moving it into the hider window
|
||||
XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0);
|
||||
XWarpPointer(getDisplay(), None, m_window, 0, 0, 0, 0, 0, 0);
|
||||
*/
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::hideWindow()
|
||||
{
|
||||
XUnmapWindow(getDisplay(), m_window);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||
{
|
||||
XTestFakeMotionEvent(getDisplay(), getScreen(), x, y, CurrentTime);
|
||||
XSync(getDisplay(), False);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::checkClipboard()
|
||||
{
|
||||
// do nothing, we're always up to date
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::createWindow()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// verify the availability of the XTest extension
|
||||
int majorOpcode, firstEvent, firstError;
|
||||
if (!XQueryExtension(display, XTestExtensionName,
|
||||
&majorOpcode, &firstEvent, &firstError)) {
|
||||
// FIXME -- subclass exception for more info?
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
|
||||
// cursor hider window attributes. this window is used to hide the
|
||||
// cursor when it's not on the screen. the window is hidden as soon
|
||||
// as the cursor enters the screen or the display's real cursor is
|
||||
// moved.
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = LeaveWindowMask;
|
||||
attr.do_not_propagate_mask = 0;
|
||||
attr.override_redirect = True;
|
||||
attr.cursor = getBlankCursor();
|
||||
|
||||
// create the cursor hider window
|
||||
m_window = XCreateWindow(display, getRoot(),
|
||||
0, 0, 1, 1, 0, 0,
|
||||
InputOnly, CopyFromParent,
|
||||
CWDontPropagate | CWEventMask |
|
||||
CWOverrideRedirect | CWCursor,
|
||||
&attr);
|
||||
if (m_window == None) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
log((CLOG_DEBUG "window is 0x%08x", m_window));
|
||||
|
||||
// become impervious to server grabs
|
||||
XTestGrabControl(display, True);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::destroyWindow()
|
||||
{
|
||||
releaseKeys();
|
||||
|
||||
CDisplayLock display(this);
|
||||
if (display != NULL) {
|
||||
// no longer impervious to server grabs
|
||||
XTestGrabControl(display, False);
|
||||
|
||||
// destroy window
|
||||
if (m_window != None) {
|
||||
XDestroyWindow(display, m_window);
|
||||
m_window = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::installScreenSaver()
|
||||
{
|
||||
getScreenSaver()->disable();
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsSecondaryScreen::uninstallScreenSaver()
|
||||
{
|
||||
getScreenSaver()->enable();
|
||||
}
|
||||
|
||||
unsigned int
|
||||
|
@ -829,17 +914,18 @@ CXWindowsSecondaryScreen::releaseKeys()
|
|||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// key up for each key that's down
|
||||
for (UInt32 i = 0; i < 256; ++i) {
|
||||
if (m_keys[i]) {
|
||||
XTestFakeKeyEvent(display, i, False, CurrentTime);
|
||||
m_keys[i] = false;
|
||||
if (display != NULL) {
|
||||
// key up for each key that's down
|
||||
for (UInt32 i = 0; i < 256; ++i) {
|
||||
if (m_keys[i]) {
|
||||
XTestFakeKeyEvent(display, i, False, CurrentTime);
|
||||
m_keys[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
// update
|
||||
XSync(display, False);
|
||||
}
|
||||
|
||||
// update
|
||||
XSync(display, False);
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
|
|
|
@ -39,6 +39,8 @@ public:
|
|||
|
||||
protected:
|
||||
// CXWindowsScreen overrides
|
||||
virtual bool onPreDispatch(const CEvent* event);
|
||||
virtual bool onEvent(CEvent* event);
|
||||
virtual void onLostClipboard(ClipboardID);
|
||||
|
||||
private:
|
||||
|
@ -60,7 +62,26 @@ private:
|
|||
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
|
||||
typedef std::map<KeyCode, unsigned int> ModifierMap;
|
||||
|
||||
void leaveNoLock(Display*);
|
||||
void showWindow();
|
||||
void hideWindow();
|
||||
|
||||
// warp the mouse to the specified position
|
||||
void warpCursor(SInt32 x, SInt32 y);
|
||||
|
||||
// check clipboard ownership and, if necessary, tell the receiver
|
||||
// of a grab.
|
||||
void checkClipboard();
|
||||
|
||||
// create/destroy window
|
||||
// also attach to desktop; this destroys and recreates the window
|
||||
// as necessary.
|
||||
void createWindow();
|
||||
void destroyWindow();
|
||||
|
||||
// start/stop watch for screen saver changes
|
||||
void installScreenSaver();
|
||||
void uninstallScreenSaver();
|
||||
|
||||
unsigned int mapButton(ButtonID button) const;
|
||||
|
||||
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
|
||||
|
@ -82,6 +103,9 @@ private:
|
|||
IScreenReceiver* m_receiver;
|
||||
Window m_window;
|
||||
|
||||
// m_active is true if this screen has been entered
|
||||
bool m_active;
|
||||
|
||||
// note toggle keys that toggles on up/down (false) or on
|
||||
// transition (true)
|
||||
bool m_numLockHalfDuplex;
|
||||
|
|
|
@ -48,36 +48,44 @@ CMSWindowsScreen::init(HINSTANCE instance)
|
|||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::doRun()
|
||||
CMSWindowsScreen::mainLoop()
|
||||
{
|
||||
// save thread id for posting quit message
|
||||
m_thread = GetCurrentThreadId();
|
||||
|
||||
// event loop
|
||||
CEvent event;
|
||||
event.m_result = 0;
|
||||
for (;;) {
|
||||
// wait for and get the next event
|
||||
MSG msg;
|
||||
getEvent(&msg);
|
||||
// wait for an event in a cancellable way
|
||||
CThread::waitForEvent();
|
||||
GetMessage(&event.m_msg, NULL, 0, 0);
|
||||
|
||||
// handle quit message
|
||||
if (msg.message == WM_QUIT) {
|
||||
if (event.m_msg.message == WM_QUIT) {
|
||||
break;
|
||||
}
|
||||
|
||||
// dispatch message
|
||||
if (!onPreTranslate(&msg)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
if (!onPreDispatch(&event)) {
|
||||
TranslateMessage(&event.m_msg);
|
||||
DispatchMessage(&event.m_msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::doStop()
|
||||
CMSWindowsScreen::exitMainLoop()
|
||||
{
|
||||
PostThreadMessage(m_thread, WM_QUIT, 0, 0);
|
||||
}
|
||||
|
||||
bool
|
||||
CMSWindowsScreen::onPreDispatch(const CEvent*)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::openDisplay()
|
||||
{
|
||||
|
@ -172,9 +180,13 @@ void
|
|||
CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
|
||||
{
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
if (GetCursorPos(&pos)) {
|
||||
x = pos.x;
|
||||
y = pos.y;
|
||||
}
|
||||
else {
|
||||
getCursorCenter(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -244,17 +256,22 @@ CMSWindowsScreen::getScreenSaver() const
|
|||
return m_screenSaver;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsScreen::getEvent(MSG* msg) const
|
||||
{
|
||||
// wait for an event in a cancellable way
|
||||
CThread::waitForEvent();
|
||||
GetMessage(msg, NULL, 0, 0);
|
||||
}
|
||||
|
||||
LRESULT CALLBACK
|
||||
CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
assert(s_screen != NULL);
|
||||
return s_screen->onEvent(hwnd, msg, wParam, lParam);
|
||||
|
||||
CEvent event;
|
||||
event.m_msg.hwnd = hwnd;
|
||||
event.m_msg.message = msg;
|
||||
event.m_msg.wParam = wParam;
|
||||
event.m_msg.lParam = lParam;
|
||||
event.m_result = 0;
|
||||
|
||||
if (s_screen->onEvent(&event)) {
|
||||
return event.m_result;
|
||||
}
|
||||
else {
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,12 @@
|
|||
class CMSWindowsScreenSaver;
|
||||
class CThread;
|
||||
|
||||
class CEvent {
|
||||
public:
|
||||
MSG m_msg;
|
||||
LRESULT m_result;
|
||||
};
|
||||
|
||||
class CMSWindowsScreen {
|
||||
public:
|
||||
CMSWindowsScreen();
|
||||
|
@ -25,11 +31,11 @@ public:
|
|||
static HINSTANCE getInstance();
|
||||
|
||||
protected:
|
||||
// runs an event loop and returns when WM_QUIT is received
|
||||
void doRun();
|
||||
// runs an event loop and returns when exitMainLoop() is called
|
||||
void mainLoop();
|
||||
|
||||
// sends WM_QUIT to force doRun() to return
|
||||
void doStop();
|
||||
// force mainLoop() to return
|
||||
void exitMainLoop();
|
||||
|
||||
// open the X display. calls onOpenDisplay() after opening the display,
|
||||
// getting the screen, its size, and root window. then it starts the
|
||||
|
@ -72,15 +78,14 @@ protected:
|
|||
CMSWindowsScreenSaver*
|
||||
getScreenSaver() const;
|
||||
|
||||
// wait for and get the next message. cancellable.
|
||||
void getEvent(MSG*) const;
|
||||
// called for each event before event translation and dispatch. return
|
||||
// true to skip translation and dispatch. subclasses should call the
|
||||
// superclass's version first and return true if it returns true.
|
||||
virtual bool onPreDispatch(const CEvent* event);
|
||||
|
||||
// called by doRun() to handle an event. return true to skip
|
||||
// event translation and dispatch.
|
||||
virtual bool onPreTranslate(MSG*) = 0;
|
||||
|
||||
// called by window proc. subclass must call DefWindowProc() if necessary
|
||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0;
|
||||
// called by mainLoop(). iff the event was handled return true and
|
||||
// store the result, if any, in m_result, which defaults to zero.
|
||||
virtual bool onEvent(CEvent* event) = 0;
|
||||
|
||||
// called by isCurrentDesktop() to get the current desktop name
|
||||
virtual CString getCurrentDesktopName() const = 0;
|
||||
|
@ -89,6 +94,7 @@ private:
|
|||
// create the transparent cursor
|
||||
void createBlankCursor();
|
||||
|
||||
// our window proc
|
||||
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
|
||||
|
||||
private:
|
||||
|
|
|
@ -120,6 +120,129 @@ CXWindowsScreen::removeTimerNoLock(IJob* job)
|
|||
m_timers.swap(tmp);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsScreen::mainLoop()
|
||||
{
|
||||
// wait for an event in a cancellable way and don't lock the
|
||||
// display while we're waiting.
|
||||
CEvent event;
|
||||
m_mutex.lock();
|
||||
while (!m_stop) {
|
||||
while (!m_stop && XPending(m_display) == 0) {
|
||||
// check timers
|
||||
if (processTimers()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// wait
|
||||
m_mutex.unlock();
|
||||
CThread::sleep(0.01);
|
||||
m_mutex.lock();
|
||||
}
|
||||
if (!m_stop) {
|
||||
// get the event
|
||||
XNextEvent(m_display, &event.m_event);
|
||||
|
||||
// process the event. if unhandled then let the subclass
|
||||
// have a go at it.
|
||||
m_mutex.unlock();
|
||||
if (!onPreDispatch(&event)) {
|
||||
onEvent(&event);
|
||||
}
|
||||
m_mutex.lock();
|
||||
}
|
||||
}
|
||||
m_mutex.unlock();
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsScreen::exitMainLoop()
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreen::onPreDispatch(const CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
const XEvent* xevent = &event->m_event;
|
||||
|
||||
switch (xevent->type) {
|
||||
case SelectionClear:
|
||||
{
|
||||
// we just lost the selection. that means someone else
|
||||
// grabbed the selection so this screen is now the
|
||||
// selection owner. report that to the subclass.
|
||||
ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
|
||||
if (id != kClipboardEnd) {
|
||||
log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
|
||||
m_clipboard[id]->lost(xevent->xselectionclear.time);
|
||||
onLostClipboard(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SelectionNotify:
|
||||
// notification of selection transferred. we shouldn't
|
||||
// get this here because we handle them in the selection
|
||||
// retrieval methods. we'll just delete the property
|
||||
// with the data (satisfying the usual ICCCM protocol).
|
||||
if (xevent->xselection.property != None) {
|
||||
CLock lock(&m_mutex);
|
||||
XDeleteProperty(m_display,
|
||||
xevent->xselection.requestor,
|
||||
xevent->xselection.property);
|
||||
}
|
||||
return true;
|
||||
|
||||
case SelectionRequest:
|
||||
{
|
||||
// somebody is asking for clipboard data
|
||||
ClipboardID id = getClipboardID(
|
||||
xevent->xselectionrequest.selection);
|
||||
if (id != kClipboardEnd) {
|
||||
CLock lock(&m_mutex);
|
||||
m_clipboard[id]->addRequest(
|
||||
xevent->xselectionrequest.owner,
|
||||
xevent->xselectionrequest.requestor,
|
||||
xevent->xselectionrequest.target,
|
||||
xevent->xselectionrequest.time,
|
||||
xevent->xselectionrequest.property);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PropertyNotify:
|
||||
// property delete may be part of a selection conversion
|
||||
if (xevent->xproperty.state == PropertyDelete) {
|
||||
processClipboardRequest(xevent->xproperty.window,
|
||||
xevent->xproperty.time,
|
||||
xevent->xproperty.atom);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case DestroyNotify:
|
||||
// looks like one of the windows that requested a clipboard
|
||||
// transfer has gone bye-bye.
|
||||
destroyClipboardRequest(xevent->xdestroywindow.window);
|
||||
|
||||
// we don't know if the event was handled or not so continue
|
||||
break;
|
||||
}
|
||||
|
||||
// let screen saver have a go
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_screenSaver->onPreDispatch(xevent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsScreen::openDisplay()
|
||||
{
|
||||
|
@ -179,6 +302,12 @@ CXWindowsScreen::closeDisplay()
|
|||
XSetIOErrorHandler(NULL);
|
||||
}
|
||||
|
||||
Display*
|
||||
CXWindowsScreen::getDisplay() const
|
||||
{
|
||||
return m_display;
|
||||
}
|
||||
|
||||
int
|
||||
CXWindowsScreen::getScreen() const
|
||||
{
|
||||
|
@ -294,49 +423,6 @@ CXWindowsScreen::getBlankCursor() const
|
|||
return m_cursor;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreen::getEvent(XEvent* xevent) const
|
||||
{
|
||||
// wait for an event in a cancellable way and don't lock the
|
||||
// display while we're waiting.
|
||||
m_mutex.lock();
|
||||
for (;;) {
|
||||
while (!m_stop && XPending(m_display) == 0) {
|
||||
// check timers
|
||||
if (const_cast<CXWindowsScreen*>(this)->processTimers()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// wait
|
||||
m_mutex.unlock();
|
||||
CThread::sleep(0.01);
|
||||
m_mutex.lock();
|
||||
}
|
||||
if (m_stop) {
|
||||
m_mutex.unlock();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
// get the event
|
||||
XNextEvent(m_display, xevent);
|
||||
|
||||
// process the event. return the event if unhandled.
|
||||
m_mutex.unlock();
|
||||
if (!const_cast<CXWindowsScreen*>(this)->processEvent(xevent)) {
|
||||
return true;
|
||||
}
|
||||
m_mutex.lock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsScreen::doStop()
|
||||
{
|
||||
// caller must have locked display
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
ClipboardID
|
||||
CXWindowsScreen::getClipboardID(Atom selection) const
|
||||
{
|
||||
|
@ -355,84 +441,6 @@ CXWindowsScreen::onUnexpectedClose()
|
|||
// do nothing
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreen::processEvent(XEvent* xevent)
|
||||
{
|
||||
switch (xevent->type) {
|
||||
case SelectionClear:
|
||||
{
|
||||
// we just lost the selection. that means someone else
|
||||
// grabbed the selection so this screen is now the
|
||||
// selection owner. report that to the subclass.
|
||||
ClipboardID id = getClipboardID(xevent->xselectionclear.selection);
|
||||
if (id != kClipboardEnd) {
|
||||
log((CLOG_DEBUG "lost clipboard %d ownership at time %d", id, xevent->xselectionclear.time));
|
||||
m_clipboard[id]->lost(xevent->xselectionclear.time);
|
||||
onLostClipboard(id);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case SelectionNotify:
|
||||
// notification of selection transferred. we shouldn't
|
||||
// get this here because we handle them in the selection
|
||||
// retrieval methods. we'll just delete the property
|
||||
// with the data (satisfying the usual ICCCM protocol).
|
||||
if (xevent->xselection.property != None) {
|
||||
CLock lock(&m_mutex);
|
||||
XDeleteProperty(m_display,
|
||||
xevent->xselection.requestor,
|
||||
xevent->xselection.property);
|
||||
}
|
||||
return true;
|
||||
|
||||
case SelectionRequest:
|
||||
{
|
||||
// somebody is asking for clipboard data
|
||||
ClipboardID id = getClipboardID(
|
||||
xevent->xselectionrequest.selection);
|
||||
if (id != kClipboardEnd) {
|
||||
CLock lock(&m_mutex);
|
||||
m_clipboard[id]->addRequest(
|
||||
xevent->xselectionrequest.owner,
|
||||
xevent->xselectionrequest.requestor,
|
||||
xevent->xselectionrequest.target,
|
||||
xevent->xselectionrequest.time,
|
||||
xevent->xselectionrequest.property);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case PropertyNotify:
|
||||
// property delete may be part of a selection conversion
|
||||
if (xevent->xproperty.state == PropertyDelete) {
|
||||
processClipboardRequest(xevent->xproperty.window,
|
||||
xevent->xproperty.time,
|
||||
xevent->xproperty.atom);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case DestroyNotify:
|
||||
// looks like one of the windows that requested a clipboard
|
||||
// transfer has gone bye-bye.
|
||||
destroyClipboardRequest(xevent->xdestroywindow.window);
|
||||
|
||||
// we don't know if the event was handled or not so continue
|
||||
break;
|
||||
}
|
||||
|
||||
// let screen saver have a go
|
||||
{
|
||||
CLock lock(&m_mutex);
|
||||
m_screenSaver->processEvent(xevent);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreen::processTimers()
|
||||
{
|
||||
|
|
|
@ -19,6 +19,12 @@ class IScreenSaver;
|
|||
class CXWindowsClipboard;
|
||||
class CXWindowsScreenSaver;
|
||||
|
||||
class CEvent {
|
||||
public:
|
||||
XEvent m_event;
|
||||
SInt32 m_result;
|
||||
};
|
||||
|
||||
class CXWindowsScreen {
|
||||
public:
|
||||
CXWindowsScreen();
|
||||
|
@ -47,6 +53,12 @@ protected:
|
|||
};
|
||||
friend class CDisplayLock;
|
||||
|
||||
// runs an event loop and returns when exitMainLoop() is called
|
||||
void mainLoop();
|
||||
|
||||
// force mainLoop() to return
|
||||
void exitMainLoop();
|
||||
|
||||
// open the X display. calls onOpenDisplay() after opening the display,
|
||||
// getting the screen, its size, and root window. then it starts the
|
||||
// event thread.
|
||||
|
@ -57,6 +69,10 @@ protected:
|
|||
// is closed.
|
||||
void closeDisplay();
|
||||
|
||||
// get the Display*. only use this when you know the display is
|
||||
// locked but don't have the CDisplayLock available.
|
||||
Display* getDisplay() const;
|
||||
|
||||
// get the opened screen and its root window. to get the display
|
||||
// create a CDisplayLock object passing this. while the object
|
||||
// exists no other threads may access the display. do not save
|
||||
|
@ -83,13 +99,6 @@ protected:
|
|||
// get a cursor that is transparent everywhere
|
||||
Cursor getBlankCursor() const;
|
||||
|
||||
// wait for and get the next X event. cancellable.
|
||||
bool getEvent(XEvent*) const;
|
||||
|
||||
// cause getEvent() to return false immediately and forever after.
|
||||
// the caller must have locked the display.
|
||||
void doStop();
|
||||
|
||||
// set the contents of the clipboard (i.e. primary selection)
|
||||
bool setDisplayClipboard(ClipboardID,
|
||||
const IClipboard* clipboard);
|
||||
|
@ -102,6 +111,15 @@ protected:
|
|||
CXWindowsScreenSaver*
|
||||
getScreenSaver() const;
|
||||
|
||||
// called for each event before event translation and dispatch. return
|
||||
// true to skip translation and dispatch. subclasses should call the
|
||||
// superclass's version first and return true if it returns true.
|
||||
virtual bool onPreDispatch(const CEvent* event) = 0;
|
||||
|
||||
// called by mainLoop(). iff the event was handled return true and
|
||||
// store the result, if any, in m_result, which defaults to zero.
|
||||
virtual bool onEvent(CEvent* event) = 0;
|
||||
|
||||
// called if the display is unexpectedly closing. default does nothing.
|
||||
virtual void onUnexpectedClose();
|
||||
|
||||
|
@ -115,9 +133,6 @@ private:
|
|||
// remove a timer without locking
|
||||
void removeTimerNoLock(IJob*);
|
||||
|
||||
// internal event processing
|
||||
bool processEvent(XEvent*);
|
||||
|
||||
// process timers
|
||||
bool processTimers();
|
||||
|
||||
|
|
|
@ -97,7 +97,7 @@ CXWindowsScreenSaver::~CXWindowsScreenSaver()
|
|||
}
|
||||
|
||||
bool
|
||||
CXWindowsScreenSaver::processEvent(XEvent* xevent)
|
||||
CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent)
|
||||
{
|
||||
switch (xevent->type) {
|
||||
case CreateNotify:
|
||||
|
|
|
@ -20,8 +20,10 @@ public:
|
|||
CXWindowsScreenSaver(CXWindowsScreen*, Display*);
|
||||
virtual ~CXWindowsScreenSaver();
|
||||
|
||||
// process X event. returns true if the event was handled.
|
||||
bool processEvent(XEvent*);
|
||||
// called for each event before event translation and dispatch. return
|
||||
// true to skip translation and dispatch. subclasses should call the
|
||||
// superclass's version first and return true if it returns true.
|
||||
bool onPreDispatch(const XEvent*);
|
||||
|
||||
// tells this object to send a ClientMessage to the given window
|
||||
// when the screen saver activates or deactivates. only one
|
||||
|
|
|
@ -22,6 +22,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen(
|
|||
m_receiver(receiver),
|
||||
m_primaryReceiver(primaryReceiver),
|
||||
m_threadID(0),
|
||||
m_timer(0),
|
||||
m_desk(NULL),
|
||||
m_deskName(),
|
||||
m_window(NULL),
|
||||
|
@ -95,28 +96,22 @@ CMSWindowsPrimaryScreen::run()
|
|||
// change our priority
|
||||
CThread::getCurrentThread().setPriority(-3);
|
||||
|
||||
// poll input desktop to see if it changes (preTranslateMessage()
|
||||
// handles WM_TIMER)
|
||||
UINT timer = 0;
|
||||
if (!m_is95Family) {
|
||||
SetTimer(NULL, 0, 200, NULL);
|
||||
}
|
||||
|
||||
// run event loop
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
doRun();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
|
||||
// remove timer
|
||||
if (!m_is95Family) {
|
||||
KillTimer(NULL, timer);
|
||||
try {
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
mainLoop();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
}
|
||||
catch (...) {
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::stop()
|
||||
{
|
||||
doStop();
|
||||
exitMainLoop();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -269,50 +264,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
|||
m_y = y;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::warpCursorToCenter()
|
||||
{
|
||||
// warp to center. the extra info tells the hook DLL to send
|
||||
// SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE.
|
||||
SInt32 x, y, w, h;
|
||||
getScreenShape(x, y, w, h);
|
||||
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
|
||||
(DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)),
|
||||
(DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)),
|
||||
0,
|
||||
0x12345678);
|
||||
// FIXME -- ignore mouse until we get warp notification?
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::checkClipboard()
|
||||
{
|
||||
// if we think we own the clipboard but we don't then somebody
|
||||
// grabbed the clipboard on this screen without us knowing.
|
||||
// tell the server that this screen grabbed the clipboard.
|
||||
//
|
||||
// this works around bugs in the clipboard viewer chain.
|
||||
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
||||
// messages for no apparent reason and rebooting fixes the
|
||||
// problem. since we don't want a broken clipboard until the
|
||||
// next reboot we do this double check. clipboard ownership
|
||||
// won't be reflected on other screens until we leave but at
|
||||
// least the clipboard itself will work.
|
||||
HWND clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != clipboardOwner) {
|
||||
try {
|
||||
m_clipboardOwner = clipboardOwner;
|
||||
if (m_clipboardOwner != m_window) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
}
|
||||
catch (XBadClient&) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/,
|
||||
const IClipboard* src)
|
||||
|
@ -408,9 +359,17 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const
|
|||
}
|
||||
|
||||
bool
|
||||
CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||
CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
|
||||
// forward to superclass
|
||||
if (CMSWindowsScreen::onPreTranslate(event)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// handle event
|
||||
const MSG* msg = &event->m_msg;
|
||||
switch (msg->message) {
|
||||
case SYNERGY_MSG_MARK:
|
||||
m_markReceived = msg->wParam;
|
||||
|
@ -567,43 +526,47 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
|||
return false;
|
||||
}
|
||||
|
||||
LRESULT
|
||||
CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||
WPARAM wParam, LPARAM lParam)
|
||||
bool
|
||||
CMSWindowsPrimaryScreen::onEvent(CEvent* event)
|
||||
{
|
||||
switch (msg) {
|
||||
assert(event != NULL);
|
||||
|
||||
const MSG& msg = event->msg;
|
||||
switch (msg.message) {
|
||||
case WM_QUERYENDSESSION:
|
||||
if (m_is95Family) {
|
||||
return TRUE;
|
||||
event->m_result = TRUE;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_ENDSESSION:
|
||||
if (m_is95Family) {
|
||||
if (wParam == TRUE && lParam == 0) {
|
||||
if (msg.wParam == TRUE && msg.lParam == 0) {
|
||||
stop();
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case WM_PAINT:
|
||||
ValidateRect(hwnd, NULL);
|
||||
return 0;
|
||||
ValidateRect(msg.hwnd, NULL);
|
||||
return true;
|
||||
|
||||
case WM_DRAWCLIPBOARD:
|
||||
log((CLOG_DEBUG "clipboard was taken"));
|
||||
|
||||
// first pass it on
|
||||
if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
SendMessage(m_nextClipboardWindow,
|
||||
msg.message, msg.wParam, msg.lParam);
|
||||
}
|
||||
|
||||
// now notify server that somebody changed the clipboard.
|
||||
// skip that if we're the new owner.
|
||||
try {
|
||||
m_clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != m_window) {
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
|
@ -612,28 +575,36 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
|||
// ignore. this can happen if we receive this event
|
||||
// before we've fully started up.
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case WM_CHANGECBCHAIN:
|
||||
if (m_nextClipboardWindow == (HWND)wParam) {
|
||||
m_nextClipboardWindow = (HWND)lParam;
|
||||
if (m_nextClipboardWindow == (HWND)msg.wParam) {
|
||||
m_nextClipboardWindow = (HWND)msg.lParam;
|
||||
}
|
||||
else if (m_nextClipboardWindow != NULL) {
|
||||
SendMessage(m_nextClipboardWindow, msg, wParam, lParam);
|
||||
SendMessage(m_nextClipboardWindow,
|
||||
msg.message, msg.wParam, msg.lParam);
|
||||
}
|
||||
return 0;
|
||||
return true;
|
||||
|
||||
case WM_DISPLAYCHANGE:
|
||||
{
|
||||
// screen resolution may have changed
|
||||
// screen resolution may have changed. get old shape.
|
||||
SInt32 xOld, yOld, wOld, hOld;
|
||||
getScreenShape(xOld, yOld, wOld, hOld);
|
||||
|
||||
// update shape
|
||||
updateScreenShape();
|
||||
SInt32 x, y, w, h;
|
||||
getScreenShape(x, y, w, h);
|
||||
|
||||
// collect new screen info
|
||||
CClientInfo info;
|
||||
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||
getCursorPos(info.m_mx, info.m_my);
|
||||
info.m_zoneSize = getJumpZoneSize();
|
||||
|
||||
// do nothing if resolution hasn't changed
|
||||
if (x != xOld || y != yOld || w != wOld || h != hOld) {
|
||||
if (info.m_x != xOld || info.m_y != yOld ||
|
||||
info.m_w != wOld || info.m_h != hOld) {
|
||||
// recompute center pixel of primary screen
|
||||
getCursorCenter(m_xCenter, m_yCenter);
|
||||
|
||||
|
@ -644,28 +615,46 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
|||
|
||||
// tell hook about resize if not active
|
||||
else {
|
||||
m_setZone(x, y, w, h, getJumpZoneSize());
|
||||
m_setZone(info.m_x, info.m_y,
|
||||
info.m_w, info.m_h, info.m_zoneSize);
|
||||
}
|
||||
|
||||
// send new screen info
|
||||
POINT pos;
|
||||
GetCursorPos(&pos);
|
||||
CClientInfo info;
|
||||
info.m_x = x;
|
||||
info.m_y = y;
|
||||
info.m_w = w;
|
||||
info.m_h = h;
|
||||
info.m_zoneSize = getJumpZoneSize();
|
||||
info.m_mx = pos.x;
|
||||
info.m_my = pos.y;
|
||||
m_receiver->onInfoChanged(info);
|
||||
}
|
||||
|
||||
return 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
return false;
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsPrimaryScreen::getCurrentDesktopName() const
|
||||
{
|
||||
return m_deskName;
|
||||
}
|
||||
|
||||
SInt32
|
||||
CMSWindowsPrimaryScreen::getJumpZoneSize() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::warpCursorToCenter()
|
||||
{
|
||||
// warp to center. the extra info tells the hook DLL to send
|
||||
// SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE.
|
||||
SInt32 x, y, w, h;
|
||||
getScreenShape(x, y, w, h);
|
||||
mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE,
|
||||
(DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)),
|
||||
(DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)),
|
||||
0,
|
||||
0x12345678);
|
||||
// FIXME -- ignore mouse until we get warp notification?
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -743,20 +732,33 @@ CMSWindowsPrimaryScreen::hideWindow()
|
|||
ShowWindow(m_window, SW_HIDE);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CMSWindowsPrimaryScreen::getJumpZoneSize() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::nextMark()
|
||||
CMSWindowsPrimaryScreen::checkClipboard()
|
||||
{
|
||||
// next mark
|
||||
++m_mark;
|
||||
|
||||
// mark point in message queue where the mark was changed
|
||||
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
|
||||
// if we think we own the clipboard but we don't then somebody
|
||||
// grabbed the clipboard on this screen without us knowing.
|
||||
// tell the server that this screen grabbed the clipboard.
|
||||
//
|
||||
// this works around bugs in the clipboard viewer chain.
|
||||
// sometimes NT will simply never send WM_DRAWCLIPBOARD
|
||||
// messages for no apparent reason and rebooting fixes the
|
||||
// problem. since we don't want a broken clipboard until the
|
||||
// next reboot we do this double check. clipboard ownership
|
||||
// won't be reflected on other screens until we leave but at
|
||||
// least the clipboard itself will work.
|
||||
HWND clipboardOwner = GetClipboardOwner();
|
||||
if (m_clipboardOwner != clipboardOwner) {
|
||||
try {
|
||||
m_clipboardOwner = clipboardOwner;
|
||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||
}
|
||||
}
|
||||
catch (XBadClient&) {
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -773,11 +775,23 @@ CMSWindowsPrimaryScreen::createWindow()
|
|||
throw XScreenOpenFailure();
|
||||
}
|
||||
}
|
||||
|
||||
// poll input desktop to see if it changes (preTranslateMessage()
|
||||
// handles WM_TIMER)
|
||||
m_timer = 0;
|
||||
if (!m_is95Family) {
|
||||
m_timer = SetTimer(NULL, 0, 200, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CMSWindowsPrimaryScreen::destroyWindow()
|
||||
{
|
||||
// remove timer
|
||||
if (m_timer != 0) {
|
||||
KillTimer(NULL, m_timer);
|
||||
}
|
||||
|
||||
// disconnect from desktop
|
||||
if (m_is95Family) {
|
||||
closeDesktop();
|
||||
|
@ -977,10 +991,14 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk)
|
|||
return true;
|
||||
}
|
||||
|
||||
CString
|
||||
CMSWindowsPrimaryScreen::getCurrentDesktopName() const
|
||||
void
|
||||
CMSWindowsPrimaryScreen::nextMark()
|
||||
{
|
||||
return m_deskName;
|
||||
// next mark
|
||||
++m_mark;
|
||||
|
||||
// mark point in message queue where the mark was changed
|
||||
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
|
||||
}
|
||||
|
||||
static const KeyID g_virtualKey[] =
|
||||
|
|
|
@ -34,30 +34,25 @@ public:
|
|||
|
||||
protected:
|
||||
// CMSWindowsScreen overrides
|
||||
virtual bool onPreTranslate(MSG*);
|
||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
||||
virtual void onOpenDisplay();
|
||||
virtual void onCloseDisplay();
|
||||
virtual bool onPreTranslate(const CEvent* event);
|
||||
virtual bool onEvent(CEvent* event);
|
||||
virtual CString getCurrentDesktopName() const;
|
||||
|
||||
private:
|
||||
void enterNoWarp();
|
||||
bool showWindow();
|
||||
void hideWindow();
|
||||
|
||||
SInt32 getJumpZoneSize() const;
|
||||
|
||||
// warp mouse to center of primary display (used when computing
|
||||
// motion deltas while mouse is on secondary screen).
|
||||
void warpCursorToCenter();
|
||||
|
||||
void enterNoWarp();
|
||||
bool showWindow();
|
||||
void hideWindow();
|
||||
|
||||
// check clipboard ownership and, if necessary, tell the receiver
|
||||
// of a grab.
|
||||
void checkClipboard();
|
||||
|
||||
// discard posted messages
|
||||
void nextMark();
|
||||
|
||||
// create/destroy window
|
||||
// also attach to desktop; this destroys and recreates the window
|
||||
// as necessary.
|
||||
|
@ -75,6 +70,9 @@ private:
|
|||
// make desk the thread desktop (for windows NT/2000/XP)
|
||||
bool switchDesktop(HDESK desk);
|
||||
|
||||
// discard posted messages
|
||||
void nextMark();
|
||||
|
||||
// key and button queries
|
||||
KeyID mapKey(WPARAM keycode, LPARAM info,
|
||||
KeyModifierMask* maskOut);
|
||||
|
@ -92,6 +90,9 @@ private:
|
|||
// the main loop's thread id
|
||||
DWORD m_threadID;
|
||||
|
||||
// the timer used to check for desktop switching
|
||||
UINT m_timer;
|
||||
|
||||
// the current desk and it's name
|
||||
HDESK m_desk;
|
||||
CString m_deskName;
|
||||
|
|
|
@ -42,199 +42,25 @@ CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen()
|
|||
void
|
||||
CXWindowsPrimaryScreen::run()
|
||||
{
|
||||
for (;;) {
|
||||
// wait for and get the next event
|
||||
XEvent xevent;
|
||||
if (!getEvent(&xevent)) {
|
||||
break;
|
||||
}
|
||||
// change our priority
|
||||
CThread::getCurrentThread().setPriority(-3);
|
||||
|
||||
// handle event
|
||||
switch (xevent.type) {
|
||||
case CreateNotify:
|
||||
{
|
||||
// select events on new window
|
||||
CDisplayLock display(this);
|
||||
selectEvents(display, xevent.xcreatewindow.window);
|
||||
}
|
||||
break;
|
||||
|
||||
case MappingNotify:
|
||||
{
|
||||
// keyboard mapping changed
|
||||
CDisplayLock display(this);
|
||||
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||
updateModifierMap(display);
|
||||
}
|
||||
break;
|
||||
|
||||
case ClientMessage:
|
||||
if (xevent.xclient.message_type == m_atomScreenSaver ||
|
||||
xevent.xclient.format == 32) {
|
||||
// screen saver activation/deactivation event
|
||||
m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0);
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||
const KeyID key = mapKey(&xevent.xkey);
|
||||
if (key != kKeyNone) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
if (key == XK_Caps_Lock && m_capsLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock);
|
||||
}
|
||||
else if (key == XK_Num_Lock && m_numLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyRelease:
|
||||
{
|
||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||
const KeyID key = mapKey(&xevent.xkey);
|
||||
if (key != kKeyNone) {
|
||||
// check if this is a key repeat by getting the next
|
||||
// KeyPress event that has the same key and time as
|
||||
// this release event, if any. first prepare the
|
||||
// filter info.
|
||||
CKeyEventInfo filter;
|
||||
filter.m_event = KeyPress;
|
||||
filter.m_window = xevent.xkey.window;
|
||||
filter.m_time = xevent.xkey.time;
|
||||
filter.m_keycode = xevent.xkey.keycode;
|
||||
|
||||
// now check for event
|
||||
XEvent xevent2;
|
||||
CDisplayLock display(this);
|
||||
if (XCheckIfEvent(display, &xevent2,
|
||||
&CXWindowsPrimaryScreen::findKeyEvent,
|
||||
(XPointer)&filter) != True) {
|
||||
// no press event follows so it's a plain release
|
||||
log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
if (key == XK_Caps_Lock && m_capsLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
}
|
||||
else if (key == XK_Num_Lock && m_numLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
}
|
||||
m_primaryReceiver->onKeyUp(key, mask);
|
||||
}
|
||||
else {
|
||||
// found a press event following so it's a repeat.
|
||||
// we could attempt to count the already queued
|
||||
// repeats but we'll just send a repeat of 1.
|
||||
// note that we discard the press event.
|
||||
log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
m_primaryReceiver->onKeyRepeat(key, mask, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonPress:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button));
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
m_primaryReceiver->onMouseDown(button);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case ButtonRelease:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button));
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
m_primaryReceiver->onMouseUp(button);
|
||||
}
|
||||
else if (xevent.xbutton.button == 4) {
|
||||
// wheel forward (away from user)
|
||||
m_primaryReceiver->onMouseWheel(120);
|
||||
}
|
||||
else if (xevent.xbutton.button == 5) {
|
||||
// wheel backward (toward user)
|
||||
m_primaryReceiver->onMouseWheel(-120);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root));
|
||||
|
||||
// compute motion delta (relative to the last known
|
||||
// mouse position)
|
||||
SInt32 x = xevent.xmotion.x_root - m_x;
|
||||
SInt32 y = xevent.xmotion.y_root - m_y;
|
||||
|
||||
// save position to compute delta of next motion
|
||||
m_x = xevent.xmotion.x_root;
|
||||
m_y = xevent.xmotion.y_root;
|
||||
|
||||
if (xevent.xmotion.send_event) {
|
||||
// we warped the mouse. discard events until we
|
||||
// find the matching sent event. see
|
||||
// warpCursorNoFlush() for where the events are
|
||||
// sent. we discard the matching sent event and
|
||||
// can be sure we've skipped the warp event.
|
||||
CDisplayLock display(this);
|
||||
do {
|
||||
XMaskEvent(display, PointerMotionMask, &xevent);
|
||||
} while (!xevent.xmotion.send_event);
|
||||
}
|
||||
else if (!m_active) {
|
||||
// motion on primary screen
|
||||
m_primaryReceiver->onMouseMovePrimary(m_x, m_y);
|
||||
}
|
||||
else {
|
||||
// motion on secondary screen. warp mouse back to
|
||||
// center.
|
||||
//
|
||||
// my lombard (powerbook g3) running linux and
|
||||
// using the adbmouse driver has two problems:
|
||||
// first, the driver only sends motions of +/-2
|
||||
// pixels and, second, it seems to discard some
|
||||
// physical input after a warp. the former isn't a
|
||||
// big deal (we're just limited to every other
|
||||
// pixel) but the latter is a PITA. to work around
|
||||
// it we only warp when the mouse has moved more
|
||||
// than s_size pixels from the center.
|
||||
static const SInt32 s_size = 32;
|
||||
if (xevent.xmotion.x_root - m_xCenter < -s_size ||
|
||||
xevent.xmotion.x_root - m_xCenter > s_size ||
|
||||
xevent.xmotion.y_root - m_yCenter < -s_size ||
|
||||
xevent.xmotion.y_root - m_yCenter > s_size) {
|
||||
CDisplayLock display(this);
|
||||
warpCursorNoFlush(display, m_xCenter, m_yCenter);
|
||||
}
|
||||
|
||||
// send event if mouse moved. do this after warping
|
||||
// back to center in case the motion takes us onto
|
||||
// the primary screen. if we sent the event first
|
||||
// in that case then the warp would happen after
|
||||
// warping to the primary screen's enter position,
|
||||
// effectively overriding it.
|
||||
if (x != 0 || y != 0) {
|
||||
m_primaryReceiver->onMouseMoveSecondary(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
// run event loop
|
||||
try {
|
||||
log((CLOG_INFO "entering event loop"));
|
||||
mainLoop();
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
}
|
||||
catch (...) {
|
||||
log((CLOG_INFO "exiting event loop"));
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::stop()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
doStop();
|
||||
exitMainLoop();
|
||||
}
|
||||
|
||||
void
|
||||
|
@ -387,196 +213,6 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
|||
m_y = y;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::warpCursorToCenter()
|
||||
{
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::warpCursorNoFlush(
|
||||
Display* display, SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(display != NULL);
|
||||
assert(m_window != None);
|
||||
|
||||
// send an event that we can recognize before the mouse warp
|
||||
XEvent eventBefore;
|
||||
eventBefore.type = MotionNotify;
|
||||
eventBefore.xmotion.display = display;
|
||||
eventBefore.xmotion.window = m_window;
|
||||
eventBefore.xmotion.root = getRoot();
|
||||
eventBefore.xmotion.subwindow = m_window;
|
||||
eventBefore.xmotion.time = CurrentTime;
|
||||
eventBefore.xmotion.x = x;
|
||||
eventBefore.xmotion.y = y;
|
||||
eventBefore.xmotion.x_root = x;
|
||||
eventBefore.xmotion.y_root = y;
|
||||
eventBefore.xmotion.state = 0;
|
||||
eventBefore.xmotion.is_hint = False;
|
||||
eventBefore.xmotion.same_screen = True;
|
||||
XEvent eventAfter = eventBefore;
|
||||
XSendEvent(display, m_window, False, 0, &eventBefore);
|
||||
|
||||
// warp mouse
|
||||
XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y);
|
||||
|
||||
// send an event that we can recognize after the mouse warp
|
||||
XSendEvent(display, m_window, False, 0, &eventAfter);
|
||||
XSync(display, False);
|
||||
|
||||
log((CLOG_DEBUG2 "warped to %d,%d", x, y));
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::checkClipboard()
|
||||
{
|
||||
// do nothing, we're always up to date
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::enterNoWarp()
|
||||
{
|
||||
m_active = false;
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsPrimaryScreen::showWindow()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// raise and show the input window
|
||||
XMapRaised(display, m_window);
|
||||
|
||||
// grab the mouse and keyboard. keep trying until we get them.
|
||||
// if we can't grab one after grabbing the other then ungrab
|
||||
// and wait before retrying. give up after s_timeout seconds.
|
||||
static const double s_timeout = 1.0;
|
||||
int result;
|
||||
CStopwatch timer;
|
||||
do {
|
||||
// keyboard first
|
||||
do {
|
||||
result = XGrabKeyboard(display, m_window, True,
|
||||
GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess) {
|
||||
log((CLOG_DEBUG2 "waiting to grab keyboard"));
|
||||
CThread::sleep(0.05);
|
||||
if (timer.getTime() >= s_timeout) {
|
||||
log((CLOG_DEBUG2 "grab keyboard timed out"));
|
||||
XUnmapWindow(display, m_window);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (result != GrabSuccess);
|
||||
log((CLOG_DEBUG2 "grabbed keyboard"));
|
||||
|
||||
// now the mouse
|
||||
result = XGrabPointer(display, m_window, True, 0,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
m_window, None, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess) {
|
||||
// back off to avoid grab deadlock
|
||||
XUngrabKeyboard(display, CurrentTime);
|
||||
log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer"));
|
||||
CThread::sleep(0.05);
|
||||
if (timer.getTime() >= s_timeout) {
|
||||
log((CLOG_DEBUG2 "grab pointer timed out"));
|
||||
XUnmapWindow(display, m_window);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (result != GrabSuccess);
|
||||
log((CLOG_DEBUG1 "grabbed pointer and keyboard"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::hideWindow()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// unmap the grab window. this also ungrabs the mouse and keyboard.
|
||||
XUnmapWindow(display, m_window);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CXWindowsPrimaryScreen::getJumpZoneSize() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::createWindow()
|
||||
{
|
||||
assert(m_window == None);
|
||||
|
||||
// get size of screen
|
||||
SInt32 x, y, w, h;
|
||||
getScreenShape(x, y, w, h);
|
||||
|
||||
// grab window attributes. this window is used to capture user
|
||||
// input when the user is focused on another client. don't let
|
||||
// the window manager mess with it.
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = PointerMotionMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
KeymapStateMask | PropertyChangeMask;
|
||||
attr.do_not_propagate_mask = 0;
|
||||
attr.override_redirect = True;
|
||||
attr.cursor = getBlankCursor();
|
||||
|
||||
// create the grab window
|
||||
CDisplayLock display(this);
|
||||
m_window = XCreateWindow(display, getRoot(),
|
||||
x, y, w, h, 0, 0,
|
||||
InputOnly, CopyFromParent,
|
||||
CWDontPropagate | CWEventMask |
|
||||
CWOverrideRedirect | CWCursor,
|
||||
&attr);
|
||||
if (m_window == None) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
log((CLOG_DEBUG "window is 0x%08x", m_window));
|
||||
|
||||
// start watching for events on other windows
|
||||
selectEvents(display, getRoot());
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::destroyWindow()
|
||||
{
|
||||
// display can be NULL if the server unexpectedly disconnected
|
||||
CDisplayLock display(this);
|
||||
if (display != NULL && m_window != None) {
|
||||
XDestroyWindow(display, m_window);
|
||||
}
|
||||
m_window = None;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::installScreenSaver()
|
||||
{
|
||||
assert(getScreenSaver() != NULL);
|
||||
|
||||
getScreenSaver()->setNotify(m_window);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::uninstallScreenSaver()
|
||||
{
|
||||
// stop being notified of screen saver activation/deactivation
|
||||
if (getScreenSaver() != NULL) {
|
||||
getScreenSaver()->setNotify(None);
|
||||
}
|
||||
m_atomScreenSaver = None;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::setClipboard(ClipboardID id,
|
||||
const IClipboard* clipboard)
|
||||
|
@ -659,6 +295,202 @@ CXWindowsPrimaryScreen::isLockedToScreen() const
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsPrimaryScreen::onPreDispatch(const CEvent* event)
|
||||
{
|
||||
// forward to superclass
|
||||
return CXWindowsScreen::onPreDispatch(event);
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsPrimaryScreen::onEvent(CEvent* event)
|
||||
{
|
||||
assert(event != NULL);
|
||||
XEvent& xevent = event->m_event;
|
||||
|
||||
// handle event
|
||||
switch (xevent.type) {
|
||||
case CreateNotify:
|
||||
{
|
||||
// select events on new window
|
||||
CDisplayLock display(this);
|
||||
selectEvents(display, xevent.xcreatewindow.window);
|
||||
}
|
||||
return true;
|
||||
|
||||
case MappingNotify:
|
||||
{
|
||||
// keyboard mapping changed
|
||||
CDisplayLock display(this);
|
||||
XRefreshKeyboardMapping(&xevent.xmapping);
|
||||
updateModifierMap(display);
|
||||
}
|
||||
return true;
|
||||
|
||||
case ClientMessage:
|
||||
if (xevent.xclient.message_type == m_atomScreenSaver ||
|
||||
xevent.xclient.format == 32) {
|
||||
// screen saver activation/deactivation event
|
||||
m_primaryReceiver->onScreenSaver(xevent.xclient.data.l[0] != 0);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
|
||||
case KeyPress:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: KeyPress code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||
const KeyID key = mapKey(&xevent.xkey);
|
||||
if (key != kKeyNone) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
if (key == XK_Caps_Lock && m_capsLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock);
|
||||
}
|
||||
else if (key == XK_Num_Lock && m_numLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case KeyRelease:
|
||||
{
|
||||
const KeyModifierMask mask = mapModifier(xevent.xkey.state);
|
||||
const KeyID key = mapKey(&xevent.xkey);
|
||||
if (key != kKeyNone) {
|
||||
// check if this is a key repeat by getting the next
|
||||
// KeyPress event that has the same key and time as
|
||||
// this release event, if any. first prepare the
|
||||
// filter info.
|
||||
CKeyEventInfo filter;
|
||||
filter.m_event = KeyPress;
|
||||
filter.m_window = xevent.xkey.window;
|
||||
filter.m_time = xevent.xkey.time;
|
||||
filter.m_keycode = xevent.xkey.keycode;
|
||||
|
||||
// now check for event
|
||||
XEvent xevent2;
|
||||
CDisplayLock display(this);
|
||||
if (XCheckIfEvent(display, &xevent2,
|
||||
&CXWindowsPrimaryScreen::findKeyEvent,
|
||||
(XPointer)&filter) != True) {
|
||||
// no press event follows so it's a plain release
|
||||
log((CLOG_DEBUG1 "event: KeyRelease code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
if (key == XK_Caps_Lock && m_capsLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
}
|
||||
else if (key == XK_Num_Lock && m_numLockHalfDuplex) {
|
||||
m_primaryReceiver->onKeyDown(key, mask);
|
||||
}
|
||||
m_primaryReceiver->onKeyUp(key, mask);
|
||||
}
|
||||
else {
|
||||
// found a press event following so it's a repeat.
|
||||
// we could attempt to count the already queued
|
||||
// repeats but we'll just send a repeat of 1.
|
||||
// note that we discard the press event.
|
||||
log((CLOG_DEBUG1 "event: repeat code=%d, state=0x%04x", xevent.xkey.keycode, xevent.xkey.state));
|
||||
m_primaryReceiver->onKeyRepeat(key, mask, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case ButtonPress:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button));
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
m_primaryReceiver->onMouseDown(button);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case ButtonRelease:
|
||||
{
|
||||
log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button));
|
||||
const ButtonID button = mapButton(xevent.xbutton.button);
|
||||
if (button != kButtonNone) {
|
||||
m_primaryReceiver->onMouseUp(button);
|
||||
}
|
||||
else if (xevent.xbutton.button == 4) {
|
||||
// wheel forward (away from user)
|
||||
m_primaryReceiver->onMouseWheel(120);
|
||||
}
|
||||
else if (xevent.xbutton.button == 5) {
|
||||
// wheel backward (toward user)
|
||||
m_primaryReceiver->onMouseWheel(-120);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
case MotionNotify:
|
||||
{
|
||||
log((CLOG_DEBUG2 "event: MotionNotify %d,%d", xevent.xmotion.x_root, xevent.xmotion.y_root));
|
||||
|
||||
// compute motion delta (relative to the last known
|
||||
// mouse position)
|
||||
SInt32 x = xevent.xmotion.x_root - m_x;
|
||||
SInt32 y = xevent.xmotion.y_root - m_y;
|
||||
|
||||
// save position to compute delta of next motion
|
||||
m_x = xevent.xmotion.x_root;
|
||||
m_y = xevent.xmotion.y_root;
|
||||
|
||||
if (xevent.xmotion.send_event) {
|
||||
// we warped the mouse. discard events until we
|
||||
// find the matching sent event. see
|
||||
// warpCursorNoFlush() for where the events are
|
||||
// sent. we discard the matching sent event and
|
||||
// can be sure we've skipped the warp event.
|
||||
CDisplayLock display(this);
|
||||
do {
|
||||
XMaskEvent(display, PointerMotionMask, &xevent);
|
||||
} while (!xevent.xmotion.send_event);
|
||||
}
|
||||
else if (!m_active) {
|
||||
// motion on primary screen
|
||||
m_primaryReceiver->onMouseMovePrimary(m_x, m_y);
|
||||
}
|
||||
else {
|
||||
// motion on secondary screen. warp mouse back to
|
||||
// center.
|
||||
//
|
||||
// my lombard (powerbook g3) running linux and
|
||||
// using the adbmouse driver has two problems:
|
||||
// first, the driver only sends motions of +/-2
|
||||
// pixels and, second, it seems to discard some
|
||||
// physical input after a warp. the former isn't a
|
||||
// big deal (we're just limited to every other
|
||||
// pixel) but the latter is a PITA. to work around
|
||||
// it we only warp when the mouse has moved more
|
||||
// than s_size pixels from the center.
|
||||
static const SInt32 s_size = 32;
|
||||
if (xevent.xmotion.x_root - m_xCenter < -s_size ||
|
||||
xevent.xmotion.x_root - m_xCenter > s_size ||
|
||||
xevent.xmotion.y_root - m_yCenter < -s_size ||
|
||||
xevent.xmotion.y_root - m_yCenter > s_size) {
|
||||
CDisplayLock display(this);
|
||||
warpCursorNoFlush(display, m_xCenter, m_yCenter);
|
||||
}
|
||||
|
||||
// send event if mouse moved. do this after warping
|
||||
// back to center in case the motion takes us onto
|
||||
// the primary screen. if we sent the event first
|
||||
// in that case then the warp would happen after
|
||||
// warping to the primary screen's enter position,
|
||||
// effectively overriding it.
|
||||
if (x != 0 || y != 0) {
|
||||
m_primaryReceiver->onMouseMoveSecondary(x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::onUnexpectedClose()
|
||||
{
|
||||
|
@ -673,6 +505,196 @@ CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id)
|
|||
m_receiver->onGrabClipboard(id);
|
||||
}
|
||||
|
||||
SInt32
|
||||
CXWindowsPrimaryScreen::getJumpZoneSize() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::warpCursorToCenter()
|
||||
{
|
||||
warpCursor(m_xCenter, m_yCenter);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::warpCursorNoFlush(
|
||||
Display* display, SInt32 x, SInt32 y)
|
||||
{
|
||||
assert(display != NULL);
|
||||
assert(m_window != None);
|
||||
|
||||
// send an event that we can recognize before the mouse warp
|
||||
XEvent eventBefore;
|
||||
eventBefore.type = MotionNotify;
|
||||
eventBefore.xmotion.display = display;
|
||||
eventBefore.xmotion.window = m_window;
|
||||
eventBefore.xmotion.root = getRoot();
|
||||
eventBefore.xmotion.subwindow = m_window;
|
||||
eventBefore.xmotion.time = CurrentTime;
|
||||
eventBefore.xmotion.x = x;
|
||||
eventBefore.xmotion.y = y;
|
||||
eventBefore.xmotion.x_root = x;
|
||||
eventBefore.xmotion.y_root = y;
|
||||
eventBefore.xmotion.state = 0;
|
||||
eventBefore.xmotion.is_hint = False;
|
||||
eventBefore.xmotion.same_screen = True;
|
||||
XEvent eventAfter = eventBefore;
|
||||
XSendEvent(display, m_window, False, 0, &eventBefore);
|
||||
|
||||
// warp mouse
|
||||
XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y);
|
||||
|
||||
// send an event that we can recognize after the mouse warp
|
||||
XSendEvent(display, m_window, False, 0, &eventAfter);
|
||||
XSync(display, False);
|
||||
|
||||
log((CLOG_DEBUG2 "warped to %d,%d", x, y));
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::enterNoWarp()
|
||||
{
|
||||
m_active = false;
|
||||
hideWindow();
|
||||
}
|
||||
|
||||
bool
|
||||
CXWindowsPrimaryScreen::showWindow()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// raise and show the input window
|
||||
XMapRaised(display, m_window);
|
||||
|
||||
// grab the mouse and keyboard. keep trying until we get them.
|
||||
// if we can't grab one after grabbing the other then ungrab
|
||||
// and wait before retrying. give up after s_timeout seconds.
|
||||
static const double s_timeout = 1.0;
|
||||
int result;
|
||||
CStopwatch timer;
|
||||
do {
|
||||
// keyboard first
|
||||
do {
|
||||
result = XGrabKeyboard(display, m_window, True,
|
||||
GrabModeAsync, GrabModeAsync, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess) {
|
||||
log((CLOG_DEBUG2 "waiting to grab keyboard"));
|
||||
CThread::sleep(0.05);
|
||||
if (timer.getTime() >= s_timeout) {
|
||||
log((CLOG_DEBUG2 "grab keyboard timed out"));
|
||||
XUnmapWindow(display, m_window);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (result != GrabSuccess);
|
||||
log((CLOG_DEBUG2 "grabbed keyboard"));
|
||||
|
||||
// now the mouse
|
||||
result = XGrabPointer(display, m_window, True, 0,
|
||||
GrabModeAsync, GrabModeAsync,
|
||||
m_window, None, CurrentTime);
|
||||
assert(result != GrabNotViewable);
|
||||
if (result != GrabSuccess) {
|
||||
// back off to avoid grab deadlock
|
||||
XUngrabKeyboard(display, CurrentTime);
|
||||
log((CLOG_DEBUG2 "ungrabbed keyboard, waiting to grab pointer"));
|
||||
CThread::sleep(0.05);
|
||||
if (timer.getTime() >= s_timeout) {
|
||||
log((CLOG_DEBUG2 "grab pointer timed out"));
|
||||
XUnmapWindow(display, m_window);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} while (result != GrabSuccess);
|
||||
log((CLOG_DEBUG1 "grabbed pointer and keyboard"));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::hideWindow()
|
||||
{
|
||||
CDisplayLock display(this);
|
||||
|
||||
// unmap the grab window. this also ungrabs the mouse and keyboard.
|
||||
XUnmapWindow(display, m_window);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::checkClipboard()
|
||||
{
|
||||
// do nothing, we're always up to date
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::createWindow()
|
||||
{
|
||||
assert(m_window == None);
|
||||
|
||||
// get size of screen
|
||||
SInt32 x, y, w, h;
|
||||
getScreenShape(x, y, w, h);
|
||||
|
||||
// grab window attributes. this window is used to capture user
|
||||
// input when the user is focused on another client. don't let
|
||||
// the window manager mess with it.
|
||||
XSetWindowAttributes attr;
|
||||
attr.event_mask = PointerMotionMask |
|
||||
ButtonPressMask | ButtonReleaseMask |
|
||||
KeyPressMask | KeyReleaseMask |
|
||||
KeymapStateMask | PropertyChangeMask;
|
||||
attr.do_not_propagate_mask = 0;
|
||||
attr.override_redirect = True;
|
||||
attr.cursor = getBlankCursor();
|
||||
|
||||
// create the grab window
|
||||
CDisplayLock display(this);
|
||||
m_window = XCreateWindow(display, getRoot(),
|
||||
x, y, w, h, 0, 0,
|
||||
InputOnly, CopyFromParent,
|
||||
CWDontPropagate | CWEventMask |
|
||||
CWOverrideRedirect | CWCursor,
|
||||
&attr);
|
||||
if (m_window == None) {
|
||||
throw XScreenOpenFailure();
|
||||
}
|
||||
log((CLOG_DEBUG "window is 0x%08x", m_window));
|
||||
|
||||
// start watching for events on other windows
|
||||
selectEvents(display, getRoot());
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::destroyWindow()
|
||||
{
|
||||
// display can be NULL if the server unexpectedly disconnected
|
||||
CDisplayLock display(this);
|
||||
if (display != NULL && m_window != None) {
|
||||
XDestroyWindow(display, m_window);
|
||||
}
|
||||
m_window = None;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::installScreenSaver()
|
||||
{
|
||||
assert(getScreenSaver() != NULL);
|
||||
|
||||
getScreenSaver()->setNotify(m_window);
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::uninstallScreenSaver()
|
||||
{
|
||||
// stop being notified of screen saver activation/deactivation
|
||||
if (getScreenSaver() != NULL) {
|
||||
getScreenSaver()->setNotify(None);
|
||||
}
|
||||
m_atomScreenSaver = None;
|
||||
}
|
||||
|
||||
void
|
||||
CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const
|
||||
{
|
||||
|
|
|
@ -30,23 +30,22 @@ public:
|
|||
|
||||
protected:
|
||||
// CXWindowsScreen overrides
|
||||
virtual bool onPreDispatch(const CEvent* event);
|
||||
virtual bool onEvent(CEvent* event);
|
||||
virtual void onUnexpectedClose();
|
||||
virtual void onLostClipboard(ClipboardID);
|
||||
|
||||
private:
|
||||
void selectEvents(Display*, Window) const;
|
||||
void doSelectEvents(Display*, Window) const;
|
||||
|
||||
void enterNoWarp();
|
||||
bool showWindow();
|
||||
void hideWindow();
|
||||
|
||||
SInt32 getJumpZoneSize() const;
|
||||
|
||||
void warpCursorToCenter();
|
||||
void warpCursorNoFlush(Display*,
|
||||
SInt32 xAbsolute, SInt32 yAbsolute);
|
||||
|
||||
void enterNoWarp();
|
||||
bool showWindow();
|
||||
void hideWindow();
|
||||
|
||||
// check clipboard ownership and, if necessary, tell the receiver
|
||||
// of a grab.
|
||||
void checkClipboard();
|
||||
|
@ -59,6 +58,9 @@ private:
|
|||
void installScreenSaver();
|
||||
void uninstallScreenSaver();
|
||||
|
||||
void selectEvents(Display*, Window) const;
|
||||
void doSelectEvents(Display*, Window) const;
|
||||
|
||||
KeyModifierMask mapModifier(unsigned int state) const;
|
||||
KeyID mapKey(XKeyEvent*) const;
|
||||
ButtonID mapButton(unsigned int button) const;
|
||||
|
|
Loading…
Reference in New Issue