diff --git a/synergy/CXWindowsPrimaryScreen.cpp b/synergy/CXWindowsPrimaryScreen.cpp index 0521210a..39595354 100644 --- a/synergy/CXWindowsPrimaryScreen.cpp +++ b/synergy/CXWindowsPrimaryScreen.cpp @@ -1,8 +1,6 @@ #include "CXWindowsPrimaryScreen.h" #include "CServer.h" #include "CThread.h" -#include "CLock.h" -#include "TMethodJob.h" #include "CLog.h" #include #include @@ -13,17 +11,15 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen() : m_server(NULL), - m_display(NULL), - m_w(0), m_h(0), - m_window(None), - m_active(false) + m_active(false), + m_window(None) { // do nothing } CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { - assert(m_display == NULL); + assert(m_window == None); } void CXWindowsPrimaryScreen::open(CServer* server) @@ -35,90 +31,37 @@ void CXWindowsPrimaryScreen::open(CServer* server) m_server = server; // open the display - log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = XOpenDisplay(NULL); // FIXME -- allow non-default - if (m_display == NULL) - throw int(5); // FIXME -- make exception for this - - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); - - // get screen size - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "primary display size: %dx%d", m_w, m_h)); - - // get the root window - m_root = RootWindow(m_display, m_screen); - - // 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; - attr.do_not_propagate_mask = 0; - attr.override_redirect = True; - attr.cursor = createBlankCursor(); - m_window = XCreateWindow(m_display, m_root, 0, 0, m_w, m_h, 0, 0, - InputOnly, CopyFromParent, - CWDontPropagate | CWEventMask | - CWOverrideRedirect | CWCursor, - &attr); - - // start watching for events on other windows - selectEvents(m_root); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsPrimaryScreen::eventThread)); + openDisplay(); } void CXWindowsPrimaryScreen::close() { assert(m_server != NULL); - assert(m_window != None); - assert(m_eventThread != NULL); - - // stop event thread - log((CLOG_DEBUG "stopping event thread")); - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; - log((CLOG_DEBUG "stopped event thread")); - - // destroy window - XDestroyWindow(m_display, m_window); - m_window = None; // close the display - XCloseDisplay(m_display); - m_display = NULL; - log((CLOG_DEBUG "closed display")); + closeDisplay(); + + // done with server + m_server = NULL; } void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) { log((CLOG_INFO "entering primary at %d,%d", x, y)); - assert(m_display != NULL); - assert(m_window != None); - assert(m_active == true); + assert(m_active == true); + assert(m_window != None); - CLock lock(&m_mutex); + CDisplayLock display(this); // warp to requested location - XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, x, y); + XWarpPointer(display, None, m_window, 0, 0, 0, 0, x, y); // unmap the grab window. this also ungrabs the mouse and keyboard. - XUnmapWindow(m_display, m_window); + XUnmapWindow(display, m_window); // remove all input events for grab window XEvent event; - while (XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(display, m_window, PointerMotionMask | ButtonPressMask | ButtonReleaseMask | KeyPressMask | KeyReleaseMask | @@ -134,14 +77,13 @@ void CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y) void CXWindowsPrimaryScreen::leave() { log((CLOG_INFO "leaving primary")); - assert(m_display != NULL); - assert(m_window != None); - assert(m_active == false); + assert(m_active == false); + assert(m_window != None); - CLock lock(&m_mutex); + CDisplayLock display(this); // raise and show the input window - XMapRaised(m_display, m_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 @@ -150,7 +92,7 @@ void CXWindowsPrimaryScreen::leave() do { // mouse first do { - result = XGrabPointer(m_display, m_window, True, 0, + result = XGrabPointer(display, m_window, True, 0, GrabModeAsync, GrabModeAsync, m_window, None, CurrentTime); assert(result != GrabNotViewable); @@ -162,11 +104,12 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed pointer")); // now the keyboard - result = XGrabKeyboard(m_display, m_window, True, + result = XGrabKeyboard(display, m_window, True, GrabModeAsync, GrabModeAsync, CurrentTime); assert(result != GrabNotViewable); if (result != GrabSuccess) { - XUngrabPointer(m_display, CurrentTime); + // back off to avoid grab deadlock + XUngrabPointer(display, CurrentTime); log((CLOG_DEBUG "ungrabbed pointer, waiting to grab keyboard")); CThread::sleep(0.25); } @@ -174,7 +117,9 @@ void CXWindowsPrimaryScreen::leave() log((CLOG_DEBUG "grabbed keyboard")); // move the mouse to the center of grab window - warpCursorNoLock(m_w >> 1, m_h >> 1); + SInt32 w, h; + getScreenSize(&w, &h); + warpCursorNoLock(display, w >> 1, h >> 1); // local client now active m_active = true; @@ -182,21 +127,24 @@ void CXWindowsPrimaryScreen::leave() void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - CLock lock(&m_mutex); - warpCursorNoLock(x, y); + CDisplayLock display(this); + warpCursorNoLock(display, x, y); } void CXWindowsPrimaryScreen::warpCursorNoLock( - SInt32 x, SInt32 y) + Display* display, SInt32 x, SInt32 y) { + assert(display != NULL); + assert(m_window != None); + // warp the mouse - XWarpPointer(m_display, None, m_root, 0, 0, 0, 0, x, y); - XSync(m_display, False); + XWarpPointer(display, None, getRoot(), 0, 0, 0, 0, x, y); + XSync(display, False); log((CLOG_DEBUG "warped to %d,%d", x, y)); // discard mouse events since we just added one we don't want XEvent xevent; - while (XCheckWindowEvent(m_display, m_window, + while (XCheckWindowEvent(display, m_window, PointerMotionMask, &xevent)) { // do nothing } @@ -205,21 +153,57 @@ void CXWindowsPrimaryScreen::warpCursorNoLock( void CXWindowsPrimaryScreen::getSize( SInt32* width, SInt32* height) const { - assert(m_display != NULL); - assert(width != NULL && height != NULL); - - *width = m_w; - *height = m_h; + getScreenSize(width, height); } SInt32 CXWindowsPrimaryScreen::getJumpZoneSize() const { - assert(m_display != NULL); - return 1; } -void CXWindowsPrimaryScreen::selectEvents(Window w) const +void CXWindowsPrimaryScreen::onOpenDisplay() +{ + assert(m_window == None); + + CDisplayLock display(this); + + // get size of screen + SInt32 w, h; + getScreenSize(&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; + attr.do_not_propagate_mask = 0; + attr.override_redirect = True; + attr.cursor = createBlankCursor(); + m_window = XCreateWindow(display, getRoot(), 0, 0, w, h, 0, 0, + InputOnly, CopyFromParent, + CWDontPropagate | CWEventMask | + CWOverrideRedirect | CWCursor, + &attr); + + // start watching for events on other windows + selectEvents(display, getRoot()); +} + +void CXWindowsPrimaryScreen::onCloseDisplay() +{ + assert(m_window != None); + + // destroy window + CDisplayLock display(this); + XDestroyWindow(display, m_window); + m_window = None; +} + +void CXWindowsPrimaryScreen::selectEvents( + Display* display, Window w) const { // we want to track the mouse everywhere on the display. to achieve // that we select PointerMotionMask on every window. we also select @@ -231,73 +215,31 @@ void CXWindowsPrimaryScreen::selectEvents(Window w) const return; // select events of interest - XSelectInput(m_display, w, PointerMotionMask | SubstructureNotifyMask); + XSelectInput(display, w, PointerMotionMask | SubstructureNotifyMask); // recurse on child windows Window rw, pw, *cw; unsigned int nc; - if (XQueryTree(m_display, w, &rw, &pw, &cw, &nc)) { + if (XQueryTree(display, w, &rw, &pw, &cw, &nc)) { for (unsigned int i = 0; i < nc; ++i) - selectEvents(cw[i]); + selectEvents(display, cw[i]); XFree(cw); } } -Cursor CXWindowsPrimaryScreen::createBlankCursor() -{ - // this seems just a bit more complicated than really necessary - - // get the closet cursor size to 1x1 - unsigned int w, h; - XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); - - // make bitmap data for cursor of closet size. since the cursor - // is blank we can use the same bitmap for shape and mask: all - // zeros. - const int size = ((w + 7) >> 3) * h; - char* data = new char[size]; - memset(data, 0, size); - - // make bitmap - Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); - - // need an arbitrary color for the cursor - XColor color; - color.pixel = 0; - color.red = color.green = color.blue = 0; - color.flags = DoRed | DoGreen | DoBlue; - - // make cursor from bitmap - Cursor 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; -} - void CXWindowsPrimaryScreen::eventThread(void*) { for (;;) { - // wait for and then get the next event - m_mutex.lock(); - while (XPending(m_display) == 0) { - m_mutex.unlock(); - CThread::sleep(0.05); - m_mutex.lock(); - } + // wait for and get the next event XEvent xevent; - XNextEvent(m_display, &xevent); - m_mutex.unlock(); + getEvent(&xevent); // handle event switch (xevent.type) { case CreateNotify: { // select events on new window - CLock lock(&m_mutex); - selectEvents(xevent.xcreatewindow.window); + CDisplayLock display(this); + selectEvents(display, xevent.xcreatewindow.window); break; } @@ -355,19 +297,22 @@ void CXWindowsPrimaryScreen::eventThread(void*) // get mouse deltas { - CLock lock(&m_mutex); + CDisplayLock display(this); Window root, window; int xRoot, yRoot, xWindow, yWindow; unsigned int mask; - if (!XQueryPointer(m_display, m_window, &root, &window, + if (!XQueryPointer(display, m_window, &root, &window, &xRoot, &yRoot, &xWindow, &yWindow, &mask)) break; - x = xRoot - (m_w >> 1); - y = yRoot - (m_h >> 1); + // compute position of center of window + SInt32 w, h; + getScreenSize(&w, &h); + x = xRoot - (w >> 1); + y = yRoot - (h >> 1); // warp mouse back to center - warpCursorNoLock(m_w >> 1, m_h >> 1); + warpCursorNoLock(display, w >> 1, h >> 1); } m_server->onMouseMoveSecondary(x, y); @@ -422,7 +367,8 @@ KeyID CXWindowsPrimaryScreen::mapKey( index = 1; else index = 0; - return static_cast(XKeycodeToKeysym(m_display, keycode, index)); + CDisplayLock display(this); + return static_cast(XKeycodeToKeysym(display, keycode, index)); } ButtonID CXWindowsPrimaryScreen::mapButton( diff --git a/synergy/CXWindowsPrimaryScreen.h b/synergy/CXWindowsPrimaryScreen.h index 0daa3342..fd8883d8 100644 --- a/synergy/CXWindowsPrimaryScreen.h +++ b/synergy/CXWindowsPrimaryScreen.h @@ -1,15 +1,12 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H -#include "CMutex.h" #include "KeyTypes.h" #include "MouseTypes.h" +#include "CXWindowsScreen.h" #include "IPrimaryScreen.h" -#include -class CThread; - -class CXWindowsPrimaryScreen : public IPrimaryScreen { +class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { public: CXWindowsPrimaryScreen(); virtual ~CXWindowsPrimaryScreen(); @@ -23,28 +20,25 @@ class CXWindowsPrimaryScreen : public IPrimaryScreen { virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; - private: - void selectEvents(Window) const; - Cursor createBlankCursor(); - void warpCursorNoLock(SInt32 xAbsolute, SInt32 yAbsolute); + protected: + // CXWindowsScreen overrides + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + virtual void eventThread(void*); + + private: + void selectEvents(Display*, Window) const; + void warpCursorNoLock(Display*, + SInt32 xAbsolute, SInt32 yAbsolute); - void eventThread(void*); KeyModifierMask mapModifier(unsigned int state) const; KeyID mapKey(KeyCode, KeyModifierMask) const; ButtonID mapButton(unsigned int button) const; private: CServer* m_server; - CThread* m_eventThread; - Display* m_display; - int m_screen; - Window m_root; - SInt32 m_w, m_h; - Window m_window; bool m_active; - - // X is not thread safe - CMutex m_mutex; + Window m_window; }; #endif diff --git a/synergy/CXWindowsScreen.cpp b/synergy/CXWindowsScreen.cpp new file mode 100644 index 00000000..1ec8413f --- /dev/null +++ b/synergy/CXWindowsScreen.cpp @@ -0,0 +1,172 @@ +#include "CXWindowsScreen.h" +#include "CThread.h" +#include "CLock.h" +#include "TMethodJob.h" +#include "CLog.h" +#include +#include +#include + +// +// CXWindowsScreen +// + +CXWindowsScreen::CXWindowsScreen() : + m_display(NULL), + m_root(None), + m_w(0), m_h(0) +{ + // do nothing +} + +CXWindowsScreen::~CXWindowsScreen() +{ + assert(m_display == NULL); +} + +void CXWindowsScreen::openDisplay() +{ + assert(m_display == NULL); + + // open the display + log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); + m_display = XOpenDisplay(NULL); // FIXME -- allow non-default + if (m_display == NULL) + throw int(5); // FIXME -- make exception for this + + // get default screen + m_screen = DefaultScreen(m_display); + Screen* screen = ScreenOfDisplay(m_display, m_screen); + + // get screen size + m_w = WidthOfScreen(screen); + m_h = HeightOfScreen(screen); + log((CLOG_INFO "display size: %dx%d", m_w, m_h)); + + // get the root window + m_root = RootWindow(m_display, m_screen); + + // let subclass prep display + onOpenDisplay(); + + // start processing events + m_eventThread = new CThread(new TMethodJob( + this, &CXWindowsScreen::eventThread)); +} + +void CXWindowsScreen::closeDisplay() +{ + assert(m_display != NULL); + assert(m_eventThread != NULL); + + // stop event thread + log((CLOG_DEBUG "stopping event thread")); + m_eventThread->cancel(); + m_eventThread->wait(); + delete m_eventThread; + m_eventThread = NULL; + log((CLOG_DEBUG "stopped event thread")); + + // let subclass close down display + onCloseDisplay(); + + // close the display + XCloseDisplay(m_display); + m_display = NULL; + log((CLOG_DEBUG "closed display")); +} + +int CXWindowsScreen::getScreen() const +{ + assert(m_display != NULL); + return m_screen; +} + +Window CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +void CXWindowsScreen::getScreenSize( + SInt32* w, SInt32* h) const +{ + assert(m_display != NULL); + assert(w != NULL && h != NULL); + + *w = m_w; + *h = m_h; +} + +Cursor CXWindowsScreen::createBlankCursor() const +{ + // this seems just a bit more complicated than really necessary + + // get the closet cursor size to 1x1 + unsigned int w, h; + XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); + + // make bitmap data for cursor of closet size. since the cursor + // is blank we can use the same bitmap for shape and mask: all + // zeros. + const int size = ((w + 7) >> 3) * h; + char* data = new char[size]; + memset(data, 0, size); + + // make bitmap + Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); + + // need an arbitrary color for the cursor + XColor color; + color.pixel = 0; + color.red = color.green = color.blue = 0; + color.flags = DoRed | DoGreen | DoBlue; + + // make cursor from bitmap + Cursor 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; +} + +void 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(); + while (XPending(m_display) == 0) { + m_mutex.unlock(); + CThread::sleep(0.05); + m_mutex.lock(); + } + XNextEvent(m_display, xevent); + m_mutex.unlock(); +} + + +// +// CXWindowsScreen::CDisplayLock +// + +CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : + m_mutex(&screen->m_mutex), + m_display(screen->m_display) +{ + assert(m_display != NULL); + + m_mutex->lock(); +} + +CXWindowsScreen::CDisplayLock::~CDisplayLock() +{ + m_mutex->unlock(); +} + +CXWindowsScreen::CDisplayLock::operator Display*() const +{ + return m_display; +} diff --git a/synergy/CXWindowsScreen.h b/synergy/CXWindowsScreen.h new file mode 100644 index 00000000..6ad39859 --- /dev/null +++ b/synergy/CXWindowsScreen.h @@ -0,0 +1,73 @@ +#ifndef CXWINDOWSSCREEN_H +#define CXWINDOWSSCREEN_H + +#include "CMutex.h" +#include "BasicTypes.h" +#include + +class CThread; + +class CXWindowsScreen { + public: + CXWindowsScreen(); + virtual ~CXWindowsScreen(); + + protected: + class CDisplayLock { + public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + + private: + const CMutex* m_mutex; + Display* m_display; + }; + friend class CDisplayLock; + + // open the X display. calls onOpenDisplay() after opening the display, + // getting the screen, its size, and root window. then it starts the + // event thread. + void openDisplay(); + + // destroy the window and close the display. calls onCloseDisplay() + // after the event thread has been shut down but before the display + // is closed. + void closeDisplay(); + + // get the opened screen, its size, 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 getScreenSize(SInt32* w, SInt32* h) const; + Window getRoot() const; + + // create a cursor that is transparent everywhere + Cursor createBlankCursor() const; + + // wait for and get the next X event. cancellable. + void getEvent(XEvent*) const; + + // called by openDisplay() to allow subclasses to prepare the display + virtual void onOpenDisplay() = 0; + + // called by closeDisplay() to + virtual void onCloseDisplay() = 0; + + // override to process X events + virtual void eventThread(void*) = 0; + + private: + CThread* m_eventThread; + Display* m_display; + int m_screen; + Window m_root; + SInt32 m_w, m_h; + + // X is not thread safe + CMutex m_mutex; +}; + +#endif diff --git a/synergy/CXWindowsSecondaryScreen.cpp b/synergy/CXWindowsSecondaryScreen.cpp index 62934590..853855a6 100644 --- a/synergy/CXWindowsSecondaryScreen.cpp +++ b/synergy/CXWindowsSecondaryScreen.cpp @@ -1,8 +1,6 @@ #include "CXWindowsSecondaryScreen.h" #include "CClient.h" #include "CThread.h" -#include "CLock.h" -#include "TMethodJob.h" #include "CLog.h" #include #include @@ -14,16 +12,14 @@ CXWindowsSecondaryScreen::CXWindowsSecondaryScreen() : m_client(NULL), - m_display(NULL), - m_window(None), - m_w(0), m_h(0) + m_window(None) { // do nothing } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { - assert(m_display == NULL); + assert(m_window == None); } void CXWindowsSecondaryScreen::open(CClient* client) @@ -35,28 +31,113 @@ void CXWindowsSecondaryScreen::open(CClient* client) m_client = client; // open the display - log((CLOG_DEBUG "XOpenDisplay(%s)", "NULL")); - m_display = XOpenDisplay(NULL); // FIXME -- allow non-default - if (m_display == NULL) - throw int(5); // FIXME -- make exception for this - - // get default screen - m_screen = DefaultScreen(m_display); - Screen* screen = ScreenOfDisplay(m_display, m_screen); - - // get screen size - m_w = WidthOfScreen(screen); - m_h = HeightOfScreen(screen); - log((CLOG_INFO "secondary display size: %dx%d", m_w, m_h)); + openDisplay(); // verify the availability of the XTest extension + CDisplayLock display(this); int majorOpcode, firstEvent, firstError; - if (!XQueryExtension(m_display, XTestExtensionName, + if (!XQueryExtension(display, XTestExtensionName, &majorOpcode, &firstEvent, &firstError)) throw int(6); // FIXME -- make exception for this +} - // get the root window - m_root = RootWindow(m_display, m_screen); +void CXWindowsSecondaryScreen::close() +{ + assert(m_client != NULL); + + // close the display + closeDisplay(); + + // done with client + m_client = NULL; +} + +void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) +{ + assert(m_window != None); + + CDisplayLock display(this); + + // warp to requested location + XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); + XSync(display, False); + + // show cursor + XUnmapWindow(display, m_window); +} + +void CXWindowsSecondaryScreen::leave() +{ + CDisplayLock display(this); + leaveNoLock(display); +} + +void CXWindowsSecondaryScreen::keyDown( + KeyID key, KeyModifierMask mask) +{ + CDisplayLock display(this); + XTestFakeKeyEvent(display, mapKey(key, mask), True, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::keyRepeat( + KeyID, KeyModifierMask, SInt32) +{ + CDisplayLock display(this); + // FIXME +} + +void CXWindowsSecondaryScreen::keyUp( + KeyID key, KeyModifierMask mask) +{ + CDisplayLock display(this); + XTestFakeKeyEvent(display, mapKey(key, mask), False, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseDown(ButtonID button) +{ + CDisplayLock display(this); + XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseUp(ButtonID button) +{ + CDisplayLock display(this); + XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) +{ + CDisplayLock display(this); + XTestFakeMotionEvent(display, getScreen(), x, y, CurrentTime); + XSync(display, False); +} + +void CXWindowsSecondaryScreen::mouseWheel(SInt32) +{ + CDisplayLock display(this); + // FIXME +} + +void CXWindowsSecondaryScreen::getSize( + SInt32* width, SInt32* height) const +{ + getScreenSize(width, height); +} + +SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void CXWindowsSecondaryScreen::onOpenDisplay() +{ + assert(m_window == None); + + CDisplayLock display(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 @@ -67,230 +148,48 @@ void CXWindowsSecondaryScreen::open(CClient* client) attr.do_not_propagate_mask = 0; attr.override_redirect = True; attr.cursor = createBlankCursor(); - m_window = XCreateWindow(m_display, m_root, 0, 0, 1, 1, 0, 0, + m_window = XCreateWindow(display, getRoot(), 0, 0, 1, 1, 0, 0, InputOnly, CopyFromParent, CWDontPropagate | CWEventMask | CWOverrideRedirect | CWCursor, &attr); // become impervious to server grabs - XTestGrabControl(m_display, True); + XTestGrabControl(display, True); // hide the cursor - leave(); - - // start processing events - m_eventThread = new CThread(new TMethodJob( - this, &CXWindowsSecondaryScreen::eventThread)); + leaveNoLock(display); } -void CXWindowsSecondaryScreen::close() +void CXWindowsSecondaryScreen::onCloseDisplay() { - assert(m_client != NULL); - assert(m_eventThread != NULL); - - // stop event thread - m_eventThread->cancel(); - m_eventThread->wait(); - delete m_eventThread; - m_eventThread = NULL; + assert(m_window != None); // no longer impervious to server grabs - XTestGrabControl(m_display, False); + CDisplayLock display(this); + XTestGrabControl(display, False); // destroy window - XDestroyWindow(m_display, m_window); + XDestroyWindow(display, m_window); m_window = None; - - // close the display - XCloseDisplay(m_display); - m_display = NULL; -} - -void CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - assert(m_window != None); - - CLock lock(&m_mutex); - - // warp to requested location - XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - XSync(m_display, False); - - // show cursor - XUnmapWindow(m_display, m_window); -} - -void CXWindowsSecondaryScreen::leave() -{ - assert(m_display != NULL); - assert(m_window != None); - - CLock lock(&m_mutex); - - // move hider window under the mouse (rather than moving the mouse - // somewhere else on the screen) - int x, y, dummy; - unsigned int dummyMask; - Window dummyWindow; - XQueryPointer(m_display, m_root, &dummyWindow, &dummyWindow, - &x, &y, &dummy, &dummy, &dummyMask); - XMoveWindow(m_display, m_window, x, y); - - // raise and show the hider window - XMapRaised(m_display, m_window); - - // hide cursor by moving it into the hider window - XWarpPointer(m_display, None, m_window, 0, 0, 0, 0, 0, 0); -} - -void CXWindowsSecondaryScreen::keyDown( - KeyID key, KeyModifierMask mask) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeKeyEvent(m_display, mapKey(key, mask), True, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::keyRepeat( - KeyID, KeyModifierMask, SInt32) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - // FIXME -} - -void CXWindowsSecondaryScreen::keyUp( - KeyID key, KeyModifierMask mask) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeKeyEvent(m_display, mapKey(key, mask), False, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseDown(ButtonID button) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeButtonEvent(m_display, mapButton(button), True, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseUp(ButtonID button) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeButtonEvent(m_display, mapButton(button), False, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - XTestFakeMotionEvent(m_display, m_screen, x, y, CurrentTime); - XSync(m_display, False); -} - -void CXWindowsSecondaryScreen::mouseWheel(SInt32) -{ - assert(m_display != NULL); - - CLock lock(&m_mutex); - - // FIXME -} - -void CXWindowsSecondaryScreen::getSize( - SInt32* width, SInt32* height) const -{ - assert(m_display != NULL); - assert(width != NULL && height != NULL); - - *width = m_w; - *height = m_h; -} - -SInt32 CXWindowsSecondaryScreen::getJumpZoneSize() const -{ - assert(m_display != NULL); - - return 0; -} - -Cursor CXWindowsSecondaryScreen::createBlankCursor() -{ - // this seems just a bit more complicated than really necessary - - // get the closet cursor size to 1x1 - unsigned int w, h; - XQueryBestCursor(m_display, m_root, 1, 1, &w, &h); - - // make bitmap data for cursor of closet size. since the cursor - // is blank we can use the same bitmap for shape and mask: all - // zeros. - const int size = ((w + 7) >> 3) * h; - char* data = new char[size]; - memset(data, 0, size); - - // make bitmap - Pixmap bitmap = XCreateBitmapFromData(m_display, m_root, data, w, h); - - // need an arbitrary color for the cursor - XColor color; - color.pixel = 0; - color.red = color.green = color.blue = 0; - color.flags = DoRed | DoGreen | DoBlue; - - // make cursor from bitmap - Cursor 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; } void CXWindowsSecondaryScreen::eventThread(void*) { - assert(m_display != NULL); assert(m_window != None); for (;;) { - // wait for and then get the next event - m_mutex.lock(); - while (XPending(m_display) == 0) { - m_mutex.unlock(); - CThread::sleep(0.05); - m_mutex.lock(); - } + // wait for and get the next event XEvent xevent; - XNextEvent(m_display, &xevent); - m_mutex.unlock(); + getEvent(&xevent); // handle event switch (xevent.type) { case LeaveNotify: { // mouse moved out of hider window somehow. hide the window. - CLock lock(&m_mutex); - XUnmapWindow(m_display, m_window); + assert(m_window != None); + CDisplayLock display(this); + XUnmapWindow(display, m_window); break; } @@ -313,11 +212,33 @@ void CXWindowsSecondaryScreen::eventThread(void*) } } +void CXWindowsSecondaryScreen::leaveNoLock(Display* display) +{ + assert(display != NULL); + assert(m_window != None); + + // move hider window under the mouse (rather than moving the mouse + // somewhere else on the screen) + int x, y, dummy; + unsigned int dummyMask; + Window dummyWindow; + XQueryPointer(display, getRoot(), &dummyWindow, &dummyWindow, + &x, &y, &dummy, &dummy, &dummyMask); + XMoveWindow(display, m_window, x, y); + + // raise and show the hider window + XMapRaised(display, m_window); + + // hide cursor by moving it into the hider window + XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); +} + KeyCode CXWindowsSecondaryScreen::mapKey( KeyID id, KeyModifierMask /*mask*/) const { + CDisplayLock display(this); // FIXME -- use mask - return XKeysymToKeycode(m_display, static_cast(id)); + return XKeysymToKeycode(display, static_cast(id)); } unsigned int CXWindowsSecondaryScreen::mapButton( diff --git a/synergy/CXWindowsSecondaryScreen.h b/synergy/CXWindowsSecondaryScreen.h index d37efc05..ef180537 100644 --- a/synergy/CXWindowsSecondaryScreen.h +++ b/synergy/CXWindowsSecondaryScreen.h @@ -1,13 +1,10 @@ #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H -#include "CMutex.h" +#include "CXWindowsScreen.h" #include "ISecondaryScreen.h" -#include -class CThread; - -class CXWindowsSecondaryScreen : public ISecondaryScreen { +class CXWindowsSecondaryScreen : public CXWindowsScreen, public ISecondaryScreen { public: CXWindowsSecondaryScreen(); virtual ~CXWindowsSecondaryScreen(); @@ -27,23 +24,20 @@ class CXWindowsSecondaryScreen : public ISecondaryScreen { virtual void getSize(SInt32* width, SInt32* height) const; virtual SInt32 getJumpZoneSize() const; + protected: + // CXWindowsScreen overrides + virtual void onOpenDisplay(); + virtual void onCloseDisplay(); + virtual void eventThread(void*); + private: - Cursor createBlankCursor(); - void eventThread(void*); + void leaveNoLock(Display*); KeyCode mapKey(KeyID, KeyModifierMask) const; unsigned int mapButton(ButtonID button) const; private: CClient* m_client; - CThread* m_eventThread; - Display* m_display; - int m_screen; - Window m_root; Window m_window; - SInt32 m_w, m_h; - - // X is not thread safe - CMutex m_mutex; }; #endif diff --git a/synergy/Makefile b/synergy/Makefile index 5305a5c5..689c29c0 100644 --- a/synergy/Makefile +++ b/synergy/Makefile @@ -20,6 +20,7 @@ CXXFILES = \ CServerProtocol1_0.cpp \ CScreenMap.cpp \ CServer.cpp \ + CXWindowsScreen.cpp \ CXWindowsPrimaryScreen.cpp \ CXWindowsSecondaryScreen.cpp \ XSynergy.cpp \