diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 46b4d7a2..2eaa1fd8 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -85,19 +85,46 @@ void CXWindowsSecondaryScreen::open() { assert(m_receiver != NULL); + assert(m_window == None); // open the display openDisplay(); { - // verify the availability of the XTest extension CDisplayLock display(this); + + // verify the availability of the XTest extension int majorOpcode, firstEvent, firstError; if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) { throw int(6); // FIXME -- make exception for this } + // 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); + // update key state updateKeys(display); updateKeycodeMap(display); @@ -130,6 +157,18 @@ CXWindowsSecondaryScreen::close() // 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(); } @@ -304,13 +343,7 @@ void CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const { CDisplayLock display(this); - int xTmp, yTmp, dummy; - unsigned int dummyMask; - Window dummyWindow; - XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, - &xTmp, &yTmp, &dummy, &dummy, &dummyMask); - x = xTmp; - y = yTmp; + getCursorPos(x, y); } void @@ -333,56 +366,6 @@ CXWindowsSecondaryScreen::getClipboard(ClipboardID id, getDisplayClipboard(id, clipboard); } -void -CXWindowsSecondaryScreen::onOpenDisplay(Display* display) -{ - assert(m_window == None); - - // 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 = createBlankCursor(); - 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); -} - -CXWindowsClipboard* -CXWindowsSecondaryScreen::createClipboard(ClipboardID id) -{ - CDisplayLock display(this); - return new CXWindowsClipboard(display, m_window, id); -} - -void -CXWindowsSecondaryScreen::onCloseDisplay(Display* display) -{ - assert(m_window != None); - - if (display != NULL) { - // no longer impervious to server grabs - XTestGrabControl(display, False); - - // destroy window - XDestroyWindow(display, m_window); - } - m_window = None; -} - void CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) { diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index a8e4fc99..aeba39ec 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -39,10 +39,6 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(Display*); - virtual CXWindowsClipboard* - createClipboard(ClipboardID); - virtual void onCloseDisplay(Display*); virtual void onLostClipboard(ClipboardID); private: diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index a9b78907..f7ed98ac 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -84,16 +84,8 @@ CMSWindowsScreen::openDisplay() assert(s_instance != NULL); assert(m_class == 0); - // create a transparent cursor - int cw = GetSystemMetrics(SM_CXCURSOR); - int ch = GetSystemMetrics(SM_CYCURSOR); - UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; - UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; - memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); - memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); - m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); - delete[] cursorXOR; - delete[] cursorAND; + // create the transparent cursor + createBlankCursor(); // register a window class WNDCLASSEX classInfo; @@ -109,14 +101,11 @@ CMSWindowsScreen::openDisplay() classInfo.lpszMenuName = NULL; classInfo.lpszClassName = "Synergy"; classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); + m_class = RegisterClassEx(&classInfo); // get screen shape updateScreenShape(); - // let subclass prep display - onOpenDisplay(); - // initialize the screen saver m_screenSaver = new CMSWindowsScreenSaver(); } @@ -125,22 +114,22 @@ void CMSWindowsScreen::closeDisplay() { assert(s_instance != NULL); - assert(m_class != 0); // done with screen saver delete m_screenSaver; m_screenSaver = NULL; - // let subclass close down display - onCloseDisplay(); - // unregister the window class - UnregisterClass((LPCTSTR)m_class, s_instance); - m_class = 0; + if (m_class != 0) { + UnregisterClass((LPCTSTR)m_class, s_instance); + m_class = 0; + } // delete resources - DestroyCursor(m_cursor); - m_cursor = NULL; + if (m_cursor != NULL) { + DestroyCursor(m_cursor); + m_cursor = NULL; + } log((CLOG_DEBUG "closed display")); } @@ -168,8 +157,8 @@ CMSWindowsScreen::updateScreenShape() } void -CMSWindowsScreen::getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +CMSWindowsScreen::getScreenShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const { assert(m_class != 0); @@ -179,6 +168,43 @@ CMSWindowsScreen::getScreenShape( h = m_h; } +void +CMSWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + POINT pos; + GetCursorPos(&pos); + x = pos.x; + y = pos.y; +} + +void +CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + x = GetSystemMetrics(SM_CXSCREEN) >> 1; + y = GetSystemMetrics(SM_CYSCREEN) >> 1; +} + +HCURSOR +CMSWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CMSWindowsScreen::createBlankCursor() +{ + // create a transparent cursor + int cw = GetSystemMetrics(SM_CXCURSOR); + int ch = GetSystemMetrics(SM_CYCURSOR); + UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)]; + UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)]; + memset(cursorAND, 0xff, ch * ((cw + 31) >> 2)); + memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2)); + m_cursor = CreateCursor(s_instance, 0, 0, cw, ch, cursorAND, cursorXOR); + delete[] cursorXOR; + delete[] cursorAND; +} + HDESK CMSWindowsScreen::openInputDesktop() const { diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index 110bc499..08a95d73 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -51,6 +51,12 @@ protected: void getScreenShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; + // get the current cursor position + void getCursorPos(SInt32& x, SInt32& y) const; + + // get the cursor center position + void getCursorCenter(SInt32& x, SInt32& y) const; + // get the input desktop. caller must CloseDesktop() the result. // do not call under windows 95/98/me. HDESK openInputDesktop() const; @@ -76,16 +82,13 @@ protected: // called by window proc. subclass must call DefWindowProc() if necessary virtual LRESULT onEvent(HWND, UINT, WPARAM, LPARAM) = 0; - // called by openDisplay() to allow subclasses to prepare the display - virtual void onOpenDisplay() = 0; - - // called by closeDisplay() to - virtual void onCloseDisplay() = 0; - // called by isCurrentDesktop() to get the current desktop name virtual CString getCurrentDesktopName() const = 0; private: + // create the transparent cursor + void createBlankCursor(); + static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 4f223569..f54decde 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -141,27 +141,15 @@ CXWindowsScreen::openDisplay() throw XScreenOpenFailure(); } - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); + // get default screen and root window + m_screen = DefaultScreen(m_display); + m_root = RootWindow(m_display, m_screen); + + // create the transparent cursor + createBlankCursor(); // get screen shape - m_x = 0; - m_y = 0; - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); - - // get the root window - m_root = RootWindow(m_display, m_screen); - - // let subclass prep display - onOpenDisplay(m_display); - - // initialize clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - m_clipboard[id] = createClipboard(id); - } + updateScreenShape(); // initialize the screen saver m_screenSaver = new CXWindowsScreenSaver(this, m_display); @@ -172,9 +160,6 @@ CXWindowsScreen::closeDisplay() { CLock lock(&m_mutex); - // let subclass close down display - onCloseDisplay(m_display); - // done with screen saver delete m_screenSaver; m_screenSaver = NULL; @@ -209,8 +194,30 @@ CXWindowsScreen::getRoot() const } void -CXWindowsScreen::getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +CXWindowsScreen::initClipboards(Window window) +{ + assert(m_display != NULL); + assert(window != None); + + // initialize clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, window, id); + } +} + +void +CXWindowsScreen::updateScreenShape() +{ + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(ScreenOfDisplay(m_display, m_screen)); + m_h = HeightOfScreen(ScreenOfDisplay(m_display, m_screen)); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); +} + +void +CXWindowsScreen::getScreenShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const { assert(m_display != NULL); @@ -220,8 +227,35 @@ CXWindowsScreen::getScreenShape( h = m_h; } -Cursor -CXWindowsScreen::createBlankCursor() const +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + assert(m_display != NULL); + + Window root, window; + int mx, my, xWindow, yWindow; + unsigned int mask; + if (XQueryPointer(m_display, getRoot(), &root, &window, + &mx, &my, &xWindow, &yWindow, &mask)) { + x = mx; + y = my; + } + else { + getCursorCenter(x, y); + } +} + +void +CXWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const +{ + assert(m_display != NULL); + + x = m_x + (m_w >> 1); + y = m_y + (m_h >> 1); +} + +void +CXWindowsScreen::createBlankCursor() { // this seems just a bit more complicated than really necessary @@ -246,14 +280,18 @@ CXWindowsScreen::createBlankCursor() const color.flags = DoRed | DoGreen | DoBlue; // make cursor from bitmap - Cursor cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, + m_cursor = XCreatePixmapCursor(m_display, bitmap, bitmap, &color, &color, 0, 0); // don't need bitmap or the data anymore delete[] data; XFreePixmap(m_display, bitmap); +} - return cursor; +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; } bool diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index e4ed2cb3..8431c990 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -57,17 +57,31 @@ protected: // is closed. void closeDisplay(); - // get the opened screen, its shape, its root window. to get the - // display create a CDisplayLock object passing this. while the - // object exists no other threads may access the display. do not - // save the Display* beyond the lifetime of the CDisplayLock. + // get the opened screen and its root window. to get the display + // create a CDisplayLock object passing this. while the object + // exists no other threads may access the display. do not save + // the Display* beyond the lifetime of the CDisplayLock. int getScreen() const; - void getScreenShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const; Window getRoot() const; - // create a cursor that is transparent everywhere - Cursor createBlankCursor() const; + // initialize the clipboards + void initClipboards(Window); + + // update screen size cache + void updateScreenShape(); + + // get the shape of the screen + void getScreenShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + // get the current cursor position + void getCursorPos(SInt32& x, SInt32& y) const; + + // get the cursor center position + void getCursorCenter(SInt32& x, SInt32& y) const; + + // get a cursor that is transparent everywhere + Cursor getBlankCursor() const; // wait for and get the next X event. cancellable. bool getEvent(XEvent*) const; @@ -88,19 +102,6 @@ protected: CXWindowsScreenSaver* getScreenSaver() const; - // called by openDisplay() to allow subclasses to prepare the display. - // the display is locked and passed to the subclass. - virtual void onOpenDisplay(Display*) = 0; - - // called by openDisplay() after onOpenDisplay() to create each clipboard - virtual CXWindowsClipboard* - createClipboard(ClipboardID) = 0; - - // called by closeDisplay() to allow subclasses to clean up the display. - // the display is locked and passed to the subclass. note that the - // display may be NULL if the display has unexpectedly disconnected. - virtual void onCloseDisplay(Display*) = 0; - // called if the display is unexpectedly closing. default does nothing. virtual void onUnexpectedClose(); @@ -108,6 +109,9 @@ protected: virtual void onLostClipboard(ClipboardID) = 0; private: + // create the transparent cursor + void createBlankCursor(); + // remove a timer without locking void removeTimerNoLock(IJob*); @@ -248,6 +252,9 @@ private: // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; + // the transparent cursor + Cursor m_cursor; + // screen saver CXWindowsScreenSaver* m_screenSaver; diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index c56e93c7..3db7ebd1 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -82,11 +82,6 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary != NULL); assert(m_window == NULL); - // uninstall screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } - // done with hook library FreeLibrary(m_hookLibrary); } @@ -127,54 +122,74 @@ CMSWindowsPrimaryScreen::stop() void CMSWindowsPrimaryScreen::open() { - // open the display - openDisplay(); + assert(m_window == NULL); - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); - - // save cursor pos - POINT pos; - GetCursorPos(&pos); - m_x = pos.x; - m_y = pos.y; - - // send screen info CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - info.m_zoneSize = getJumpZoneSize(); - info.m_mx = m_x; - info.m_my = m_y; - m_receiver->onInfoChanged(info); + try { + // initialize hook library + m_threadID = GetCurrentThreadId(); + m_init(m_threadID); - // compute center pixel of primary screen - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + // open the display + openDisplay(); - // get keyboard state - updateKeys(); + // create and prepare our window + createWindow(); - // set jump zones - m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); + // set jump zones + m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); + + // collect screen 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(); + + // save mouse position + m_x = info.m_mx; + m_y = info.m_my; + + // compute center pixel of primary screen + getCursorCenter(m_xCenter, m_yCenter); + + // get keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + installScreenSaver(); + } + catch (...) { + close(); + throw; + } // enter the screen enterNoWarp(); + + // send screen info + m_receiver->onInfoChanged(info); } void CMSWindowsPrimaryScreen::close() { - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); + m_cleanup(); + m_threadID = 0; } void CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) { - log((CLOG_INFO "entering primary at %d,%d", x, y)); + log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); assert(m_active == true); + assert(m_window != NULL); // enter the screen enterNoWarp(); @@ -190,12 +205,13 @@ CMSWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); assert(m_active == false); + assert(m_window != NULL); // all messages prior to now are invalid nextMark(); - // save active window, show ours, and grab mouse/keyboard - if (!onLeave()) { + // show our window + if (!showWindow()) { return false; } @@ -229,6 +245,48 @@ CMSWindowsPrimaryScreen::leave() // local client now active m_active = true; + // make sure our idea of clipboard ownership is correct + checkClipboard(); + + return true; +} + +void +CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) +{ + m_setSides(activeSides); +} + +void +CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + // set the cursor position without generating an event +// FIXME -- doesn't this generate an event anyway? + SetCursorPos(x, y); + + // save position as last position + m_x = x; + 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. @@ -253,45 +311,13 @@ CMSWindowsPrimaryScreen::leave() // ignore } } - - return true; -} - -void -CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) -{ - m_setSides(activeSides); -} - -void -CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) -{ - // set the cursor position without generating an event - SetCursorPos(x, y); - - // save position as last position - m_x = x; - 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); } void CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, const IClipboard* src) { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard dst(m_window); @@ -301,10 +327,12 @@ CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, void CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard clipboard(m_window); if (clipboard.open(0)) { + // FIXME -- don't we need to empty it? clipboard.close(); } } @@ -313,6 +341,7 @@ void CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, IClipboard* dst) const { + // FIXME -- this is identical to CMSWindowsSecondaryScreen's code assert(m_window != NULL); CMSWindowsClipboard src(m_window); @@ -378,67 +407,6 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } -void -CMSWindowsPrimaryScreen::onOpenDisplay() -{ - assert(m_window == NULL); - - // save thread id. we'll need to pass this to the hook library. - m_threadID = GetCurrentThreadId(); - - // initialize hook library - m_init(m_threadID); - - try { - // install the screen saver hook - if (m_installScreenSaver != NULL) { - m_installScreenSaver(); - } - - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } - } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } - } - } - catch (...) { - m_cleanup(); - throw; - } -} - -void -CMSWindowsPrimaryScreen::onCloseDisplay() -{ - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - // uninstall the screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } - - // cleanup hook library - m_cleanup(); - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - bool CMSWindowsPrimaryScreen::onPreTranslate(MSG* msg) { @@ -667,8 +635,7 @@ CMSWindowsPrimaryScreen::onEvent(HWND hwnd, UINT msg, // do nothing if resolution hasn't changed if (x != xOld || y != yOld || w != wOld || h != hOld) { // recompute center pixel of primary screen - m_xCenter = GetSystemMetrics(SM_CXSCREEN) >> 1; - m_yCenter = GetSystemMetrics(SM_CYSCREEN) >> 1; + getCursorCenter(m_xCenter, m_yCenter); // warp mouse to center if active if (m_active) { @@ -720,33 +687,14 @@ CMSWindowsPrimaryScreen::enterNoWarp() m_setRelay(false); // restore active window and hide our window - onEnter(); + hideWindow(); // all messages prior to now are invalid nextMark(); } -void -CMSWindowsPrimaryScreen::onEnter() -{ - // restore the active window and hide our window. we can only set - // the active window for another thread if we first attach our input - // to that thread. - ReleaseCapture(); - if (m_lastActiveWindow != NULL) { - DWORD myThread = GetCurrentThreadId(); - if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { - // FIXME -- shouldn't raise window if X-Mouse is enabled - // but i have no idea how to do that or check if enabled. - SetActiveWindow(m_lastActiveWindow); - AttachThreadInput(myThread, m_lastActiveThread, FALSE); - } - } - ShowWindow(m_window, SW_HIDE); -} - bool -CMSWindowsPrimaryScreen::onLeave() +CMSWindowsPrimaryScreen::showWindow() { // remember the active window before we leave. GetActiveWindow() // will only return the active window for the thread's queue (i.e. @@ -776,6 +724,25 @@ CMSWindowsPrimaryScreen::onLeave() return true; } +void +CMSWindowsPrimaryScreen::hideWindow() +{ + // restore the active window and hide our window. we can only set + // the active window for another thread if we first attach our input + // to that thread. + ReleaseCapture(); + if (m_lastActiveWindow != NULL) { + DWORD myThread = GetCurrentThreadId(); + if (AttachThreadInput(myThread, m_lastActiveThread, TRUE)) { + // FIXME -- shouldn't raise window if X-Mouse is enabled + // but i have no idea how to do that or check if enabled. + SetActiveWindow(m_lastActiveWindow); + AttachThreadInput(myThread, m_lastActiveThread, FALSE); + } + } + ShowWindow(m_window, SW_HIDE); +} + SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const { @@ -792,6 +759,55 @@ CMSWindowsPrimaryScreen::nextMark() PostThreadMessage(m_threadID, SYNERGY_MSG_MARK, m_mark, 0); } +void +CMSWindowsPrimaryScreen::createWindow() +{ + // get the input desktop and switch to it + if (m_is95Family) { + if (!openDesktop()) { + throw XScreenOpenFailure(); + } + } + else { + if (!switchDesktop(openInputDesktop())) { + throw XScreenOpenFailure(); + } + } +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // disconnect from desktop + if (m_is95Family) { + closeDesktop(); + } + else { + switchDesktop(NULL); + } + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +void +CMSWindowsPrimaryScreen::installScreenSaver() +{ + // install the screen saver hook + if (m_installScreenSaver != NULL) { + m_installScreenSaver(); + } +} + +void +CMSWindowsPrimaryScreen::uninstallScreenSaver() +{ + // uninstall the screen saver hook + if (m_uninstallScreenSaver != NULL) { + m_uninstallScreenSaver(); + } +} + bool CMSWindowsPrimaryScreen::openDesktop() { @@ -835,7 +851,7 @@ CMSWindowsPrimaryScreen::closeDesktop() if (m_window != NULL) { // restore active window and hide ours if (m_active) { - onEnter(); + hideWindow(); } // first remove clipboard snooper @@ -866,7 +882,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) if (m_window != NULL) { // restore active window and hide ours if (m_active) { - onEnter(); + hideWindow(); } // first remove clipboard snooper @@ -948,7 +964,7 @@ CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) // get active window and show ours if (m_active) { - onLeave(); + showWindow(); } else { // watch jump zones diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index e0814917..8b083fd5 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -42,8 +42,8 @@ protected: private: void enterNoWarp(); - void onEnter(); - bool onLeave(); + bool showWindow(); + void hideWindow(); SInt32 getJumpZoneSize() const; @@ -51,9 +51,23 @@ private: // motion deltas while mouse is on secondary screen). void warpCursorToCenter(); + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + // discard posted messages void nextMark(); + // create/destroy window + // also attach to desktop; this destroys and recreates the window + // as necessary. + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); + // open/close desktop (for windows 95/98/me) bool openDesktop(); void closeDesktop(); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index 0c7b10dc..fd490586 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -6,6 +6,7 @@ #include "CXWindowsUtil.h" #include "CClipboard.h" #include "ProtocolTypes.h" +#include "XScreen.h" #include "CThread.h" #include "CLog.h" #include "CStopwatch.h" @@ -179,9 +180,9 @@ CXWindowsPrimaryScreen::run() if (xevent.xmotion.send_event) { // we warped the mouse. discard events until we // find the matching sent event. see - // warpCursorNoLockNoFlush() for where the events - // are sent. we discard the matching sent event - // and can be sure we've skipped the warp event. + // 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); @@ -210,7 +211,7 @@ CXWindowsPrimaryScreen::run() xevent.xmotion.y_root - m_yCenter < -s_size || xevent.xmotion.y_root - m_yCenter > s_size) { CDisplayLock display(this); - warpCursorNoLockNoFlush(display, m_xCenter, m_yCenter); + warpCursorNoFlush(display, m_xCenter, m_yCenter); } // send event if mouse moved. do this after warping @@ -239,69 +240,67 @@ CXWindowsPrimaryScreen::stop() void CXWindowsPrimaryScreen::open() { - // open the display - openDisplay(); + assert(m_window == None); - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; + CClientInfo info; + try { + // open the display + openDisplay(); - // get screen shape - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); + // create and prepare our window + createWindow(); - { + // get the display CDisplayLock display(this); - // get notified of screen saver activation/deactivation + // initialize the clipboards + initClipboards(m_window); + + // miscellaneous initialization m_atomScreenSaver = XInternAtom(display, "SCREENSAVER", False); - getScreenSaver()->setNotify(m_window); + + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; + + // collect screen 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(); + + // save mouse position + m_x = info.m_mx; + m_y = info.m_my; + + // compute center pixel of primary screen + getCursorCenter(m_xCenter, m_yCenter); // update key state updateModifierMap(display); - // get mouse position - Window root, window; - int mx, my, xWindow, yWindow; - unsigned int mask; - if (!XQueryPointer(display, m_window, &root, &window, - &mx, &my, &xWindow, &yWindow, &mask)) { - mx = w >> 1; - my = h >> 1; - } - - // save mouse position - m_x = mx; - m_y = my; + // get notified of screen saver activation/deactivation + installScreenSaver(); + } + catch (...) { + close(); + throw; } - // save position of center of screen - m_xCenter = x + (w >> 1); - m_yCenter = y + (h >> 1); + // enter the screen + enterNoWarp(); // send screen info - CClientInfo info; - info.m_x = x; - info.m_y = y; - info.m_w = w; - info.m_h = h; - info.m_zoneSize = 1; - info.m_mx = m_x; - info.m_my = m_y; m_receiver->onInfoChanged(info); } void CXWindowsPrimaryScreen::close() { - // stop being notified of screen saver activation/deactivation - getScreenSaver()->setNotify(None); - m_atomScreenSaver = None; - - // close the display + uninstallScreenSaver(); + destroyWindow(); closeDisplay(); } @@ -312,24 +311,20 @@ CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) assert(m_active == true); assert(m_window != None); - CDisplayLock display(this); - - // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(display, m_window); + // enter the screen + enterNoWarp(); // warp to requested location if (!forScreenSaver) { - warpCursorNoLock(display, x, y); + warpCursor(x, y); } // redirect input to root window. do not warp the mouse because // that will deactivate the screen saver. else { + CDisplayLock display(this); XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); } - - // not active anymore - m_active = false; } bool @@ -339,6 +334,116 @@ CXWindowsPrimaryScreen::leave() assert(m_active == false); assert(m_window != None); + // show our window + if (!showWindow()) { + return false; + } + + // warp mouse to center + warpCursorToCenter(); + // FIXME -- this doesn't match the win32 version. that just does + // the warp while we flush the input queue until we find the warp + // and we discard that too. would prefer to at least match our + // own warping when we receive MotionNotify; that just does the + // warp. however, the win32 version sometimes stutters when + // leaving and perhaps this is why. hmm, win32 does ignore the + // events until after the warp (via the mark). + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + checkClipboard(); + + return true; +} + +void +CXWindowsPrimaryScreen::reconfigure(UInt32) +{ + // do nothing +} + +void +CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +{ + CDisplayLock display(this); + + // warp mouse + warpCursorNoFlush(display, x, y); + + // remove all input events before and including warp + XEvent event; + while (XCheckMaskEvent(display, PointerMotionMask | + ButtonPressMask | ButtonReleaseMask | + KeyPressMask | KeyReleaseMask | + KeymapStateMask, + &event)) { + // do nothing + } + + // save position as last position + m_x = x; + 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 @@ -387,82 +492,89 @@ CXWindowsPrimaryScreen::leave() } while (result != GrabSuccess); log((CLOG_DEBUG1 "grabbed pointer and keyboard")); - // warp mouse to center - warpCursorNoLock(display, m_xCenter, m_yCenter); - - // local client now active - m_active = true; - return true; } void -CXWindowsPrimaryScreen::reconfigure(UInt32) -{ - // do nothing -} - -void -CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::hideWindow() { CDisplayLock display(this); - warpCursorNoLock(display, x, y); + + // unmap the grab window. this also ungrabs the mouse and keyboard. + XUnmapWindow(display, m_window); +} + +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; } void -CXWindowsPrimaryScreen::warpCursorNoLock(Display* display, SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::createWindow() { - // warp mouse - warpCursorNoLockNoFlush(display, x, y); + assert(m_window == None); - // remove all input events before and including warp - XEvent event; - while (XCheckMaskEvent(display, PointerMotionMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask, - &event)) { - // do nothing + // 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)); - // save position as last position - m_x = x; - m_y = y; + // start watching for events on other windows + selectEvents(display, getRoot()); } void -CXWindowsPrimaryScreen::warpCursorNoLockNoFlush( - Display* display, SInt32 x, SInt32 y) +CXWindowsPrimaryScreen::destroyWindow() { - assert(display != NULL); - assert(m_window != None); + // 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; +} - // 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); +void +CXWindowsPrimaryScreen::installScreenSaver() +{ + assert(getScreenSaver() != NULL); - // warp mouse - XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + getScreenSaver()->setNotify(m_window); +} - // 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::uninstallScreenSaver() +{ + // stop being notified of screen saver activation/deactivation + if (getScreenSaver() != NULL) { + getScreenSaver()->setNotify(None); + } + m_atomScreenSaver = None; } void @@ -491,7 +603,6 @@ CXWindowsPrimaryScreen::getToggleMask() const CDisplayLock display(this); // query the pointer to get the keyboard state - // FIXME -- is there a better way to do this? Window root, window; int xRoot, yRoot, xWindow, yWindow; unsigned int state; @@ -548,56 +659,6 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -void -CXWindowsPrimaryScreen::onOpenDisplay(Display* display) -{ - assert(m_window == None); - - // get size of screen - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the grab window. this window is used to capture user - // input when the user is focussed on another client. don't let - // the window manager mess with it. - XSetWindowAttributes attr; - attr.event_mask = PointerMotionMask |// PointerMotionHintMask | - ButtonPressMask | ButtonReleaseMask | - KeyPressMask | KeyReleaseMask | - KeymapStateMask | PropertyChangeMask; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = createBlankCursor(); - m_window = XCreateWindow(display, getRoot(), x, y, w, h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - log((CLOG_DEBUG "window is 0x%08x", m_window)); - - // start watching for events on other windows - selectEvents(display, getRoot()); -} - -CXWindowsClipboard* -CXWindowsPrimaryScreen::createClipboard(ClipboardID id) -{ - CDisplayLock display(this); - return new CXWindowsClipboard(display, m_window, id); -} - -void -CXWindowsPrimaryScreen::onCloseDisplay(Display* display) -{ - assert(m_window != None); - - // destroy window - if (display != NULL) { - XDestroyWindow(display, m_window); - } - m_window = None; -} - void CXWindowsPrimaryScreen::onUnexpectedClose() { diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 0d4b2ac7..b7eb345a 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -30,21 +30,35 @@ public: protected: // CXWindowsScreen overrides - virtual void onOpenDisplay(Display*); - virtual CXWindowsClipboard* - createClipboard(ClipboardID); - virtual void onCloseDisplay(Display*); virtual void onUnexpectedClose(); virtual void onLostClipboard(ClipboardID); private: void selectEvents(Display*, Window) const; void doSelectEvents(Display*, Window) const; - void warpCursorNoLock(Display*, - SInt32 xAbsolute, SInt32 yAbsolute); - void warpCursorNoLockNoFlush(Display*, + + void enterNoWarp(); + bool showWindow(); + void hideWindow(); + + SInt32 getJumpZoneSize() const; + + void warpCursorToCenter(); + void warpCursorNoFlush(Display*, SInt32 xAbsolute, SInt32 yAbsolute); + // check clipboard ownership and, if necessary, tell the receiver + // of a grab. + void checkClipboard(); + + // create/destroy window + void createWindow(); + void destroyWindow(); + + // start/stop watch for screen saver changes + void installScreenSaver(); + void uninstallScreenSaver(); + KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const;