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
|
void
|
||||||
CMSWindowsSecondaryScreen::run()
|
CMSWindowsSecondaryScreen::run()
|
||||||
{
|
{
|
||||||
|
assert(m_window != NULL);
|
||||||
|
|
||||||
// must call run() from same thread as open()
|
// must call run() from same thread as open()
|
||||||
assert(m_threadID == GetCurrentThreadId());
|
assert(m_threadID == GetCurrentThreadId());
|
||||||
|
|
||||||
// change our priority
|
// change our priority
|
||||||
CThread::getCurrentThread().setPriority(-7);
|
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
|
// run event loop
|
||||||
|
try {
|
||||||
log((CLOG_INFO "entering event loop"));
|
log((CLOG_INFO "entering event loop"));
|
||||||
doRun();
|
mainLoop();
|
||||||
log((CLOG_INFO "exiting event loop"));
|
log((CLOG_INFO "exiting event loop"));
|
||||||
|
}
|
||||||
// remove timer
|
catch (...) {
|
||||||
if (!m_is95Family) {
|
log((CLOG_INFO "exiting event loop"));
|
||||||
KillTimer(NULL, timer);
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsSecondaryScreen::stop()
|
CMSWindowsSecondaryScreen::stop()
|
||||||
{
|
{
|
||||||
doStop();
|
exitMainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsSecondaryScreen::open()
|
CMSWindowsSecondaryScreen::open()
|
||||||
{
|
{
|
||||||
|
assert(m_window == NULL);
|
||||||
|
|
||||||
|
try {
|
||||||
// open the display
|
// open the display
|
||||||
openDisplay();
|
openDisplay();
|
||||||
|
|
||||||
// update key state
|
// create and prepare our window
|
||||||
updateKeys();
|
createWindow();
|
||||||
updateModifiers();
|
|
||||||
|
|
||||||
// assume primary has all clipboards
|
// initialize the clipboards; assume primary has all clipboards
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||||
grabClipboard(id);
|
grabClipboard(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// get keyboard state
|
||||||
|
updateKeys();
|
||||||
|
updateModifiers();
|
||||||
|
|
||||||
// disable the screen saver
|
// disable the screen saver
|
||||||
getScreenSaver()->disable();
|
installScreenSaver();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
// hide the cursor
|
// hide the cursor
|
||||||
m_active = true;
|
m_active = true;
|
||||||
|
@ -106,13 +113,8 @@ CMSWindowsSecondaryScreen::open()
|
||||||
void
|
void
|
||||||
CMSWindowsSecondaryScreen::close()
|
CMSWindowsSecondaryScreen::close()
|
||||||
{
|
{
|
||||||
// release keys that are logically pressed
|
uninstallScreenSaver();
|
||||||
releaseKeys();
|
destroyWindow();
|
||||||
|
|
||||||
// restore the screen saver settings
|
|
||||||
getScreenSaver()->enable();
|
|
||||||
|
|
||||||
// close the display
|
|
||||||
closeDisplay();
|
closeDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,8 +147,11 @@ CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
||||||
toggleKey(VK_SCROLL, KeyModifierScrollLock);
|
toggleKey(VK_SCROLL, KeyModifierScrollLock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// warp to requested location
|
||||||
|
warpCursor(x, y);
|
||||||
|
|
||||||
// show mouse
|
// show mouse
|
||||||
onEnter(x, y);
|
hideWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -161,30 +166,13 @@ CMSWindowsSecondaryScreen::leave()
|
||||||
syncDesktop();
|
syncDesktop();
|
||||||
|
|
||||||
// hide mouse
|
// hide mouse
|
||||||
onLeave();
|
showWindow();
|
||||||
|
|
||||||
// not active anymore
|
// not active anymore
|
||||||
m_active = false;
|
m_active = false;
|
||||||
|
|
||||||
// if we think we own the clipboard but we don't then somebody
|
// make sure our idea of clipboard ownership is correct
|
||||||
// grabbed the clipboard on this screen without us knowing.
|
checkClipboard();
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -402,15 +390,7 @@ CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const
|
||||||
assert(m_window != NULL);
|
assert(m_window != NULL);
|
||||||
syncDesktop();
|
syncDesktop();
|
||||||
|
|
||||||
POINT pos;
|
getCursorPos(x, y);
|
||||||
if (GetCursorPos(&pos)) {
|
|
||||||
x = pos.x;
|
|
||||||
y = pos.y;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
x = 0;
|
|
||||||
y = 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -437,52 +417,18 @@ CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/,
|
||||||
CClipboard::copy(dst, &src);
|
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
|
bool
|
||||||
CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event)
|
||||||
{
|
{
|
||||||
|
assert(event != NULL);
|
||||||
|
|
||||||
|
// forward to superclass
|
||||||
|
if (CMSWindowsScreen::onPreDispatch(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// handle event
|
// handle event
|
||||||
|
const MSG* msg = &event->m_msg;
|
||||||
switch (msg->message) {
|
switch (msg->message) {
|
||||||
case WM_TIMER:
|
case WM_TIMER:
|
||||||
// if current desktop is not the input desktop then switch to it
|
// if current desktop is not the input desktop then switch to it
|
||||||
|
@ -503,32 +449,35 @@ CMSWindowsSecondaryScreen::onPreTranslate(MSG* msg)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT
|
bool
|
||||||
CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg,
|
CMSWindowsSecondaryScreen::onEvent(CEvent* event)
|
||||||
WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
{
|
||||||
switch (msg) {
|
assert(event != NULL);
|
||||||
|
|
||||||
|
const MSG& msg = event->msg;
|
||||||
|
switch (msg.message) {
|
||||||
case WM_QUERYENDSESSION:
|
case WM_QUERYENDSESSION:
|
||||||
if (m_is95Family) {
|
if (m_is95Family) {
|
||||||
return TRUE;
|
event->m_result = TRUE;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_ENDSESSION:
|
case WM_ENDSESSION:
|
||||||
if (m_is95Family) {
|
if (m_is95Family) {
|
||||||
if (wParam == TRUE && lParam == 0) {
|
if (msg.wParam == TRUE && msg.lParam == 0) {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
ValidateRect(hwnd, NULL);
|
ValidateRect(msg.hwnd, NULL);
|
||||||
return 0;
|
return true;
|
||||||
|
|
||||||
case WM_ACTIVATEAPP:
|
case WM_ACTIVATEAPP:
|
||||||
if (wParam == FALSE) {
|
if (msg.wParam == FALSE) {
|
||||||
// some other app activated. hide the hider window.
|
// some other app activated. hide the hider window.
|
||||||
ShowWindow(m_window, SW_HIDE);
|
ShowWindow(m_window, SW_HIDE);
|
||||||
}
|
}
|
||||||
|
@ -538,67 +487,265 @@ CMSWindowsSecondaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||||
log((CLOG_DEBUG "clipboard was taken"));
|
log((CLOG_DEBUG "clipboard was taken"));
|
||||||
|
|
||||||
// first pass it on
|
// 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
|
// now notify client that somebody changed the clipboard (unless
|
||||||
// we're now the owner, in which case it's because we took
|
// 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
|
// ownership, or now it's owned by nobody, which will happen if
|
||||||
// we owned it and switched desktops because we destroy our
|
// we owned it and switched desktops because we destroy our
|
||||||
// window to do that).
|
// window to do that).
|
||||||
|
try {
|
||||||
m_clipboardOwner = GetClipboardOwner();
|
m_clipboardOwner = GetClipboardOwner();
|
||||||
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
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:
|
case WM_CHANGECBCHAIN:
|
||||||
if (m_nextClipboardWindow == (HWND)wParam) {
|
if (m_nextClipboardWindow == (HWND)msg.wParam) {
|
||||||
m_nextClipboardWindow = (HWND)lParam;
|
m_nextClipboardWindow = (HWND)msg.lParam;
|
||||||
}
|
}
|
||||||
else {
|
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:
|
case WM_DISPLAYCHANGE:
|
||||||
// screen resolution has changed
|
{
|
||||||
|
// screen resolution may have changed. get old shape.
|
||||||
|
SInt32 xOld, yOld, wOld, hOld;
|
||||||
|
getScreenShape(xOld, yOld, wOld, hOld);
|
||||||
|
|
||||||
|
// update shape
|
||||||
updateScreenShape();
|
updateScreenShape();
|
||||||
m_multimon = isMultimon();
|
m_multimon = isMultimon();
|
||||||
|
|
||||||
// send new info
|
// collect new screen info
|
||||||
CClientInfo info;
|
CClientInfo info;
|
||||||
getShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h);
|
||||||
getMousePos(info.m_mx, info.m_my);
|
getCursorPos(info.m_mx, info.m_my);
|
||||||
info.m_zoneSize = getJumpZoneSize();
|
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);
|
m_receiver->onInfoChanged(info);
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return DefWindowProc(hwnd, msg, wParam, lParam);
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
CString
|
||||||
|
CMSWindowsSecondaryScreen::getCurrentDesktopName() const
|
||||||
|
{
|
||||||
|
return m_deskName;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsSecondaryScreen::onEnter(SInt32 x, SInt32 y)
|
CMSWindowsSecondaryScreen::showWindow()
|
||||||
{
|
{
|
||||||
// warp to requested location
|
// move hider window under the mouse (rather than moving the mouse
|
||||||
warpCursor(x, y);
|
// 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);
|
ShowWindow(m_window, SW_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsSecondaryScreen::onLeave()
|
CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||||
{
|
{
|
||||||
// move hider window under the mouse (rather than moving the mouse
|
// move the mouse directly to target position on NT family or if
|
||||||
// somewhere else on the screen)
|
// not using multiple monitors.
|
||||||
POINT point;
|
if (!m_multimon || !m_is95Family) {
|
||||||
GetCursorPos(&point);
|
SInt32 x0, y0, w, h;
|
||||||
MoveWindow(m_window, point.x, point.y, 1, 1, FALSE);
|
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.
|
// windows 98 (and Me?) is broken. you cannot set the absolute
|
||||||
ShowWindow(m_window, SW_SHOWNORMAL);
|
// 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
|
bool
|
||||||
|
@ -720,7 +867,7 @@ CMSWindowsSecondaryScreen::switchDesktop(HDESK desk)
|
||||||
|
|
||||||
// get desktop up to date
|
// get desktop up to date
|
||||||
if (!m_active) {
|
if (!m_active) {
|
||||||
onLeave();
|
showWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
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
|
bool
|
||||||
CMSWindowsSecondaryScreen::isMultimon() const
|
CMSWindowsSecondaryScreen::isMultimon() const
|
||||||
{
|
{
|
||||||
|
|
|
@ -46,10 +46,8 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CMSWindowsScreen overrides
|
// CMSWindowsScreen overrides
|
||||||
virtual bool onPreTranslate(MSG*);
|
virtual bool onPreDispatch(const CEvent* event);
|
||||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
virtual bool onEvent(CEvent* event);
|
||||||
virtual void onOpenDisplay();
|
|
||||||
virtual void onCloseDisplay();
|
|
||||||
virtual CString getCurrentDesktopName() const;
|
virtual CString getCurrentDesktopName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -62,8 +60,25 @@ private:
|
||||||
};
|
};
|
||||||
typedef std::vector<Keystroke> Keystrokes;
|
typedef std::vector<Keystroke> Keystrokes;
|
||||||
|
|
||||||
void onEnter(SInt32 x, SInt32 y);
|
void showWindow();
|
||||||
void onLeave();
|
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)
|
// open/close desktop (for windows 95/98/me)
|
||||||
bool openDesktop();
|
bool openDesktop();
|
||||||
|
@ -75,9 +90,6 @@ private:
|
||||||
// get calling thread to use the input desktop
|
// get calling thread to use the input desktop
|
||||||
void syncDesktop() const;
|
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
|
// returns true iff there appear to be multiple monitors
|
||||||
bool isMultimon() const;
|
bool isMultimon() const;
|
||||||
|
|
||||||
|
@ -108,6 +120,9 @@ private:
|
||||||
// the main loop's thread id
|
// the main loop's thread id
|
||||||
DWORD m_threadID;
|
DWORD m_threadID;
|
||||||
|
|
||||||
|
// the timer used to check for desktop switching
|
||||||
|
UINT m_timer;
|
||||||
|
|
||||||
// the thread id of the last attached thread
|
// the thread id of the last attached thread
|
||||||
mutable DWORD m_lastThreadID;
|
mutable DWORD m_lastThreadID;
|
||||||
|
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
#include "CXWindowsSecondaryScreen.h"
|
#include "CXWindowsSecondaryScreen.h"
|
||||||
#include "CClient.h"
|
#include "IScreenReceiver.h"
|
||||||
#include "CXWindowsClipboard.h"
|
#include "CXWindowsClipboard.h"
|
||||||
#include "CXWindowsScreenSaver.h"
|
#include "CXWindowsScreenSaver.h"
|
||||||
#include "CXWindowsUtil.h"
|
#include "CXWindowsUtil.h"
|
||||||
|
#include "XScreen.h"
|
||||||
#include "CThread.h"
|
#include "CThread.h"
|
||||||
#include "CLog.h"
|
#include "CLog.h"
|
||||||
#if defined(X_DISPLAY_MISSING)
|
#if defined(X_DISPLAY_MISSING)
|
||||||
|
@ -26,9 +27,10 @@
|
||||||
|
|
||||||
CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) :
|
CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) :
|
||||||
m_receiver(receiver),
|
m_receiver(receiver),
|
||||||
m_window(None)
|
m_window(None),
|
||||||
|
m_active(false)
|
||||||
{
|
{
|
||||||
// do nothing
|
assert(m_receiver != NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen()
|
CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen()
|
||||||
|
@ -41,95 +43,43 @@ CXWindowsSecondaryScreen::run()
|
||||||
{
|
{
|
||||||
assert(m_window != None);
|
assert(m_window != None);
|
||||||
|
|
||||||
for (;;) {
|
// change our priority
|
||||||
// wait for and get the next event
|
CThread::getCurrentThread().setPriority(-7);
|
||||||
XEvent xevent;
|
|
||||||
if (!getEvent(&xevent)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle event
|
// run event loop
|
||||||
switch (xevent.type) {
|
try {
|
||||||
case MappingNotify:
|
log((CLOG_INFO "entering event loop"));
|
||||||
{
|
mainLoop();
|
||||||
// keyboard mapping changed
|
log((CLOG_INFO "exiting event loop"));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
log((CLOG_INFO "exiting event loop"));
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsSecondaryScreen::stop()
|
CXWindowsSecondaryScreen::stop()
|
||||||
{
|
{
|
||||||
CDisplayLock display(this);
|
exitMainLoop();
|
||||||
doStop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsSecondaryScreen::open()
|
CXWindowsSecondaryScreen::open()
|
||||||
{
|
{
|
||||||
assert(m_receiver != NULL);
|
|
||||||
assert(m_window == None);
|
assert(m_window == None);
|
||||||
|
|
||||||
|
try {
|
||||||
// open the display
|
// open the display
|
||||||
openDisplay();
|
openDisplay();
|
||||||
|
|
||||||
{
|
// create and prepare our window
|
||||||
CDisplayLock display(this);
|
createWindow();
|
||||||
|
|
||||||
// verify the availability of the XTest extension
|
// initialize the clipboards; assume primary has all clipboards
|
||||||
int majorOpcode, firstEvent, firstError;
|
|
||||||
if (!XQueryExtension(display, XTestExtensionName,
|
|
||||||
&majorOpcode, &firstEvent, &firstError)) {
|
|
||||||
throw int(6); // FIXME -- make exception for this
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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));
|
|
||||||
|
|
||||||
// become impervious to server grabs
|
|
||||||
XTestGrabControl(display, True);
|
|
||||||
|
|
||||||
// hide the cursor
|
|
||||||
leaveNoLock(display);
|
|
||||||
|
|
||||||
// initialize the clipboards
|
|
||||||
initClipboards(m_window);
|
initClipboards(m_window);
|
||||||
|
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
||||||
// update key state
|
grabClipboard(id);
|
||||||
updateKeys(display);
|
|
||||||
updateKeycodeMap(display);
|
|
||||||
updateModifierMap(display);
|
|
||||||
updateModifiers(display);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// check for peculiarities
|
// check for peculiarities
|
||||||
|
@ -139,37 +89,33 @@ CXWindowsSecondaryScreen::open()
|
||||||
// m_numLockHalfDuplex = true;
|
// m_numLockHalfDuplex = true;
|
||||||
// m_capsLockHalfDuplex = true;
|
// m_capsLockHalfDuplex = true;
|
||||||
|
|
||||||
// assume primary has all clipboards
|
// get the display
|
||||||
for (ClipboardID id = 0; id < kClipboardEnd; ++id) {
|
CDisplayLock display(this);
|
||||||
grabClipboard(id);
|
|
||||||
}
|
// update key state
|
||||||
|
updateKeys(display);
|
||||||
|
updateKeycodeMap(display);
|
||||||
|
updateModifierMap(display);
|
||||||
|
updateModifiers(display);
|
||||||
|
|
||||||
// disable the screen saver
|
// disable the screen saver
|
||||||
getScreenSaver()->disable();
|
installScreenSaver();
|
||||||
|
}
|
||||||
|
catch (...) {
|
||||||
|
close();
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide the cursor
|
||||||
|
m_active = true;
|
||||||
|
leave();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsSecondaryScreen::close()
|
CXWindowsSecondaryScreen::close()
|
||||||
{
|
{
|
||||||
// release keys that are logically pressed
|
uninstallScreenSaver();
|
||||||
releaseKeys();
|
destroyWindow();
|
||||||
|
|
||||||
// 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
|
|
||||||
closeDisplay();
|
closeDisplay();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -177,15 +123,14 @@ void
|
||||||
CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
||||||
{
|
{
|
||||||
assert(m_window != None);
|
assert(m_window != None);
|
||||||
|
assert(m_active == false);
|
||||||
|
|
||||||
|
log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask));
|
||||||
|
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
|
|
||||||
// warp to requested location
|
// now active
|
||||||
XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime);
|
m_active = true;
|
||||||
XSync(display, False);
|
|
||||||
|
|
||||||
// show cursor
|
|
||||||
XUnmapWindow(display, m_window);
|
|
||||||
|
|
||||||
// update our keyboard state to reflect the local state
|
// update our keyboard state to reflect the local state
|
||||||
updateKeys(display);
|
updateKeys(display);
|
||||||
|
@ -202,14 +147,32 @@ CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask)
|
||||||
if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) {
|
if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) {
|
||||||
toggleKey(display, XK_Scroll_Lock, m_scrollLockMask);
|
toggleKey(display, XK_Scroll_Lock, m_scrollLockMask);
|
||||||
}
|
}
|
||||||
XSync(display, False);
|
|
||||||
|
// warp to requested location
|
||||||
|
warpCursor(x, y);
|
||||||
|
|
||||||
|
// show mouse
|
||||||
|
hideWindow();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsSecondaryScreen::leave()
|
CXWindowsSecondaryScreen::leave()
|
||||||
{
|
{
|
||||||
|
assert(m_window != None);
|
||||||
|
assert(m_active == true);
|
||||||
|
|
||||||
|
log((CLOG_INFO "leaving screen"));
|
||||||
|
|
||||||
CDisplayLock display(this);
|
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
|
void
|
||||||
|
@ -290,8 +253,7 @@ void
|
||||||
CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
|
CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y)
|
||||||
{
|
{
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime);
|
warpCursor(x, y);
|
||||||
XSync(display, False);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -366,6 +328,44 @@ CXWindowsSecondaryScreen::getClipboard(ClipboardID id,
|
||||||
getDisplayClipboard(id, clipboard);
|
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
|
void
|
||||||
CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id)
|
CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id)
|
||||||
{
|
{
|
||||||
|
@ -374,25 +374,110 @@ CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
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
|
// move hider window under the mouse (rather than moving the mouse
|
||||||
// somewhere else on the screen)
|
// somewhere else on the screen)
|
||||||
int x, y, dummy;
|
SInt32 x, y;
|
||||||
unsigned int dummyMask;
|
getCursorPos(x, y);
|
||||||
Window dummyWindow;
|
XMoveWindow(getDisplay(), m_window, x, y);
|
||||||
XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow,
|
|
||||||
&x, &y, &dummy, &dummy, &dummyMask);
|
|
||||||
XMoveWindow(display, m_window, x, y);
|
|
||||||
|
|
||||||
// raise and show the hider window
|
// raise and show the hider window. take activation.
|
||||||
XMapRaised(display, m_window);
|
// FIXME -- take focus?
|
||||||
|
XMapRaised(getDisplay(), m_window);
|
||||||
|
|
||||||
|
/* XXX -- this should have no effect
|
||||||
// hide cursor by moving it into the hider window
|
// 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
|
unsigned int
|
||||||
|
@ -829,6 +914,7 @@ CXWindowsSecondaryScreen::releaseKeys()
|
||||||
{
|
{
|
||||||
CDisplayLock display(this);
|
CDisplayLock display(this);
|
||||||
|
|
||||||
|
if (display != NULL) {
|
||||||
// key up for each key that's down
|
// key up for each key that's down
|
||||||
for (UInt32 i = 0; i < 256; ++i) {
|
for (UInt32 i = 0; i < 256; ++i) {
|
||||||
if (m_keys[i]) {
|
if (m_keys[i]) {
|
||||||
|
@ -839,7 +925,7 @@ CXWindowsSecondaryScreen::releaseKeys()
|
||||||
|
|
||||||
// update
|
// update
|
||||||
XSync(display, False);
|
XSync(display, False);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
|
@ -39,6 +39,8 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CXWindowsScreen overrides
|
// CXWindowsScreen overrides
|
||||||
|
virtual bool onPreDispatch(const CEvent* event);
|
||||||
|
virtual bool onEvent(CEvent* event);
|
||||||
virtual void onLostClipboard(ClipboardID);
|
virtual void onLostClipboard(ClipboardID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
@ -60,7 +62,26 @@ private:
|
||||||
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
|
typedef std::map<KeyID, KeyCodeMask> KeyCodeMap;
|
||||||
typedef std::map<KeyCode, unsigned int> ModifierMap;
|
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 mapButton(ButtonID button) const;
|
||||||
|
|
||||||
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
|
unsigned int mapKey(Keystrokes&, KeyCode&, KeyID,
|
||||||
|
@ -82,6 +103,9 @@ private:
|
||||||
IScreenReceiver* m_receiver;
|
IScreenReceiver* m_receiver;
|
||||||
Window m_window;
|
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
|
// note toggle keys that toggles on up/down (false) or on
|
||||||
// transition (true)
|
// transition (true)
|
||||||
bool m_numLockHalfDuplex;
|
bool m_numLockHalfDuplex;
|
||||||
|
|
|
@ -48,36 +48,44 @@ CMSWindowsScreen::init(HINSTANCE instance)
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsScreen::doRun()
|
CMSWindowsScreen::mainLoop()
|
||||||
{
|
{
|
||||||
// save thread id for posting quit message
|
// save thread id for posting quit message
|
||||||
m_thread = GetCurrentThreadId();
|
m_thread = GetCurrentThreadId();
|
||||||
|
|
||||||
// event loop
|
// event loop
|
||||||
|
CEvent event;
|
||||||
|
event.m_result = 0;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
// wait for and get the next event
|
// wait for an event in a cancellable way
|
||||||
MSG msg;
|
CThread::waitForEvent();
|
||||||
getEvent(&msg);
|
GetMessage(&event.m_msg, NULL, 0, 0);
|
||||||
|
|
||||||
// handle quit message
|
// handle quit message
|
||||||
if (msg.message == WM_QUIT) {
|
if (event.m_msg.message == WM_QUIT) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// dispatch message
|
// dispatch message
|
||||||
if (!onPreTranslate(&msg)) {
|
if (!onPreDispatch(&event)) {
|
||||||
TranslateMessage(&msg);
|
TranslateMessage(&event.m_msg);
|
||||||
DispatchMessage(&msg);
|
DispatchMessage(&event.m_msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsScreen::doStop()
|
CMSWindowsScreen::exitMainLoop()
|
||||||
{
|
{
|
||||||
PostThreadMessage(m_thread, WM_QUIT, 0, 0);
|
PostThreadMessage(m_thread, WM_QUIT, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CMSWindowsScreen::onPreDispatch(const CEvent*)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsScreen::openDisplay()
|
CMSWindowsScreen::openDisplay()
|
||||||
{
|
{
|
||||||
|
@ -172,9 +180,13 @@ void
|
||||||
CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
|
CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const
|
||||||
{
|
{
|
||||||
POINT pos;
|
POINT pos;
|
||||||
GetCursorPos(&pos);
|
if (GetCursorPos(&pos)) {
|
||||||
x = pos.x;
|
x = pos.x;
|
||||||
y = pos.y;
|
y = pos.y;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
getCursorCenter(x, y);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -244,17 +256,22 @@ CMSWindowsScreen::getScreenSaver() const
|
||||||
return m_screenSaver;
|
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
|
LRESULT CALLBACK
|
||||||
CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
CMSWindowsScreen::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
||||||
{
|
{
|
||||||
assert(s_screen != NULL);
|
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 CMSWindowsScreenSaver;
|
||||||
class CThread;
|
class CThread;
|
||||||
|
|
||||||
|
class CEvent {
|
||||||
|
public:
|
||||||
|
MSG m_msg;
|
||||||
|
LRESULT m_result;
|
||||||
|
};
|
||||||
|
|
||||||
class CMSWindowsScreen {
|
class CMSWindowsScreen {
|
||||||
public:
|
public:
|
||||||
CMSWindowsScreen();
|
CMSWindowsScreen();
|
||||||
|
@ -25,11 +31,11 @@ public:
|
||||||
static HINSTANCE getInstance();
|
static HINSTANCE getInstance();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// runs an event loop and returns when WM_QUIT is received
|
// runs an event loop and returns when exitMainLoop() is called
|
||||||
void doRun();
|
void mainLoop();
|
||||||
|
|
||||||
// sends WM_QUIT to force doRun() to return
|
// force mainLoop() to return
|
||||||
void doStop();
|
void exitMainLoop();
|
||||||
|
|
||||||
// open the X display. calls onOpenDisplay() after opening the display,
|
// open the X display. calls onOpenDisplay() after opening the display,
|
||||||
// getting the screen, its size, and root window. then it starts the
|
// getting the screen, its size, and root window. then it starts the
|
||||||
|
@ -72,15 +78,14 @@ protected:
|
||||||
CMSWindowsScreenSaver*
|
CMSWindowsScreenSaver*
|
||||||
getScreenSaver() const;
|
getScreenSaver() const;
|
||||||
|
|
||||||
// wait for and get the next message. cancellable.
|
// called for each event before event translation and dispatch. return
|
||||||
void getEvent(MSG*) const;
|
// 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
|
// called by mainLoop(). iff the event was handled return true and
|
||||||
// event translation and dispatch.
|
// store the result, if any, in m_result, which defaults to zero.
|
||||||
virtual bool onPreTranslate(MSG*) = 0;
|
virtual bool onEvent(CEvent* event) = 0;
|
||||||
|
|
||||||
// called by window proc. subclass must call DefWindowProc() if necessary
|
|
||||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0;
|
|
||||||
|
|
||||||
// called by isCurrentDesktop() to get the current desktop name
|
// called by isCurrentDesktop() to get the current desktop name
|
||||||
virtual CString getCurrentDesktopName() const = 0;
|
virtual CString getCurrentDesktopName() const = 0;
|
||||||
|
@ -89,6 +94,7 @@ private:
|
||||||
// create the transparent cursor
|
// create the transparent cursor
|
||||||
void createBlankCursor();
|
void createBlankCursor();
|
||||||
|
|
||||||
|
// our window proc
|
||||||
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
|
static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -120,6 +120,129 @@ CXWindowsScreen::removeTimerNoLock(IJob* job)
|
||||||
m_timers.swap(tmp);
|
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
|
void
|
||||||
CXWindowsScreen::openDisplay()
|
CXWindowsScreen::openDisplay()
|
||||||
{
|
{
|
||||||
|
@ -179,6 +302,12 @@ CXWindowsScreen::closeDisplay()
|
||||||
XSetIOErrorHandler(NULL);
|
XSetIOErrorHandler(NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Display*
|
||||||
|
CXWindowsScreen::getDisplay() const
|
||||||
|
{
|
||||||
|
return m_display;
|
||||||
|
}
|
||||||
|
|
||||||
int
|
int
|
||||||
CXWindowsScreen::getScreen() const
|
CXWindowsScreen::getScreen() const
|
||||||
{
|
{
|
||||||
|
@ -294,49 +423,6 @@ CXWindowsScreen::getBlankCursor() const
|
||||||
return m_cursor;
|
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
|
ClipboardID
|
||||||
CXWindowsScreen::getClipboardID(Atom selection) const
|
CXWindowsScreen::getClipboardID(Atom selection) const
|
||||||
{
|
{
|
||||||
|
@ -355,84 +441,6 @@ CXWindowsScreen::onUnexpectedClose()
|
||||||
// do nothing
|
// 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
|
bool
|
||||||
CXWindowsScreen::processTimers()
|
CXWindowsScreen::processTimers()
|
||||||
{
|
{
|
||||||
|
|
|
@ -19,6 +19,12 @@ class IScreenSaver;
|
||||||
class CXWindowsClipboard;
|
class CXWindowsClipboard;
|
||||||
class CXWindowsScreenSaver;
|
class CXWindowsScreenSaver;
|
||||||
|
|
||||||
|
class CEvent {
|
||||||
|
public:
|
||||||
|
XEvent m_event;
|
||||||
|
SInt32 m_result;
|
||||||
|
};
|
||||||
|
|
||||||
class CXWindowsScreen {
|
class CXWindowsScreen {
|
||||||
public:
|
public:
|
||||||
CXWindowsScreen();
|
CXWindowsScreen();
|
||||||
|
@ -47,6 +53,12 @@ protected:
|
||||||
};
|
};
|
||||||
friend class CDisplayLock;
|
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,
|
// open the X display. calls onOpenDisplay() after opening the display,
|
||||||
// getting the screen, its size, and root window. then it starts the
|
// getting the screen, its size, and root window. then it starts the
|
||||||
// event thread.
|
// event thread.
|
||||||
|
@ -57,6 +69,10 @@ protected:
|
||||||
// is closed.
|
// is closed.
|
||||||
void closeDisplay();
|
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
|
// get the opened screen and its root window. to get the display
|
||||||
// create a CDisplayLock object passing this. while the object
|
// create a CDisplayLock object passing this. while the object
|
||||||
// exists no other threads may access the display. do not save
|
// exists no other threads may access the display. do not save
|
||||||
|
@ -83,13 +99,6 @@ protected:
|
||||||
// get a cursor that is transparent everywhere
|
// get a cursor that is transparent everywhere
|
||||||
Cursor getBlankCursor() const;
|
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)
|
// set the contents of the clipboard (i.e. primary selection)
|
||||||
bool setDisplayClipboard(ClipboardID,
|
bool setDisplayClipboard(ClipboardID,
|
||||||
const IClipboard* clipboard);
|
const IClipboard* clipboard);
|
||||||
|
@ -102,6 +111,15 @@ protected:
|
||||||
CXWindowsScreenSaver*
|
CXWindowsScreenSaver*
|
||||||
getScreenSaver() const;
|
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.
|
// called if the display is unexpectedly closing. default does nothing.
|
||||||
virtual void onUnexpectedClose();
|
virtual void onUnexpectedClose();
|
||||||
|
|
||||||
|
@ -115,9 +133,6 @@ private:
|
||||||
// remove a timer without locking
|
// remove a timer without locking
|
||||||
void removeTimerNoLock(IJob*);
|
void removeTimerNoLock(IJob*);
|
||||||
|
|
||||||
// internal event processing
|
|
||||||
bool processEvent(XEvent*);
|
|
||||||
|
|
||||||
// process timers
|
// process timers
|
||||||
bool processTimers();
|
bool processTimers();
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ CXWindowsScreenSaver::~CXWindowsScreenSaver()
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CXWindowsScreenSaver::processEvent(XEvent* xevent)
|
CXWindowsScreenSaver::onPreDispatch(const XEvent* xevent)
|
||||||
{
|
{
|
||||||
switch (xevent->type) {
|
switch (xevent->type) {
|
||||||
case CreateNotify:
|
case CreateNotify:
|
||||||
|
|
|
@ -20,8 +20,10 @@ public:
|
||||||
CXWindowsScreenSaver(CXWindowsScreen*, Display*);
|
CXWindowsScreenSaver(CXWindowsScreen*, Display*);
|
||||||
virtual ~CXWindowsScreenSaver();
|
virtual ~CXWindowsScreenSaver();
|
||||||
|
|
||||||
// process X event. returns true if the event was handled.
|
// called for each event before event translation and dispatch. return
|
||||||
bool processEvent(XEvent*);
|
// 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
|
// tells this object to send a ClientMessage to the given window
|
||||||
// when the screen saver activates or deactivates. only one
|
// when the screen saver activates or deactivates. only one
|
||||||
|
|
|
@ -22,6 +22,7 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen(
|
||||||
m_receiver(receiver),
|
m_receiver(receiver),
|
||||||
m_primaryReceiver(primaryReceiver),
|
m_primaryReceiver(primaryReceiver),
|
||||||
m_threadID(0),
|
m_threadID(0),
|
||||||
|
m_timer(0),
|
||||||
m_desk(NULL),
|
m_desk(NULL),
|
||||||
m_deskName(),
|
m_deskName(),
|
||||||
m_window(NULL),
|
m_window(NULL),
|
||||||
|
@ -95,28 +96,22 @@ CMSWindowsPrimaryScreen::run()
|
||||||
// change our priority
|
// change our priority
|
||||||
CThread::getCurrentThread().setPriority(-3);
|
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
|
// run event loop
|
||||||
|
try {
|
||||||
log((CLOG_INFO "entering event loop"));
|
log((CLOG_INFO "entering event loop"));
|
||||||
doRun();
|
mainLoop();
|
||||||
log((CLOG_INFO "exiting event loop"));
|
log((CLOG_INFO "exiting event loop"));
|
||||||
|
}
|
||||||
// remove timer
|
catch (...) {
|
||||||
if (!m_is95Family) {
|
log((CLOG_INFO "exiting event loop"));
|
||||||
KillTimer(NULL, timer);
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsPrimaryScreen::stop()
|
CMSWindowsPrimaryScreen::stop()
|
||||||
{
|
{
|
||||||
doStop();
|
exitMainLoop();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -269,50 +264,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||||
m_y = 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
|
void
|
||||||
CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/,
|
CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/,
|
||||||
const IClipboard* src)
|
const IClipboard* src)
|
||||||
|
@ -408,9 +359,17 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event)
|
||||||
{
|
{
|
||||||
|
assert(event != NULL);
|
||||||
|
|
||||||
|
// forward to superclass
|
||||||
|
if (CMSWindowsScreen::onPreTranslate(event)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
// handle event
|
// handle event
|
||||||
|
const MSG* msg = &event->m_msg;
|
||||||
switch (msg->message) {
|
switch (msg->message) {
|
||||||
case SYNERGY_MSG_MARK:
|
case SYNERGY_MSG_MARK:
|
||||||
m_markReceived = msg->wParam;
|
m_markReceived = msg->wParam;
|
||||||
|
@ -567,43 +526,47 @@ CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
LRESULT
|
bool
|
||||||
CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
CMSWindowsPrimaryScreen::onEvent(CEvent* event)
|
||||||
WPARAM wParam, LPARAM lParam)
|
|
||||||
{
|
{
|
||||||
switch (msg) {
|
assert(event != NULL);
|
||||||
|
|
||||||
|
const MSG& msg = event->msg;
|
||||||
|
switch (msg.message) {
|
||||||
case WM_QUERYENDSESSION:
|
case WM_QUERYENDSESSION:
|
||||||
if (m_is95Family) {
|
if (m_is95Family) {
|
||||||
return TRUE;
|
event->m_result = TRUE;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_ENDSESSION:
|
case WM_ENDSESSION:
|
||||||
if (m_is95Family) {
|
if (m_is95Family) {
|
||||||
if (wParam == TRUE && lParam == 0) {
|
if (msg.wParam == TRUE && msg.lParam == 0) {
|
||||||
stop();
|
stop();
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case WM_PAINT:
|
case WM_PAINT:
|
||||||
ValidateRect(hwnd, NULL);
|
ValidateRect(msg.hwnd, NULL);
|
||||||
return 0;
|
return true;
|
||||||
|
|
||||||
case WM_DRAWCLIPBOARD:
|
case WM_DRAWCLIPBOARD:
|
||||||
log((CLOG_DEBUG "clipboard was taken"));
|
log((CLOG_DEBUG "clipboard was taken"));
|
||||||
|
|
||||||
// first pass it on
|
// first pass it on
|
||||||
if (m_nextClipboardWindow != NULL) {
|
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.
|
// now notify server that somebody changed the clipboard.
|
||||||
// skip that if we're the new owner.
|
// skip that if we're the new owner.
|
||||||
try {
|
try {
|
||||||
m_clipboardOwner = GetClipboardOwner();
|
m_clipboardOwner = GetClipboardOwner();
|
||||||
if (m_clipboardOwner != m_window) {
|
if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) {
|
||||||
m_receiver->onGrabClipboard(kClipboardClipboard);
|
m_receiver->onGrabClipboard(kClipboardClipboard);
|
||||||
m_receiver->onGrabClipboard(kClipboardSelection);
|
m_receiver->onGrabClipboard(kClipboardSelection);
|
||||||
}
|
}
|
||||||
|
@ -612,28 +575,36 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||||
// ignore. this can happen if we receive this event
|
// ignore. this can happen if we receive this event
|
||||||
// before we've fully started up.
|
// before we've fully started up.
|
||||||
}
|
}
|
||||||
return 0;
|
return true;
|
||||||
|
|
||||||
case WM_CHANGECBCHAIN:
|
case WM_CHANGECBCHAIN:
|
||||||
if (m_nextClipboardWindow == (HWND)wParam) {
|
if (m_nextClipboardWindow == (HWND)msg.wParam) {
|
||||||
m_nextClipboardWindow = (HWND)lParam;
|
m_nextClipboardWindow = (HWND)msg.lParam;
|
||||||
}
|
}
|
||||||
else if (m_nextClipboardWindow != NULL) {
|
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:
|
case WM_DISPLAYCHANGE:
|
||||||
{
|
{
|
||||||
// screen resolution may have changed
|
// screen resolution may have changed. get old shape.
|
||||||
SInt32 xOld, yOld, wOld, hOld;
|
SInt32 xOld, yOld, wOld, hOld;
|
||||||
getScreenShape(xOld, yOld, wOld, hOld);
|
getScreenShape(xOld, yOld, wOld, hOld);
|
||||||
|
|
||||||
|
// update shape
|
||||||
updateScreenShape();
|
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
|
// 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
|
// recompute center pixel of primary screen
|
||||||
getCursorCenter(m_xCenter, m_yCenter);
|
getCursorCenter(m_xCenter, m_yCenter);
|
||||||
|
|
||||||
|
@ -644,28 +615,46 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg,
|
||||||
|
|
||||||
// tell hook about resize if not active
|
// tell hook about resize if not active
|
||||||
else {
|
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
|
// 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);
|
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
|
void
|
||||||
|
@ -743,20 +732,33 @@ CMSWindowsPrimaryScreen::hideWindow()
|
||||||
ShowWindow(m_window, SW_HIDE);
|
ShowWindow(m_window, SW_HIDE);
|
||||||
}
|
}
|
||||||
|
|
||||||
SInt32
|
|
||||||
CMSWindowsPrimaryScreen::getJumpZoneSize() const
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
CMSWindowsPrimaryScreen::nextMark()
|
CMSWindowsPrimaryScreen::checkClipboard()
|
||||||
{
|
{
|
||||||
// next mark
|
// if we think we own the clipboard but we don't then somebody
|
||||||
++m_mark;
|
// grabbed the clipboard on this screen without us knowing.
|
||||||
|
// tell the server that this screen grabbed the clipboard.
|
||||||
// mark point in message queue where the mark was changed
|
//
|
||||||
PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0);
|
// 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
|
void
|
||||||
|
@ -773,11 +775,23 @@ CMSWindowsPrimaryScreen::createWindow()
|
||||||
throw XScreenOpenFailure();
|
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
|
void
|
||||||
CMSWindowsPrimaryScreen::destroyWindow()
|
CMSWindowsPrimaryScreen::destroyWindow()
|
||||||
{
|
{
|
||||||
|
// remove timer
|
||||||
|
if (m_timer != 0) {
|
||||||
|
KillTimer(NULL, m_timer);
|
||||||
|
}
|
||||||
|
|
||||||
// disconnect from desktop
|
// disconnect from desktop
|
||||||
if (m_is95Family) {
|
if (m_is95Family) {
|
||||||
closeDesktop();
|
closeDesktop();
|
||||||
|
@ -977,10 +991,14 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
CString
|
void
|
||||||
CMSWindowsPrimaryScreen::getCurrentDesktopName() const
|
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[] =
|
static const KeyID g_virtualKey[] =
|
||||||
|
|
|
@ -34,30 +34,25 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CMSWindowsScreen overrides
|
// CMSWindowsScreen overrides
|
||||||
virtual bool onPreTranslate(MSG*);
|
virtual bool onPreTranslate(const CEvent* event);
|
||||||
virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM);
|
virtual bool onEvent(CEvent* event);
|
||||||
virtual void onOpenDisplay();
|
|
||||||
virtual void onCloseDisplay();
|
|
||||||
virtual CString getCurrentDesktopName() const;
|
virtual CString getCurrentDesktopName() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void enterNoWarp();
|
|
||||||
bool showWindow();
|
|
||||||
void hideWindow();
|
|
||||||
|
|
||||||
SInt32 getJumpZoneSize() const;
|
SInt32 getJumpZoneSize() const;
|
||||||
|
|
||||||
// warp mouse to center of primary display (used when computing
|
// warp mouse to center of primary display (used when computing
|
||||||
// motion deltas while mouse is on secondary screen).
|
// motion deltas while mouse is on secondary screen).
|
||||||
void warpCursorToCenter();
|
void warpCursorToCenter();
|
||||||
|
|
||||||
|
void enterNoWarp();
|
||||||
|
bool showWindow();
|
||||||
|
void hideWindow();
|
||||||
|
|
||||||
// check clipboard ownership and, if necessary, tell the receiver
|
// check clipboard ownership and, if necessary, tell the receiver
|
||||||
// of a grab.
|
// of a grab.
|
||||||
void checkClipboard();
|
void checkClipboard();
|
||||||
|
|
||||||
// discard posted messages
|
|
||||||
void nextMark();
|
|
||||||
|
|
||||||
// create/destroy window
|
// create/destroy window
|
||||||
// also attach to desktop; this destroys and recreates the window
|
// also attach to desktop; this destroys and recreates the window
|
||||||
// as necessary.
|
// as necessary.
|
||||||
|
@ -75,6 +70,9 @@ private:
|
||||||
// make desk the thread desktop (for windows NT/2000/XP)
|
// make desk the thread desktop (for windows NT/2000/XP)
|
||||||
bool switchDesktop(HDESK desk);
|
bool switchDesktop(HDESK desk);
|
||||||
|
|
||||||
|
// discard posted messages
|
||||||
|
void nextMark();
|
||||||
|
|
||||||
// key and button queries
|
// key and button queries
|
||||||
KeyID mapKey(WPARAM keycode, LPARAM info,
|
KeyID mapKey(WPARAM keycode, LPARAM info,
|
||||||
KeyModifierMask* maskOut);
|
KeyModifierMask* maskOut);
|
||||||
|
@ -92,6 +90,9 @@ private:
|
||||||
// the main loop's thread id
|
// the main loop's thread id
|
||||||
DWORD m_threadID;
|
DWORD m_threadID;
|
||||||
|
|
||||||
|
// the timer used to check for desktop switching
|
||||||
|
UINT m_timer;
|
||||||
|
|
||||||
// the current desk and it's name
|
// the current desk and it's name
|
||||||
HDESK m_desk;
|
HDESK m_desk;
|
||||||
CString m_deskName;
|
CString m_deskName;
|
||||||
|
|
|
@ -42,199 +42,25 @@ CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen()
|
||||||
void
|
void
|
||||||
CXWindowsPrimaryScreen::run()
|
CXWindowsPrimaryScreen::run()
|
||||||
{
|
{
|
||||||
for (;;) {
|
// change our priority
|
||||||
// wait for and get the next event
|
CThread::getCurrentThread().setPriority(-3);
|
||||||
XEvent xevent;
|
|
||||||
if (!getEvent(&xevent)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle event
|
// run event loop
|
||||||
switch (xevent.type) {
|
try {
|
||||||
case CreateNotify:
|
log((CLOG_INFO "entering event loop"));
|
||||||
{
|
mainLoop();
|
||||||
// select events on new window
|
log((CLOG_INFO "exiting event loop"));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
catch (...) {
|
||||||
|
log((CLOG_INFO "exiting event loop"));
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CXWindowsPrimaryScreen::stop()
|
CXWindowsPrimaryScreen::stop()
|
||||||
{
|
{
|
||||||
CDisplayLock display(this);
|
exitMainLoop();
|
||||||
doStop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -387,196 +213,6 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y)
|
||||||
m_y = 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
|
void
|
||||||
CXWindowsPrimaryScreen::setClipboard(ClipboardID id,
|
CXWindowsPrimaryScreen::setClipboard(ClipboardID id,
|
||||||
const IClipboard* clipboard)
|
const IClipboard* clipboard)
|
||||||
|
@ -659,6 +295,202 @@ CXWindowsPrimaryScreen::isLockedToScreen() const
|
||||||
return false;
|
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
|
void
|
||||||
CXWindowsPrimaryScreen::onUnexpectedClose()
|
CXWindowsPrimaryScreen::onUnexpectedClose()
|
||||||
{
|
{
|
||||||
|
@ -673,6 +505,196 @@ CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id)
|
||||||
m_receiver->onGrabClipboard(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
|
void
|
||||||
CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const
|
CXWindowsPrimaryScreen::selectEvents(Display* display, Window w) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -30,23 +30,22 @@ public:
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
// CXWindowsScreen overrides
|
// CXWindowsScreen overrides
|
||||||
|
virtual bool onPreDispatch(const CEvent* event);
|
||||||
|
virtual bool onEvent(CEvent* event);
|
||||||
virtual void onUnexpectedClose();
|
virtual void onUnexpectedClose();
|
||||||
virtual void onLostClipboard(ClipboardID);
|
virtual void onLostClipboard(ClipboardID);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void selectEvents(Display*, Window) const;
|
|
||||||
void doSelectEvents(Display*, Window) const;
|
|
||||||
|
|
||||||
void enterNoWarp();
|
|
||||||
bool showWindow();
|
|
||||||
void hideWindow();
|
|
||||||
|
|
||||||
SInt32 getJumpZoneSize() const;
|
SInt32 getJumpZoneSize() const;
|
||||||
|
|
||||||
void warpCursorToCenter();
|
void warpCursorToCenter();
|
||||||
void warpCursorNoFlush(Display*,
|
void warpCursorNoFlush(Display*,
|
||||||
SInt32 xAbsolute, SInt32 yAbsolute);
|
SInt32 xAbsolute, SInt32 yAbsolute);
|
||||||
|
|
||||||
|
void enterNoWarp();
|
||||||
|
bool showWindow();
|
||||||
|
void hideWindow();
|
||||||
|
|
||||||
// check clipboard ownership and, if necessary, tell the receiver
|
// check clipboard ownership and, if necessary, tell the receiver
|
||||||
// of a grab.
|
// of a grab.
|
||||||
void checkClipboard();
|
void checkClipboard();
|
||||||
|
@ -59,6 +58,9 @@ private:
|
||||||
void installScreenSaver();
|
void installScreenSaver();
|
||||||
void uninstallScreenSaver();
|
void uninstallScreenSaver();
|
||||||
|
|
||||||
|
void selectEvents(Display*, Window) const;
|
||||||
|
void doSelectEvents(Display*, Window) const;
|
||||||
|
|
||||||
KeyModifierMask mapModifier(unsigned int state) const;
|
KeyModifierMask mapModifier(unsigned int state) const;
|
||||||
KeyID mapKey(XKeyEvent*) const;
|
KeyID mapKey(XKeyEvent*) const;
|
||||||
ButtonID mapButton(unsigned int button) const;
|
ButtonID mapButton(unsigned int button) const;
|
||||||
|
|
Loading…
Reference in New Issue