diff --git a/client/CClient.cpp b/client/CClient.cpp index 15514275..ab221a7e 100644 --- a/client/CClient.cpp +++ b/client/CClient.cpp @@ -4,7 +4,7 @@ #include "CInputPacketStream.h" #include "COutputPacketStream.h" #include "CProtocolUtil.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" #include "IServer.h" #include "ProtocolTypes.h" #include "XScreen.h" @@ -148,9 +148,7 @@ CClient::run() this, &CClient::runSession)); // handle events - log((CLOG_DEBUG "starting event handling")); m_screen->run(); - log((CLOG_DEBUG "stopped event handling")); // clean up deleteSession(thread); @@ -160,7 +158,6 @@ CClient::run() log((CLOG_ERR "client error: %s", e.what())); // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); CLock lock(&m_mutex); @@ -168,7 +165,6 @@ CClient::run() } catch (XThread&) { // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; @@ -177,7 +173,6 @@ CClient::run() log((CLOG_DEBUG "unknown client error")); // clean up - log((CLOG_DEBUG "stopped event handling")); deleteSession(thread); log((CLOG_NOTE "stopping client \"%s\"", m_name.c_str())); throw; @@ -188,6 +183,7 @@ void CClient::close() { closeSecondaryScreen(); + log((CLOG_INFO "closed screen")); } void @@ -291,9 +287,9 @@ CClient::mouseWheel(SInt32 delta) } void -CClient::screenSaver(bool activate) +CClient::screensaver(bool activate) { - m_screen->screenSaver(activate); + m_screen->screensaver(activate); } CString @@ -302,6 +298,12 @@ CClient::getName() const return m_name; } +SInt32 +CClient::getJumpZoneSize() const +{ + return m_screen->getJumpZoneSize(); +} + void CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -309,21 +311,15 @@ CClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClient::getCenter(SInt32&, SInt32&) const +CClient::getCursorPos(SInt32& x, SInt32& y) const { - assert(0 && "shouldn't be called"); + m_screen->getCursorPos(x, y); } void -CClient::getMousePos(SInt32& x, SInt32& y) const +CClient::getCursorCenter(SInt32&, SInt32&) const { - m_screen->getMousePos(x, y); -} - -SInt32 -CClient::getJumpZoneSize() const -{ - return m_screen->getJumpZoneSize(); + assert(0 && "shouldn't be called"); } // FIXME -- use factory to create screen diff --git a/client/CClient.h b/client/CClient.h index b36fa98c..c555d87c 100644 --- a/client/CClient.h +++ b/client/CClient.h @@ -7,10 +7,10 @@ #include "CNetworkAddress.h" #include "CMutex.h" +class CSecondaryScreen; class CServerProxy; class CThread; class IDataSocket; -class ISecondaryScreen; class IScreenReceiver; class CClient : public IScreenReceiver, public IClient { @@ -50,7 +50,7 @@ public: // FIXME -- can we avoid passing everything here? virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -62,13 +62,13 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: // open/close the secondary screen @@ -87,7 +87,7 @@ private: private: CMutex m_mutex; CString m_name; - ISecondaryScreen* m_screen; + CSecondaryScreen* m_screen; IScreenReceiver* m_server; CNetworkAddress m_serverAddress; bool m_camp; diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp new file mode 100644 index 00000000..12358b2a --- /dev/null +++ b/client/CSecondaryScreen.cpp @@ -0,0 +1,263 @@ +#include "CSecondaryScreen.h" +#include "IScreen.h" +#include "CLock.h" +#include "CThread.h" +#include "CLog.h" + +// +// CSecondaryScreen +// + +CSecondaryScreen::CSecondaryScreen() +{ + // do nothing +} + +CSecondaryScreen::~CSecondaryScreen() +{ + // do nothing +} + +bool +CSecondaryScreen::isActive() const +{ + CLock lock(&m_mutex); + return m_active; +} + +void +CSecondaryScreen::run() +{ + // change our priority + CThread::getCurrentThread().setPriority(-7); + + // run event loop + try { + log((CLOG_DEBUG "entering event loop")); + onPreRun(); + getScreen()->mainLoop(); + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CSecondaryScreen::stop() +{ + getScreen()->exitMainLoop(); +} + +void +CSecondaryScreen::open() +{ + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // assume primary has all clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + grabClipboard(id); + } + + // update keyboard state + updateKeys(); + + // disable the screen saver + getScreen()->openScreenSaver(false); + + // subclass hook + onPostOpen(); + } + catch (...) { + close(); + throw; + } + + // hide the cursor + m_active = true; + leave(); +} + +void +CSecondaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreenSaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) +{ + CLock lock(&m_mutex); + assert(m_active == false); + + log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); + + getScreen()->syncDesktop(); + + // now active + m_active = true; + + // subclass hook + onPreEnter(); + + // update our keyboard state to reflect the local state + updateKeys(); + + // toggle modifiers that don't match the desired state + setToggleState(mask); + + // warp to requested location + warpCursor(x, y); + + // show mouse + hideWindow(); + + // subclass hook + onPostEnter(); +} + +void +CSecondaryScreen::leave() +{ + log((CLOG_INFO "leaving screen")); + CLock lock(&m_mutex); + assert(m_active == true); + + getScreen()->syncDesktop(); + + // subclass hook + onPreLeave(); + + // hide mouse + showWindow(); + + // subclass hook + onPostLeave(); + + // not active anymore + m_active = false; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); +} + +void +CSecondaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CSecondaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +void +CSecondaryScreen::screensaver(bool activate) +{ + getScreen()->screensaver(activate); +} + +void +CSecondaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +SInt32 +CSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + +void +CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + getScreen()->syncDesktop(); + getScreen()->getShape(x, y, w, h); +} + +void +CSecondaryScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + getScreen()->syncDesktop(); + getScreen()->getCursorPos(x, y); +} + +void +CSecondaryScreen::onPreRun() +{ + // do nothing +} + +void +CSecondaryScreen::onPostRun() +{ + // do nothing +} + +void +CSecondaryScreen::onPreOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPostOpen() +{ + // do nothing +} + +void +CSecondaryScreen::onPreClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPostClose() +{ + // do nothing +} + +void +CSecondaryScreen::onPreEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPostEnter() +{ + // do nothing +} + +void +CSecondaryScreen::onPreLeave() +{ + // do nothing +} + +void +CSecondaryScreen::onPostLeave() +{ + // do nothing +} diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h new file mode 100644 index 00000000..736fc4d9 --- /dev/null +++ b/client/CSecondaryScreen.h @@ -0,0 +1,141 @@ +#ifndef CSECONDARYSCREEN_H +#define CSECONDARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" +#include "MouseTypes.h" +#include "CMutex.h" + +class IClipboard; +class IScreen; + +// platform independent base class for secondary screen implementations. +// each platform will derive a class from CSecondaryScreen to handle +// platform dependent operations. +class CSecondaryScreen { +public: + CSecondaryScreen(); + virtual ~CSecondaryScreen(); + + // manipulators + + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // run() may only be called between open() and close(). + void run(); + + // cause run() to return + void stop(); + + // initialize the screen, hide the cursor, and disable the screen + // saver. start reporting events to the IScreenReceiver (which is + // set through some other interface). + void open(); + + // close the screen. should restore the screen saver. it should + // also simulate key up events for any keys that have simulate key + // down events without a matching key up. without this the client + // will leave its keyboard in the wrong logical state. + void close(); + + // called when the user navigates to this secondary screen. warps + // the cursor to the given absoltue coordinates and unhide it. prepare to + // simulate input events. + void enter(SInt32 x, SInt32 y, KeyModifierMask mask); + + // called when the user navigates off the secondary screen. clean + // up input event simulation and hide the cursor. + void leave(); + + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + void setClipboard(ClipboardID, const IClipboard*); + + // synergy should own the clipboard + void grabClipboard(ClipboardID); + + // activate or deactivate the screen saver + void screensaver(bool activate); + + // keyboard input event synthesis + virtual void keyDown(KeyID, KeyModifierMask) = 0; + virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; + virtual void keyUp(KeyID, KeyModifierMask) = 0; + + // mouse input event synthesis + virtual void mouseDown(ButtonID) = 0; + virtual void mouseUp(ButtonID) = 0; + virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; + virtual void mouseWheel(SInt32 delta) = 0; + + // accessors + + // returns true iff the screen is active (i.e. the user has entered + // the screen) + bool isActive() const; + + // return the contents of the given clipboard + void getClipboard(ClipboardID, IClipboard*) const; + + // returns the size of the zone on the edges of the screen that + // causes the cursor to jump to another screen. default returns 0. + virtual SInt32 getJumpZoneSize() const; + + // get the shape (position of upper-left corner and size) of the + // screen + virtual void getShape(SInt32& x, SInt32& y, + SInt32& width, SInt32& height) const; + + // get the position of the mouse on the screen + virtual void getCursorPos(SInt32& x, SInt32& y) const; + + // get the platform dependent screen object + virtual IScreen* getScreen() const = 0; + +protected: + // template method hooks. these are called on entry/exit to the + // named method. override to do platform specific operations. + // defaults do nothing. + virtual void onPreRun(); + virtual void onPostRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(); + + // create/destroy the window. this window is generally used to + // receive events and hide the cursor. + virtual void createWindow() = 0; + virtual void destroyWindow() = 0; + + // called when the user navigates off the secondary screen. hide + // the cursor. + virtual void showWindow() = 0; + + // called when the user navigates to this secondary screen. show + // the cursor and prepare to synthesize input events. + virtual void hideWindow() = 0; + + // warp the cursor to the given absolute coordinates + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + // check the current keyboard state. normally a screen will save + // the keyboard state in this method and use this shadow state + // when synthesizing events. + virtual void updateKeys() = 0; + + // toggle modifiers that don't match the given state + virtual void setToggleState(KeyModifierMask) = 0; + +private: + CMutex m_mutex; + + // m_active is true if this screen has been entered + bool m_active; +}; + +#endif diff --git a/client/CServerProxy.cpp b/client/CServerProxy.cpp index 3b8216a9..2aa59a4a 100644 --- a/client/CServerProxy.cpp +++ b/client/CServerProxy.cpp @@ -131,7 +131,7 @@ CServerProxy::run() } else if (memcmp(code, kMsgCScreenSaver, 4) == 0) { - screenSaver(); + screensaver(); } else if (memcmp(code, kMsgQInfo, 4) == 0) { @@ -484,7 +484,7 @@ CServerProxy::mouseWheel() } void -CServerProxy::screenSaver() +CServerProxy::screensaver() { // parse SInt8 on; @@ -492,7 +492,7 @@ CServerProxy::screenSaver() log((CLOG_DEBUG1 "recv screen saver on=%d", on)); // forward - getClient()->screenSaver(on != 0); + getClient()->screensaver(on != 0); } void @@ -501,7 +501,7 @@ CServerProxy::queryInfo() // get current info CClientInfo info; getClient()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); - getClient()->getMousePos(info.m_mx, info.m_my); + getClient()->getCursorPos(info.m_mx, info.m_my); info.m_zoneSize = getClient()->getJumpZoneSize(); // send it diff --git a/client/CServerProxy.h b/client/CServerProxy.h index 198b3d9b..0370a646 100644 --- a/client/CServerProxy.h +++ b/client/CServerProxy.h @@ -56,7 +56,7 @@ private: void mouseUp(); void mouseMove(); void mouseWheel(); - void screenSaver(); + void screensaver(); void queryInfo(); void infoAcknowledgment(); diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index aea09204..1207cd8f 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -1,8 +1,9 @@ #include "CXWindowsSecondaryScreen.h" -#include "IScreenReceiver.h" #include "CXWindowsClipboard.h" +#include "CXWindowsScreen.h" #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" +#include "IScreenReceiver.h" #include "XScreen.h" #include "CThread.h" #include "CLog.h" @@ -26,153 +27,16 @@ // CXWindowsSecondaryScreen::CXWindowsSecondaryScreen(IScreenReceiver* receiver) : - m_receiver(receiver), - m_window(None), - m_active(false) + CSecondaryScreen(), + m_window(None) { - assert(m_receiver != NULL); + m_screen = new CXWindowsScreen(receiver, this); } CXWindowsSecondaryScreen::~CXWindowsSecondaryScreen() { assert(m_window == None); -} - -void -CXWindowsSecondaryScreen::run() -{ - assert(m_window != None); - - // change our priority - CThread::getCurrentThread().setPriority(-7); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CXWindowsSecondaryScreen::stop() -{ - exitMainLoop(); -} - -void -CXWindowsSecondaryScreen::open() -{ - assert(m_window == None); - - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // initialize the clipboards; assume primary has all clipboards - initClipboards(m_window); - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // check for peculiarities - // FIXME -- may have to get these from some database - m_numLockHalfDuplex = false; - m_capsLockHalfDuplex = false; -// m_numLockHalfDuplex = true; -// m_capsLockHalfDuplex = true; - - // get the display - CDisplayLock display(this); - - // update key state - updateKeys(display); - updateKeycodeMap(display); - updateModifierMap(display); - updateModifiers(display); - - // disable the screen saver - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // hide the cursor - m_active = true; - leave(); -} - -void -CXWindowsSecondaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CXWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) -{ - assert(m_window != None); - assert(m_active == false); - - log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - - CDisplayLock display(this); - - // now active - m_active = true; - - // update our keyboard state to reflect the local state - updateKeys(display); - updateModifiers(display); - - // toggle modifiers that don't match the desired state - unsigned int xMask = maskToX(mask); - if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { - toggleKey(display, XK_Caps_Lock, m_capsLockMask); - } - if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { - toggleKey(display, XK_Num_Lock, m_numLockMask); - } - if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { - toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); - } - - // warp to requested location - warpCursor(x, y); - - // show mouse - hideWindow(); -} - -void -CXWindowsSecondaryScreen::leave() -{ - assert(m_window != None); - assert(m_active == true); - - log((CLOG_INFO "leaving screen")); - - CDisplayLock display(this); - - // hide mouse - showWindow(); - - // not active anymore - m_active = false; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); + delete m_screen; } void @@ -236,7 +100,7 @@ CXWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) void CXWindowsSecondaryScreen::mouseDown(ButtonID button) { - CDisplayLock display(this); + CDisplayLock display(m_screen); XTestFakeButtonEvent(display, mapButton(button), True, CurrentTime); XSync(display, False); } @@ -244,7 +108,7 @@ CXWindowsSecondaryScreen::mouseDown(ButtonID button) void CXWindowsSecondaryScreen::mouseUp(ButtonID button) { - CDisplayLock display(this); + CDisplayLock display(m_screen); XTestFakeButtonEvent(display, mapButton(button), False, CurrentTime); XSync(display, False); } @@ -252,7 +116,6 @@ CXWindowsSecondaryScreen::mouseUp(ButtonID button) void CXWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { - CDisplayLock display(this); warpCursor(x, y); } @@ -268,7 +131,7 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) } // send as many clicks as necessary - CDisplayLock display(this); + CDisplayLock display(m_screen); for (; delta >= 120; delta -= 120) { XTestFakeButtonEvent(display, button, True, CurrentTime); XTestFakeButtonEvent(display, button, False, CurrentTime); @@ -276,63 +139,29 @@ CXWindowsSecondaryScreen::mouseWheel(SInt32 delta) XSync(display, False); } -void -CXWindowsSecondaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) +IScreen* +CXWindowsSecondaryScreen::getScreen() const { - setDisplayClipboard(id, clipboard); + return m_screen; } void -CXWindowsSecondaryScreen::grabClipboard(ClipboardID id) +CXWindowsSecondaryScreen::onScreensaver(bool) { - setDisplayClipboard(id, NULL); + // ignore } void -CXWindowsSecondaryScreen::screenSaver(bool activate) +CXWindowsSecondaryScreen::onError() { - CDisplayLock display(this); - if (activate) { - getScreenSaver()->activate(); - } - else { - getScreenSaver()->deactivate(); - } -} - -void -CXWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const -{ - CDisplayLock display(this); - getCursorPos(x, y); -} - -void -CXWindowsSecondaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); -} - -SInt32 -CXWindowsSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - -void -CXWindowsSecondaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getDisplayClipboard(id, clipboard); + // ignore + // FIXME -- forward this? to whom? } bool -CXWindowsSecondaryScreen::onPreDispatch(const CEvent* event) +CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) { - // forward to superclass - return CXWindowsScreen::onPreDispatch(event); + return false; } bool @@ -344,33 +173,117 @@ CXWindowsSecondaryScreen::onEvent(CEvent* 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); - } + // keyboard mapping changed + updateKeys(); return true; case LeaveNotify: - { - // mouse moved out of hider window somehow. hide the window. - assert(m_window != None); - CDisplayLock display(this); - hideWindow(); - } + // mouse moved out of hider window somehow. hide the window. + hideWindow(); return true; } } void -CXWindowsSecondaryScreen::onLostClipboard(ClipboardID id) +CXWindowsSecondaryScreen::onPreRun() { - // tell client that the clipboard was grabbed locally - m_receiver->onGrabClipboard(id); + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreOpen() +{ + assert(m_window == None); +} + +void +CXWindowsSecondaryScreen::onPostOpen() +{ + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; + +} + +void +CXWindowsSecondaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::onPreLeave() +{ + assert(m_window != None); +} + +void +CXWindowsSecondaryScreen::createWindow() +{ + { + CDisplayLock display(m_screen); + + // 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 = m_screen->getBlankCursor(); + + // create the cursor hider window + m_window = XCreateWindow(display, m_screen->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); + } + + // tell our superclass about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsSecondaryScreen::destroyWindow() +{ + CDisplayLock display(m_screen); + if (display != NULL) { + // release keys that are still pressed + releaseKeys(display); + + // no longer impervious to server grabs + XTestGrabControl(display, False); + + // destroy window + if (m_window != None) { + XDestroyWindow(display, m_window); + m_window = None; + } + + // update + XSync(display, False); + } } void @@ -380,106 +293,55 @@ CXWindowsSecondaryScreen::showWindow() // somewhere else on the screen) SInt32 x, y; getCursorPos(x, y); - XMoveWindow(getDisplay(), m_window, x, y); + CDisplayLock display(m_screen); + XMoveWindow(display, m_window, x, y); // raise and show the hider window. take activation. // FIXME -- take focus? - XMapRaised(getDisplay(), m_window); + XMapRaised(display, m_window); /* XXX -- this should have no effect // hide cursor by moving it into the hider window - XWarpPointer(getDisplay(), None, m_window, 0, 0, 0, 0, 0, 0); + XWarpPointer(display, None, m_window, 0, 0, 0, 0, 0, 0); */ } void CXWindowsSecondaryScreen::hideWindow() { - XUnmapWindow(getDisplay(), m_window); + assert(m_window != None); + + CDisplayLock display(m_screen); + XUnmapWindow(display, m_window); } void CXWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { - XTestFakeMotionEvent(getDisplay(), getScreen(), x, y, CurrentTime); - XSync(getDisplay(), False); + CDisplayLock display(m_screen); + Display* pDisplay = display; + XTestFakeMotionEvent(display, DefaultScreen(pDisplay), x, y, CurrentTime); + XSync(display, False); } void -CXWindowsSecondaryScreen::checkClipboard() +CXWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) { - // do nothing, we're always up to date -} + CDisplayLock display(m_screen); -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(); + // toggle modifiers that don't match the desired state + unsigned int xMask = maskToX(mask); + if ((xMask & m_capsLockMask) != (m_mask & m_capsLockMask)) { + toggleKey(display, XK_Caps_Lock, m_capsLockMask); } - - // 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(); + if ((xMask & m_numLockMask) != (m_mask & m_numLockMask)) { + toggleKey(display, XK_Num_Lock, m_numLockMask); } - 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; - } + if ((xMask & m_scrollLockMask) != (m_mask & m_scrollLockMask)) { + toggleKey(display, XK_Scroll_Lock, m_scrollLockMask); } } -void -CXWindowsSecondaryScreen::installScreenSaver() -{ - getScreenSaver()->disable(); -} - -void -CXWindowsSecondaryScreen::uninstallScreenSaver() -{ - getScreenSaver()->enable(); -} - unsigned int CXWindowsSecondaryScreen::mapButton(ButtonID id) const { @@ -848,7 +710,7 @@ CXWindowsSecondaryScreen::doKeystrokes(const Keystrokes& keys, SInt32 count) } // lock display - CDisplayLock display(this); + CDisplayLock display(m_screen); // generate key events for (Keystrokes::const_iterator k = keys.begin(); k != keys.end(); ) { @@ -910,27 +772,24 @@ CXWindowsSecondaryScreen::maskToX(KeyModifierMask inMask) const } void -CXWindowsSecondaryScreen::releaseKeys() +CXWindowsSecondaryScreen::releaseKeys(Display* display) { - CDisplayLock display(this); + assert(display != NULL); - if (display != NULL) { - // key up for each key that's down - for (UInt32 i = 0; i < 256; ++i) { - if (m_keys[i]) { - XTestFakeKeyEvent(display, i, False, CurrentTime); - m_keys[i] = false; - } + // key up for each key that's down + for (UInt32 i = 0; i < 256; ++i) { + if (m_keys[i]) { + XTestFakeKeyEvent(display, i, False, CurrentTime); + m_keys[i] = false; } - - // update - XSync(display, False); } } void -CXWindowsSecondaryScreen::updateKeys(Display* display) +CXWindowsSecondaryScreen::updateKeys() { + CDisplayLock display(m_screen); + // ask server which keys are pressed char keys[32]; XQueryKeymap(display, keys); @@ -946,6 +805,11 @@ CXWindowsSecondaryScreen::updateKeys(Display* display) m_keys[j + 6] = ((keys[i] & 0x40) != 0); m_keys[j + 7] = ((keys[i] & 0x80) != 0); } + + // update mappings and current modifiers + updateKeycodeMap(display); + updateModifierMap(display); + updateModifiers(display); } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index 558b7526..d2797d65 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -1,47 +1,55 @@ #ifndef CXWINDOWSSECONDARYSCREEN_H #define CXWINDOWSSECONDARYSCREEN_H -#include "CXWindowsScreen.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" +#include "IScreenEventHandler.h" #include "stdmap.h" #include "stdvector.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +class CXWindowsScreen; class IScreenReceiver; -class CXWindowsSecondaryScreen : public CXWindowsScreen, - public ISecondaryScreen { +class CXWindowsSecondaryScreen : + public CSecondaryScreen, public IScreenEventHandler { public: CXWindowsSecondaryScreen(IScreenReceiver*); virtual ~CXWindowsSecondaryScreen(); - // ISecondaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask); - virtual void leave(); + // CSecondaryScreen overrides virtual void keyDown(KeyID, KeyModifierMask); virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count); virtual void keyUp(KeyID, KeyModifierMask); virtual void mouseDown(ButtonID); virtual void mouseUp(ButtonID); - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); + virtual void mouseMove(SInt32 x, SInt32 y); virtual void mouseWheel(SInt32 delta); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void screenSaver(bool activate); - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; - virtual SInt32 getJumpZoneSize() const; - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual IScreen* getScreen() const; -protected: - // CXWindowsScreen overrides + // IScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual void onLostClipboard(ClipboardID); + +protected: + // CSecondaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void createWindow(); + virtual void destroyWindow(); + virtual void showWindow(); + virtual void hideWindow(); + virtual void warpCursor(SInt32 x, SInt32 y); + virtual void updateKeys(); + virtual void setToggleState(KeyModifierMask); private: enum EKeyAction { kPress, kRelease, kRepeat }; @@ -62,26 +70,6 @@ private: typedef std::map KeyCodeMap; typedef std::map ModifierMap; - void showWindow(); - void hideWindow(); - - // warp the mouse to the specified position - void warpCursor(SInt32 x, SInt32 y); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - unsigned int mapButton(ButtonID button) const; unsigned int mapKey(Keystrokes&, KeyCode&, KeyID, @@ -91,8 +79,7 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); unsigned int maskToX(KeyModifierMask) const; - void releaseKeys(); - void updateKeys(Display* display); + void releaseKeys(Display*); void updateKeycodeMap(Display* display); void updateModifiers(Display* display); void updateModifierMap(Display* display); @@ -100,12 +87,9 @@ private: static bool isToggleKeysym(KeySym); private: - IScreenReceiver* m_receiver; + CXWindowsScreen* m_screen; Window m_window; - // m_active is true if this screen has been entered - bool m_active; - // note toggle keys that toggles on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/client/ISecondaryScreen.h b/client/ISecondaryScreen.h deleted file mode 100644 index fe0264f7..00000000 --- a/client/ISecondaryScreen.h +++ /dev/null @@ -1,82 +0,0 @@ -#ifndef ISECONDARYSCREEN_H -#define ISECONDARYSCREEN_H - -#include "IInterface.h" -#include "ClipboardTypes.h" -#include "KeyTypes.h" -#include "MouseTypes.h" - -class IClipboard; - -class ISecondaryScreen : public IInterface { -public: - // manipulators - - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // the screen must be open()'d before run() and must not be - // close()'d until run() returns. - virtual void run() = 0; - - // cause run() to return - virtual void stop() = 0; - - // initialize the screen, hide the cursor, and disable the screen - // saver. start reporting events to the IScreenReceiver (which is - // set through some other interface). - virtual void open() = 0; - - // close the screen. should restore the screen saver. it should - // also simulate key up events for any keys that have simulate key - // down events without a matching key up. without this the client - // will leave its keyboard in the wrong logical state. - virtual void close() = 0; - - // called when the user navigates to the secondary screen. warp - // the cursor to the given coordinates and unhide it. prepare to - // simulate input events. - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - KeyModifierMask mask) = 0; - - // called when the user navigates off the secondary screen. clean - // up input event simulation and hide the cursor. - virtual void leave() = 0; - - // keyboard input simulation - virtual void keyDown(KeyID, KeyModifierMask) = 0; - virtual void keyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void keyUp(KeyID, KeyModifierMask) = 0; - - // mouse input simulation - virtual void mouseDown(ButtonID) = 0; - virtual void mouseUp(ButtonID) = 0; - virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - virtual void mouseWheel(SInt32 delta) = 0; - - // set the screen's clipboard contents. this is usually called - // soon after an enter(). - virtual void setClipboard(ClipboardID, const IClipboard*) = 0; - - // take ownership of clipboard - virtual void grabClipboard(ClipboardID) = 0; - - // activate or deactivate the screen saver - virtual void screenSaver(bool activate) = 0; - - // accessors - - // get the position of the mouse on the screen - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; - - // get the size of the screen - virtual void getShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const = 0; - - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; - - // get the screen's clipboard contents - virtual void getClipboard(ClipboardID, IClipboard*) const = 0; -}; - -#endif diff --git a/client/Makefile.am b/client/Makefile.am index 27d35cc9..5c06a480 100644 --- a/client/Makefile.am +++ b/client/Makefile.am @@ -5,13 +5,14 @@ DEPTH = .. bin_PROGRAMS = synergy synergy_SOURCES = \ CClient.cpp \ + CSecondaryScreen.cpp \ CServerProxy.cpp \ CXWindowsSecondaryScreen.cpp \ client.cpp \ CClient.h \ + CSecondaryScreen.h \ CServerProxy.h \ CXWindowsSecondaryScreen.h \ - ISecondaryScreen.h \ $(NULL) synergy_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/client/client.cpp b/client/client.cpp index 620e7846..cda8e0a9 100644 --- a/client/client.cpp +++ b/client/client.cpp @@ -116,9 +116,11 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } - s_client->close(); - delete s_client; - s_client = NULL; + if (s_client != NULL) { + s_client->close(); + delete s_client; + s_client = NULL; + } CLog::setLock(NULL); s_logMutex = NULL; throw; diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index 3176ce69..e9b46721 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -3,14 +3,22 @@ #include "CXWindowsScreenSaver.h" #include "CXWindowsUtil.h" #include "CClipboard.h" +#include "IScreenEventHandler.h" +#include "IScreenReceiver.h" #include "XScreen.h" #include "CLock.h" #include "CThread.h" #include "CLog.h" #include "IJob.h" -#include "CString.h" -#include +//#include "CString.h" +//#include #include +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +# include +#endif // // CXWindowsScreen::CTimer @@ -68,15 +76,24 @@ CXWindowsScreen::CTimer::operator<(const CTimer& t) const CXWindowsScreen* CXWindowsScreen::s_screen = NULL; -CXWindowsScreen::CXWindowsScreen() : +CXWindowsScreen::CXWindowsScreen(IScreenReceiver* receiver, + IScreenEventHandler* eventHandler) : m_display(NULL), m_root(None), + m_stop(false), + m_receiver(receiver), + m_eventHandler(eventHandler), + m_window(None), m_x(0), m_y(0), m_w(0), m_h(0), - m_stop(false), - m_screenSaver(NULL) + m_screensaver(NULL), + m_screensaverNotify(false), + m_atomScreensaver(None) { - assert(s_screen == NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + s_screen = this; } @@ -120,6 +137,77 @@ CXWindowsScreen::removeTimerNoLock(IJob* job) m_timers.swap(tmp); } +void +CXWindowsScreen::setWindow(Window window) +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + // destroy the clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // save the new window + m_window = window; + + // initialize the clipboards + if (m_window != None) { + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + m_clipboard[id] = new CXWindowsClipboard(m_display, m_window, id); + } + } +} + +Window +CXWindowsScreen::getRoot() const +{ + assert(m_display != NULL); + return m_root; +} + +Cursor +CXWindowsScreen::getBlankCursor() const +{ + return m_cursor; +} + +void +CXWindowsScreen::open() +{ + assert(m_display == NULL); + + // set the X I/O error handler so we catch the display disconnecting + XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); + + // get the DISPLAY + const char* display = getenv("DISPLAY"); + if (display == NULL) { + display = ":0.0"; + } + + // open the display + log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); + m_display = XOpenDisplay(display); + if (m_display == NULL) { + throw XScreenOpenFailure(); + } + + // get root window + m_root = DefaultRootWindow(m_display); + + // create the transparent cursor + createBlankCursor(); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_atomScreensaver = XInternAtom(m_display, "SCREENSAVER", False); + m_screensaver = new CXWindowsScreenSaver(this, m_display); +} + void CXWindowsScreen::mainLoop() { @@ -147,7 +235,7 @@ CXWindowsScreen::mainLoop() // have a go at it. m_mutex.unlock(); if (!onPreDispatch(&event)) { - onEvent(&event); + m_eventHandler->onEvent(&event); } m_mutex.lock(); } @@ -162,23 +250,210 @@ CXWindowsScreen::exitMainLoop() m_stop = true; } +void +CXWindowsScreen::close() +{ + CLock lock(&m_mutex); + + // done with screen saver + delete m_screensaver; + m_screensaver = NULL; + + // destroy clipboards + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + delete m_clipboard[id]; + m_clipboard[id] = NULL; + } + + // close the display + if (m_display != NULL) { + XCloseDisplay(m_display); + m_display = NULL; + log((CLOG_DEBUG "closed display")); + } + XSetIOErrorHandler(NULL); +} + bool -CXWindowsScreen::onPreDispatch(const CEvent* event) +CXWindowsScreen::setClipboard(ClipboardID id, const IClipboard* clipboard) +{ + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + if (clipboard != NULL) { + // save clipboard data + return CClipboard::copy(m_clipboard[id], clipboard, timestamp); + } + else { + // assert clipboard ownership + if (!m_clipboard[id]->open(timestamp)) { + return false; + } + m_clipboard[id]->empty(); + m_clipboard[id]->close(); + return true; + } +} + +void +CXWindowsScreen::checkClipboards() +{ + // do nothing, we're always up to date +} + +void +CXWindowsScreen::openScreenSaver(bool notify) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + m_screensaver->setNotify(m_window); + } + else { + m_screensaver->disable(); + } +} + +void +CXWindowsScreen::closeScreenSaver() +{ + CLock lock(&m_mutex); + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + m_screensaver->setNotify(None); + } + else { + m_screensaver->enable(); + } + } +} + +void +CXWindowsScreen::screensaver(bool activate) +{ + CLock lock(&m_mutex); + assert(m_screensaver != NULL); + + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CXWindowsScreen::syncDesktop() +{ + // do nothing; X doesn't suffer from this bogosity +} + +bool +CXWindowsScreen::getClipboard(ClipboardID id, IClipboard* clipboard) const +{ + assert(clipboard != NULL); + + // block others from using the display while we get the clipboard + CLock lock(&m_mutex); + + // fail if we don't have the requested clipboard + if (m_clipboard[id] == NULL) { + return false; + } + + // get the actual time. ICCCM does not allow CurrentTime. + Time timestamp = CXWindowsUtil::getCurrentTime( + m_display, m_clipboard[id]->getWindow()); + + // copy the clipboard + return CClipboard::copy(clipboard, m_clipboard[id], timestamp); +} + +void +CXWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x; + y = m_y; + w = m_w; + h = m_h; +} + +void +CXWindowsScreen::getCursorPos(SInt32& x, SInt32& y) const +{ + CLock lock(&m_mutex); + 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 +{ + CLock lock(&m_mutex); + assert(m_display != NULL); + + x = m_x + (m_w >> 1); + y = m_y + (m_h >> 1); +} + +void +CXWindowsScreen::updateScreenShape() +{ + m_x = 0; + m_y = 0; + m_w = WidthOfScreen(DefaultScreenOfDisplay(m_display)); + m_h = HeightOfScreen(DefaultScreenOfDisplay(m_display)); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); +} + +bool +CXWindowsScreen::onPreDispatch(CEvent* event) { assert(event != NULL); - const XEvent* xevent = &event->m_event; + XEvent* xevent = &event->m_event; switch (xevent->type) { + case MappingNotify: + // keyboard mapping changed + XRefreshKeyboardMapping(&xevent->xmapping); + + // pass event on + break; + 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. + // selection owner. report that to the receiver. 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); + m_receiver->onGrabClipboard(id); return true; } } @@ -225,6 +500,15 @@ CXWindowsScreen::onPreDispatch(const CEvent* event) } break; + case ClientMessage: + if (xevent->xclient.message_type == m_atomScreensaver || + xevent->xclient.format == 32) { + // screen saver activation/deactivation event + m_eventHandler->onScreensaver(xevent->xclient.data.l[0] != 0); + return true; + } + break; + case DestroyNotify: // looks like one of the windows that requested a clipboard // transfer has gone bye-bye. @@ -237,150 +521,10 @@ CXWindowsScreen::onPreDispatch(const CEvent* event) // let screen saver have a go { CLock lock(&m_mutex); - m_screenSaver->onPreDispatch(xevent); + m_screensaver->onPreDispatch(xevent); } - return false; -} - -void -CXWindowsScreen::openDisplay() -{ - assert(m_display == NULL); - - // set the X I/O error handler so we catch the display disconnecting - XSetIOErrorHandler(&CXWindowsScreen::ioErrorHandler); - - // get the DISPLAY - const char* display = getenv("DISPLAY"); - if (display == NULL) { - display = ":0.0"; - } - - // open the display - log((CLOG_DEBUG "XOpenDisplay(\"%s\")", display)); - m_display = XOpenDisplay(display); - if (m_display == NULL) { - throw XScreenOpenFailure(); - } - - // 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 - updateScreenShape(); - - // initialize the screen saver - m_screenSaver = new CXWindowsScreenSaver(this, m_display); -} - -void -CXWindowsScreen::closeDisplay() -{ - CLock lock(&m_mutex); - - // done with screen saver - delete m_screenSaver; - m_screenSaver = NULL; - - // destroy clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - delete m_clipboard[id]; - m_clipboard[id] = NULL; - } - - // close the display - if (m_display != NULL) { - XCloseDisplay(m_display); - m_display = NULL; - log((CLOG_DEBUG "closed display")); - } - XSetIOErrorHandler(NULL); -} - -Display* -CXWindowsScreen::getDisplay() const -{ - return m_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::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); - - x = m_x; - y = m_y; - w = m_w; - h = m_h; -} - -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); + return m_eventHandler->onPreDispatch(event); } void @@ -417,30 +561,6 @@ CXWindowsScreen::createBlankCursor() XFreePixmap(m_display, bitmap); } -Cursor -CXWindowsScreen::getBlankCursor() const -{ - return m_cursor; -} - -ClipboardID -CXWindowsScreen::getClipboardID(Atom selection) const -{ - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - if (m_clipboard[id] != NULL && - m_clipboard[id]->getSelection() == selection) { - return id; - } - } - return kClipboardEnd; -} - -void -CXWindowsScreen::onUnexpectedClose() -{ - // do nothing -} - bool CXWindowsScreen::processTimers() { @@ -486,62 +606,16 @@ CXWindowsScreen::processTimers() } } -CXWindowsScreenSaver* -CXWindowsScreen::getScreenSaver() const +ClipboardID +CXWindowsScreen::getClipboardID(Atom selection) const { - return m_screenSaver; -} - -bool -CXWindowsScreen::setDisplayClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - CLock lock(&m_mutex); - - // fail if we don't have the requested clipboard - if (m_clipboard[id] == NULL) { - return false; - } - - // get the actual time. ICCCM does not allow CurrentTime. - Time timestamp = CXWindowsUtil::getCurrentTime( - m_display, m_clipboard[id]->getWindow()); - - if (clipboard != NULL) { - // save clipboard data - return CClipboard::copy(m_clipboard[id], clipboard, timestamp); - } - else { - // assert clipboard ownership - if (!m_clipboard[id]->open(timestamp)) { - return false; + for (ClipboardID id = 0; id < kClipboardEnd; ++id) { + if (m_clipboard[id] != NULL && + m_clipboard[id]->getSelection() == selection) { + return id; } - m_clipboard[id]->empty(); - m_clipboard[id]->close(); - return true; } -} - -bool -CXWindowsScreen::getDisplayClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - assert(clipboard != NULL); - - // block others from using the display while we get the clipboard - CLock lock(&m_mutex); - - // fail if we don't have the requested clipboard - if (m_clipboard[id] == NULL) { - return false; - } - - // get the actual time. ICCCM does not allow CurrentTime. - Time timestamp = CXWindowsUtil::getCurrentTime( - m_display, m_clipboard[id]->getWindow()); - - // copy the clipboard - return CClipboard::copy(clipboard, m_clipboard[id], timestamp); + return kClipboardEnd; } void @@ -584,17 +658,17 @@ CXWindowsScreen::ioErrorHandler(Display*) // so we set it to NULL), and exit. log((CLOG_WARN "X display has unexpectedly disconnected")); s_screen->m_display = NULL; - s_screen->onUnexpectedClose(); + s_screen->m_eventHandler->onError(); log((CLOG_CRIT "quiting due to X display disconnection")); exit(17); } // -// CXWindowsScreen::CDisplayLock +// CDisplayLock // -CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : +CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex(&screen->m_mutex), m_display(screen->m_display) { @@ -603,12 +677,12 @@ CXWindowsScreen::CDisplayLock::CDisplayLock(const CXWindowsScreen* screen) : m_mutex->lock(); } -CXWindowsScreen::CDisplayLock::~CDisplayLock() +CDisplayLock::~CDisplayLock() { m_mutex->unlock(); } -CXWindowsScreen::CDisplayLock::operator Display*() const +CDisplayLock::operator Display*() const { return m_display; } diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index 9900b7ec..e164c583 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -1,6 +1,7 @@ #ifndef CXWINDOWSSCREEN_H #define CXWINDOWSSCREEN_H +#include "IScreen.h" #include "ClipboardTypes.h" #include "CMutex.h" #include "CStopwatch.h" @@ -13,9 +14,9 @@ #include #include -class IClipboard; class IJob; -class IScreenSaver; +class IScreenEventHandler; +class IScreenReceiver; class CXWindowsClipboard; class CXWindowsScreenSaver; @@ -25,9 +26,9 @@ public: SInt32 m_result; }; -class CXWindowsScreen { +class CXWindowsScreen : public IScreen { public: - CXWindowsScreen(); + CXWindowsScreen(IScreenReceiver*, IScreenEventHandler*); virtual ~CXWindowsScreen(); // manipulators @@ -39,94 +40,41 @@ public: void addTimer(IJob*, double timeout); void removeTimer(IJob*); -protected: - class CDisplayLock { - public: - CDisplayLock(const CXWindowsScreen*); - ~CDisplayLock(); + // set the window (created by the subclass). this performs some + // initialization and saves the window in case it's needed later. + void setWindow(Window); - operator Display*() const; + // accessors - private: - const CMutex* m_mutex; - Display* m_display; - }; - friend class CDisplayLock; - - // runs an event loop and returns when exitMainLoop() is called - void mainLoop(); - - // force mainLoop() to return - void exitMainLoop(); - - // open the X display. calls onOpenDisplay() after opening the display, - // getting the screen, its size, and root window. then it starts the - // event thread. - 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 Display*. only use this when you know the display is - // locked but don't have the CDisplayLock available. - Display* getDisplay() const; - - // get the opened screen and its root window. to get the display - // create a CDisplayLock object passing this. while the object - // exists no other threads may access the display. do not save - // the Display* beyond the lifetime of the CDisplayLock. - int getScreen() const; + // get the root window of the screen Window getRoot() 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; - // set the contents of the clipboard (i.e. primary selection) - bool setDisplayClipboard(ClipboardID, - const IClipboard* clipboard); - - // copy the clipboard contents to clipboard - bool getDisplayClipboard(ClipboardID, - IClipboard* clipboard) const; - - // get the screen saver object - CXWindowsScreenSaver* - getScreenSaver() const; - - // called for each event before event translation and dispatch. return - // true to skip translation and dispatch. subclasses should call the - // superclass's version first and return true if it returns true. - virtual bool onPreDispatch(const CEvent* event) = 0; - - // called by mainLoop(). iff the event was handled return true and - // store the result, if any, in m_result, which defaults to zero. - virtual bool onEvent(CEvent* event) = 0; - - // called if the display is unexpectedly closing. default does nothing. - virtual void onUnexpectedClose(); - - // called when a clipboard is lost - virtual void onLostClipboard(ClipboardID) = 0; + // IScreen overrides + void open(); + void mainLoop(); + void exitMainLoop(); + void close(); + bool setClipboard(ClipboardID, const IClipboard*); + void checkClipboards(); + void openScreenSaver(bool notify); + void closeScreenSaver(); + void screensaver(bool activate); + void syncDesktop(); + bool getClipboard(ClipboardID, IClipboard*) const; + void getShape(SInt32&, SInt32&, SInt32&, SInt32&) const; + void getCursorPos(SInt32&, SInt32&) const; + void getCursorCenter(SInt32&, SInt32&) const; private: + // update screen size cache + void updateScreenShape(); + + // process events before dispatching to receiver + bool onPreDispatch(CEvent* event); + // create the transparent cursor void createBlankCursor(); @@ -255,14 +203,23 @@ private: }; private: + friend class CDisplayLock; + typedef CPriorityQueue CTimerPriorityQueue; + // X is not thread safe + CMutex m_mutex; + Display* m_display; - int m_screen; Window m_root; + bool m_stop; + + IScreenReceiver* m_receiver; + IScreenEventHandler* m_eventHandler; + Window m_window; + SInt32 m_x, m_y; SInt32 m_w, m_h; - bool m_stop; // clipboards CXWindowsClipboard* m_clipboard[kClipboardEnd]; @@ -270,20 +227,31 @@ private: // the transparent cursor Cursor m_cursor; - // screen saver - CXWindowsScreenSaver* m_screenSaver; + // screen saver stuff + CXWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + Atom m_atomScreensaver; // timers, the stopwatch used to time, and a mutex for the timers CTimerPriorityQueue m_timers; CStopwatch m_time; CMutex m_timersMutex; - // X is not thread safe - CMutex m_mutex; - // pointer to (singleton) screen. this is only needed by // ioErrorHandler(). static CXWindowsScreen* s_screen; }; +class CDisplayLock { +public: + CDisplayLock(const CXWindowsScreen*); + ~CDisplayLock(); + + operator Display*() const; + +private: + const CMutex* m_mutex; + Display* m_display; +}; + #endif diff --git a/server/CClientProxy.h b/server/CClientProxy.h index ef5d158f..8eb07a93 100644 --- a/server/CClientProxy.h +++ b/server/CClientProxy.h @@ -32,7 +32,7 @@ public: virtual void close() = 0; virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver) = 0; + bool forScreensaver) = 0; virtual bool leave() = 0; virtual void setClipboard(ClipboardID, const CString&) = 0; virtual void grabClipboard(ClipboardID) = 0; @@ -44,13 +44,13 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; - virtual void screenSaver(bool activate) = 0; + virtual void screensaver(bool activate) = 0; virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const = 0; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - virtual void getCenter(SInt32& x, SInt32& y) const = 0; - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; - virtual SInt32 getJumpZoneSize() const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; private: IServer* m_server; diff --git a/server/CClientProxy1_0.cpp b/server/CClientProxy1_0.cpp index 6a214d0d..4a969b93 100644 --- a/server/CClientProxy1_0.cpp +++ b/server/CClientProxy1_0.cpp @@ -236,12 +236,19 @@ CClientProxy1_0::mouseWheel(SInt32 delta) } void -CClientProxy1_0::screenSaver(bool on) +CClientProxy1_0::screensaver(bool on) { log((CLOG_DEBUG1 "send screen saver to \"%s\" on=%d", getName().c_str(), on ? 1 : 0)); CProtocolUtil::writef(getOutputStream(), kMsgCScreenSaver, on ? 1 : 0); } +SInt32 +CClientProxy1_0::getJumpZoneSize() const +{ + CLock lock(&m_mutex); + return m_info.m_zoneSize; +} + void CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -253,24 +260,17 @@ CClientProxy1_0::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CClientProxy1_0::getCenter(SInt32& x, SInt32& y) const -{ - CLock lock(&m_mutex); - x = m_info.m_mx; - y = m_info.m_my; -} - -void -CClientProxy1_0::getMousePos(SInt32&, SInt32&) const +CClientProxy1_0::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); } -SInt32 -CClientProxy1_0::getJumpZoneSize() const +void +CClientProxy1_0::getCursorCenter(SInt32& x, SInt32& y) const { CLock lock(&m_mutex); - return m_info.m_zoneSize; + x = m_info.m_mx; + y = m_info.m_my; } void diff --git a/server/CClientProxy1_0.h b/server/CClientProxy1_0.h index 59d463b7..80b20cb1 100644 --- a/server/CClientProxy1_0.h +++ b/server/CClientProxy1_0.h @@ -18,7 +18,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -30,12 +30,12 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: void recvInfo(bool notify); diff --git a/server/CPrimaryClient.cpp b/server/CPrimaryClient.cpp index d76d1734..89668c22 100644 --- a/server/CPrimaryClient.cpp +++ b/server/CPrimaryClient.cpp @@ -1,6 +1,6 @@ #include "CPrimaryClient.h" #include "IServer.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" #include "CClipboard.h" #include "CLog.h" @@ -15,7 +15,8 @@ // CPrimaryClient // -CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : +CPrimaryClient::CPrimaryClient(IServer* server, + IPrimaryScreenReceiver* receiver, const CString& name) : m_server(server), m_name(name), m_seqNum(0) @@ -25,9 +26,9 @@ CPrimaryClient::CPrimaryClient(IServer* server, const CString& name) : // create screen log((CLOG_DEBUG1 "creating primary screen")); #if WINDOWS_LIKE - m_screen = new CMSWindowsPrimaryScreen(this, m_server); + m_screen = new CMSWindowsPrimaryScreen(this, receiver); #elif UNIX_LIKE - m_screen = new CXWindowsPrimaryScreen(this, m_server); + m_screen = new CXWindowsPrimaryScreen(this, receiver); #endif } @@ -211,7 +212,7 @@ CPrimaryClient::mouseWheel(SInt32) } void -CPrimaryClient::screenSaver(bool) +CPrimaryClient::screensaver(bool) { // ignore } @@ -222,6 +223,12 @@ CPrimaryClient::getName() const return m_name; } +SInt32 +CPrimaryClient::getJumpZoneSize() const +{ + return m_info.m_zoneSize; +} + void CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { @@ -232,20 +239,14 @@ CPrimaryClient::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const } void -CPrimaryClient::getCenter(SInt32& x, SInt32& y) const -{ - x = m_info.m_mx; - y = m_info.m_my; -} - -void -CPrimaryClient::getMousePos(SInt32&, SInt32&) const +CPrimaryClient::getCursorPos(SInt32&, SInt32&) const { assert(0 && "shouldn't be called"); } -SInt32 -CPrimaryClient::getJumpZoneSize() const +void +CPrimaryClient::getCursorCenter(SInt32& x, SInt32& y) const { - return m_info.m_zoneSize; + x = m_info.m_mx; + y = m_info.m_my; } diff --git a/server/CPrimaryClient.h b/server/CPrimaryClient.h index 358afac5..3605a8e1 100644 --- a/server/CPrimaryClient.h +++ b/server/CPrimaryClient.h @@ -6,12 +6,13 @@ #include "ProtocolTypes.h" class IClipboard; -class IPrimaryScreen; +class CPrimaryScreen; +class IPrimaryScreenReceiver; class IServer; class CPrimaryClient : public IScreenReceiver, public IClient { public: - CPrimaryClient(IServer*, const CString& name); + CPrimaryClient(IServer*, IPrimaryScreenReceiver*, const CString& name); ~CPrimaryClient(); // manipulators @@ -44,7 +45,7 @@ public: virtual void close(); virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver); + bool forScreensaver); virtual bool leave(); virtual void setClipboard(ClipboardID, const CString&); virtual void grabClipboard(ClipboardID); @@ -56,17 +57,17 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbs, SInt32 yAbs); virtual void mouseWheel(SInt32 delta); - virtual void screenSaver(bool activate); + virtual void screensaver(bool activate); virtual CString getName() const; + virtual SInt32 getJumpZoneSize() const; virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const; - virtual void getCenter(SInt32& x, SInt32& y) const; - virtual void getMousePos(SInt32& x, SInt32& y) const; - virtual SInt32 getJumpZoneSize() const; + virtual void getCursorPos(SInt32& x, SInt32& y) const; + virtual void getCursorCenter(SInt32& x, SInt32& y) const; private: IServer* m_server; - IPrimaryScreen* m_screen; + CPrimaryScreen* m_screen; CString m_name; UInt32 m_seqNum; CClientInfo m_info; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp new file mode 100644 index 00000000..5dafa752 --- /dev/null +++ b/server/CPrimaryScreen.cpp @@ -0,0 +1,270 @@ +#include "CPrimaryScreen.h" +#include "IScreen.h" +#include "IScreenReceiver.h" +#include "ProtocolTypes.h" +#include "CThread.h" +#include "CLog.h" + +// FIXME -- should be locking + +// +// CPrimaryScreen +// + +CPrimaryScreen::CPrimaryScreen(IScreenReceiver* receiver) : + m_receiver(receiver), + m_active(false) +{ + // do nothing +} + +CPrimaryScreen::~CPrimaryScreen() +{ + // do nothing +} + +void +CPrimaryScreen::run() +{ + // change our priority + CThread::getCurrentThread().setPriority(-3); + + // run event loop + try { + log((CLOG_DEBUG "entering event loop")); + onPreRun(); + getScreen()->mainLoop(); + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + } + catch (...) { + onPostRun(); + log((CLOG_DEBUG "exiting event loop")); + throw; + } +} + +void +CPrimaryScreen::stop() +{ + getScreen()->exitMainLoop(); +} + +void +CPrimaryScreen::open() +{ + CClientInfo info; + try { + // subclass hook + onPreOpen(); + + // open the screen + getScreen()->open(); + + // create and prepare our window + createWindow(); + + // collect screen info + getScreen()->getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getScreen()->getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = getJumpZoneSize(); + + // update keyboard state + updateKeys(); + + // get notified of screen saver activation/deactivation + getScreen()->openScreenSaver(true); + + // subclass hook + onPostOpen(); + } + catch (...) { + close(); + throw; + } + + // enter the screen + enterNoWarp(); + + // send screen info + m_receiver->onInfoChanged(info); +} + +void +CPrimaryScreen::close() +{ + onPreClose(); + getScreen()->closeScreenSaver(); + destroyWindow(); + getScreen()->close(); + onPostClose(); +} + +void +CPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreensaver) +{ + log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreensaver ? " for screen saver" : "")); + assert(m_active == true); + + enterNoWarp(); + if (!forScreensaver) { + warpCursor(x, y); + } + else { + onEnterScreensaver(); + } +} + +void +CPrimaryScreen::enterNoWarp() +{ + // not active anymore + m_active = false; + + // subclass hook + onPreEnter(); + + // restore active window and hide our window + hideWindow(); + + // subclass hook + onPostEnter(); +} + +bool +CPrimaryScreen::leave() +{ + log((CLOG_INFO "leaving primary")); + assert(m_active == false); + + // subclass hook + onPreLeave(); + + // show our window + if (!showWindow()) { + onPostLeave(false); + return false; + } + + // get keyboard state as we leave + updateKeys(); + + // 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). + + // subclass hook + onPostLeave(true); + + // local client now active + m_active = true; + + // make sure our idea of clipboard ownership is correct + getScreen()->checkClipboards(); + + return true; +} + +void +CPrimaryScreen::setClipboard(ClipboardID id, + const IClipboard* clipboard) +{ + getScreen()->setClipboard(id, clipboard); +} + +void +CPrimaryScreen::grabClipboard(ClipboardID id) +{ + getScreen()->setClipboard(id, NULL); +} + +bool +CPrimaryScreen::isActive() const +{ + return m_active; +} + +void +CPrimaryScreen::getClipboard(ClipboardID id, + IClipboard* clipboard) const +{ + getScreen()->getClipboard(id, clipboard); +} + +SInt32 +CPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + +void +CPrimaryScreen::onPreRun() +{ + // do nothing +} + +void +CPrimaryScreen::onPostRun() +{ + // do nothing +} + +void +CPrimaryScreen::onPreOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPostOpen() +{ + // do nothing +} + +void +CPrimaryScreen::onPreClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPostClose() +{ + // do nothing +} + +void +CPrimaryScreen::onPreEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onPostEnter() +{ + // do nothing +} + +void +CPrimaryScreen::onEnterScreensaver() +{ + // do nothing +} + +void +CPrimaryScreen::onPreLeave() +{ + // do nothing +} + +void +CPrimaryScreen::onPostLeave(bool) +{ + // do nothing +} diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h new file mode 100644 index 00000000..06b020dd --- /dev/null +++ b/server/CPrimaryScreen.h @@ -0,0 +1,157 @@ +#ifndef CPRIMARYSCREEN_H +#define CPRIMARYSCREEN_H + +#include "ClipboardTypes.h" +#include "KeyTypes.h" + +class IClipboard; +class IScreen; +class IScreenReceiver; + +// platform independent base class for primary screen implementations. +// each platform will derive a class from CPrimaryScreen to handle +// platform dependent operations. +class CPrimaryScreen { +public: + CPrimaryScreen(IScreenReceiver*); + virtual ~CPrimaryScreen(); + + // manipulators + + // enter the screen's message loop. this returns when it detects + // the application should terminate or when stop() is called. + // run() may only be called between open() and close(). + void run(); + + // cause run() to return + void stop(); + + // initializes the screen and starts reporting events + void open(); + + // close the screen + void close(); + + // called when the user navigates to the primary screen. + // forScreensaver == true means that we're entering the primary + // screen because the screensaver has activated. + void enter(SInt32 x, SInt32 y, bool forScreensaver); + + // called when the user navigates off the primary screen. returns + // true iff successful. + bool leave(); + + // called when the configuration has changed. activeSides is a + // bitmask of CConfig::EDirectionMask indicating which sides of + // the primary screen are linked to clients. + virtual void reconfigure(UInt32 activeSides) = 0; + + // warp the cursor to the given absolute coordinates + virtual void warpCursor(SInt32 x, SInt32 y) = 0; + + // set the screen's clipboard contents. this is usually called + // soon after an enter(). + void setClipboard(ClipboardID, const IClipboard*); + + // synergy should own the clipboard + void grabClipboard(ClipboardID); + + // accessors + + // returns true iff the screen is active (i.e. the user has left + // the screen) + bool isActive() const; + + // return the contents of the given clipboard + void getClipboard(ClipboardID, IClipboard*) const; + + // returns the size of the zone on the edges of the screen that + // causes the cursor to jump to another screen. default returns 1. + virtual SInt32 getJumpZoneSize() const; + + // get the primary screen's current toggle modifier key state. + // the returned mask should have the corresponding bit set for + // each toggle key that is active. + virtual KeyModifierMask getToggleMask() const = 0; + + // return true if any key or button is being pressed or if there's + // any other reason that the user should not be allowed to switch + // screens. + virtual bool isLockedToScreen() const = 0; + + // get the platform dependent screen object + virtual IScreen* getScreen() const = 0; + +protected: + // template method hooks. these are called on entry/exit to the + // named method. onEnterScreensaver() is called by enter() iff + // forScreensaver is true. onPostLeave() is passed the result of + // showWindow(). override to do platform specific operations. + // defaults do nothing. + virtual void onPreRun(); + virtual void onPostRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreClose(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onEnterScreensaver(); + virtual void onPreLeave(); + virtual void onPostLeave(bool success); + + // create/destroy the window. this window is generally used to + // receive events and, when the user navigates to another screen, + // to capture keyboard and mouse input. + virtual void createWindow() = 0; + virtual void destroyWindow() = 0; + + // called when the user navigates off the primary screen. hide the + // cursor and grab exclusive access to the input devices. returns + // true iff successful. every call to showWindow() has a matching + // call to hideWindow() which preceeds it. return true iff + // successful (in particular, iff the input devices were grabbed). + // + // after a successful showWindow(), user input events and + // screensaver activation/deactivation should be reported to an + // IPrimaryScreenReceiver until hideWindow() is called. report + // mouse motion to IPrimaryScreenReceiver::onMouseMoveSecondary(). + // user input should not be delivered to any application except + // synergy. + virtual bool showWindow() = 0; + + // called when the user navigates back to the primary screen. show + // the cursor and ungab the input devices. + // + // after hideWindow(), user input events should be delivered normally. + // mouse motion over (at least) the jump zones must be reported to + // an IPrimaryScreenReceiver::onMouseMovePrimary(). + virtual void hideWindow() = 0; + + // prepare the cursor to report relative motion. when the user has + // navigated to another screen, synergy requires the cursor motion + // deltas, not the absolute coordinates. typically this is done by + // warping the cursor to the center of the primary screen and then + // every time it moves compute the motion and warp back to the + // center (but without reporting that warp as motion). this is + // only called after a successful showWindow(). + virtual void warpCursorToCenter() = 0; + + // check the current keyboard state. normally a screen will save + // the keyboard state in this method and use this shadow state + // when handling user input and in methods like isLockedToScreen(). + virtual void updateKeys() = 0; + +private: + void enterNoWarp(); + +private: + // FIXME -- should have a mutex + + IScreenReceiver* m_receiver; + + // m_active is true if this screen has been left + bool m_active; +}; + +#endif diff --git a/server/CServer.cpp b/server/CServer.cpp index 7457c99b..f13af415 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -92,7 +92,6 @@ CServer::run() } // handle events - log((CLOG_DEBUG "starting event handling")); m_primaryClient->run(); // clean up @@ -108,9 +107,6 @@ CServer::run() stopThreads(); \ delete m_httpServer; \ m_httpServer = NULL; \ - if (m_primaryClient != NULL) { \ - closePrimaryScreen(); \ - } \ } while (false) FINALLY; } @@ -144,6 +140,15 @@ CServer::quit() m_primaryClient->stop(); } +void +CServer::close() +{ + if (m_primaryClient != NULL) { + closePrimaryScreen(); + } + log((CLOG_INFO "closed screen")); +} + bool CServer::setConfig(const CConfig& config) { @@ -211,20 +216,6 @@ CServer::getActivePrimarySides() const return sides; } -void -CServer::onError() -{ - // stop all running threads but don't wait too long since some - // threads may be unable to proceed until this thread returns. - stopThreads(3.0); - - // done with the HTTP server - delete m_httpServer; - m_httpServer = NULL; - - // note -- we do not attempt to close down the primary screen -} - void CServer::onInfoChanged(const CString& name, const CClientInfo& info) { @@ -347,10 +338,74 @@ CServer::onClipboardChangedNoLock(ClipboardID id, m_active->setClipboard(id, m_clipboards[id].m_clipboardData); } -bool -CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) +void +CServer::onError() { - return false; + // stop all running threads but don't wait too long since some + // threads may be unable to proceed until this thread returns. + stopThreads(3.0); + + // done with the HTTP server + delete m_httpServer; + m_httpServer = NULL; + + // note -- we do not attempt to close down the primary screen +} + +void +CServer::onScreensaver(bool activated) +{ + log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); + CLock lock(&m_mutex); + + if (activated) { + // save current screen and position + m_activeSaver = m_active; + m_xSaver = m_x; + m_ySaver = m_y; + + // jump to primary screen + if (m_active != m_primaryClient) { + switchScreen(m_primaryClient, 0, 0, true); + } + } + else { + // jump back to previous screen and position. we must check + // that the position is still valid since the screen may have + // changed resolutions while the screen saver was running. + if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { + // check position + IClient* screen = m_activeSaver; + SInt32 x, y, w, h; + screen->getShape(x, y, w, h); + SInt32 zoneSize = screen->getJumpZoneSize(); + if (m_xSaver < x + zoneSize) { + m_xSaver = x + zoneSize; + } + else if (m_xSaver >= x + w - zoneSize) { + m_xSaver = x + w - zoneSize - 1; + } + if (m_ySaver < y + zoneSize) { + m_ySaver = y + zoneSize; + } + else if (m_ySaver >= y + h - zoneSize) { + m_ySaver = y + h - zoneSize - 1; + } + + // jump + switchScreen(screen, m_xSaver, m_ySaver, false); + } + + // reset state + m_activeSaver = NULL; + } + + // send message to all clients + for (CClientList::const_iterator index = m_clients.begin(); + index != m_clients.end(); ++index) { + IClient* client = index->second; + client->screensaver(activated); + } } void @@ -615,60 +670,10 @@ CServer::onMouseWheel(SInt32 delta) m_active->mouseWheel(delta); } -void -CServer::onScreenSaver(bool activated) +bool +CServer::onCommandKey(KeyID /*id*/, KeyModifierMask /*mask*/, bool /*down*/) { - log((CLOG_DEBUG "onScreenSaver %s", activated ? "activated" : "deactivated")); - CLock lock(&m_mutex); - - if (activated) { - // save current screen and position - m_activeSaver = m_active; - m_xSaver = m_x; - m_ySaver = m_y; - - // jump to primary screen - if (m_active != m_primaryClient) { - switchScreen(m_primaryClient, 0, 0, true); - } - } - else { - // jump back to previous screen and position. we must check - // that the position is still valid since the screen may have - // changed resolutions while the screen saver was running. - if (m_activeSaver != NULL && m_activeSaver != m_primaryClient) { - // check position - IClient* screen = m_activeSaver; - SInt32 x, y, w, h; - screen->getShape(x, y, w, h); - SInt32 zoneSize = screen->getJumpZoneSize(); - if (m_xSaver < x + zoneSize) { - m_xSaver = x + zoneSize; - } - else if (m_xSaver >= x + w - zoneSize) { - m_xSaver = x + w - zoneSize - 1; - } - if (m_ySaver < y + zoneSize) { - m_ySaver = y + zoneSize; - } - else if (m_ySaver >= y + h - zoneSize) { - m_ySaver = y + h - zoneSize - 1; - } - - // jump - switchScreen(screen, m_xSaver, m_ySaver, false); - } - - // reset state - m_activeSaver = NULL; - } - - // send message to all clients - for (CClientList::const_iterator index = m_clients.begin(); - index != m_clients.end(); ++index) { - IClient* client = index->second; - client->screenSaver(activated); - } + return false; } bool @@ -689,7 +694,7 @@ CServer::isLockedToScreenNoLock() const } void -CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) +CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) { assert(dst != NULL); #ifndef NDEBUG @@ -741,7 +746,8 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool screenSaver) // enter new screen m_active->enter(x, y, m_seqNum, - m_primaryClient->getToggleMask(), screenSaver); + m_primaryClient->getToggleMask(), + forScreensaver); // send the clipboard data to new active screen for (ClipboardID id = 0; id < kClipboardEnd; ++id) { @@ -1209,7 +1215,7 @@ CServer::runClient(void* vsocket) { CLock lock(&m_mutex); if (m_activeSaver != NULL) { - proxy->screenSaver(true); + proxy->screensaver(true); } } @@ -1510,7 +1516,7 @@ CServer::openPrimaryScreen() // create the primary client and open it try { - m_primaryClient = new CPrimaryClient(this, primary); + m_primaryClient = new CPrimaryClient(this, this, primary); // add connection addConnection(m_primaryClient); @@ -1593,7 +1599,7 @@ CServer::removeConnection(const CString& name) IClient* active = (m_activeSaver != NULL) ? m_activeSaver : m_active; if (active == index->second && active != m_primaryClient) { // record new position (center of primary screen) - m_primaryClient->getCenter(m_x, m_y); + m_primaryClient->getCursorCenter(m_x, m_y); // don't notify active screen since it probably already disconnected log((CLOG_INFO "jump from \"%s\" to \"%s\" at %d,%d", active->getName().c_str(), m_primaryClient->getName().c_str(), m_x, m_y)); diff --git a/server/CServer.h b/server/CServer.h index d8998116..7ddb4956 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -2,6 +2,7 @@ #define CSERVER_H #include "IServer.h" +#include "IPrimaryScreenReceiver.h" #include "CConfig.h" #include "CClipboard.h" #include "CCondVar.h" @@ -20,7 +21,7 @@ class IServerProtocol; class ISocketFactory; class ISecurityFactory; -class CServer : public IServer { +class CServer : public IServer, public IPrimaryScreenReceiver { public: CServer(const CString& serverName); ~CServer(); @@ -38,6 +39,9 @@ public: // after a successful open(). void quit(); + // close the server's screen + void close(); + // update screen map. returns true iff the new configuration was // accepted. bool setConfig(const CConfig&); @@ -50,8 +54,14 @@ public: // get the primary screen's name CString getPrimaryScreenName() const; + // IServer overrides + virtual void onInfoChanged(const CString&, const CClientInfo&); + virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); + virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); + // IPrimaryScreenReceiver overrides virtual void onError(); + virtual void onScreensaver(bool activated); virtual void onKeyDown(KeyID, KeyModifierMask); virtual void onKeyUp(KeyID, KeyModifierMask); virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count); @@ -60,12 +70,6 @@ public: virtual bool onMouseMovePrimary(SInt32 x, SInt32 y); virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy); virtual void onMouseWheel(SInt32 delta); - virtual void onScreenSaver(bool activated); - - // IServer overrides - virtual void onInfoChanged(const CString&, const CClientInfo&); - virtual bool onGrabClipboard(const CString&, ClipboardID, UInt32); - virtual void onClipboardChanged(ClipboardID, UInt32, const CString&); protected: bool onCommandKey(KeyID, KeyModifierMask, bool down); diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index a4cdd4b1..dff594c8 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -1,11 +1,7 @@ #include "CXWindowsPrimaryScreen.h" -#include "IScreenReceiver.h" -#include "IPrimaryScreenReceiver.h" -#include "CXWindowsClipboard.h" -#include "CXWindowsScreenSaver.h" +#include "CXWindowsScreen.h" #include "CXWindowsUtil.h" -#include "CClipboard.h" -#include "ProtocolTypes.h" +#include "IPrimaryScreenReceiver.h" #include "XScreen.h" #include "CThread.h" #include "CLog.h" @@ -26,162 +22,17 @@ CXWindowsPrimaryScreen::CXWindowsPrimaryScreen( IScreenReceiver* receiver, IPrimaryScreenReceiver* primaryReceiver) : - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), - m_active(false), + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), m_window(None) { - // do nothing + m_screen = new CXWindowsScreen(receiver, this); } CXWindowsPrimaryScreen::~CXWindowsPrimaryScreen() { assert(m_window == None); -} - -void -CXWindowsPrimaryScreen::run() -{ - // change our priority - CThread::getCurrentThread().setPriority(-3); - - // run event loop - try { - log((CLOG_INFO "entering event loop")); - mainLoop(); - log((CLOG_INFO "exiting event loop")); - } - catch (...) { - log((CLOG_INFO "exiting event loop")); - throw; - } -} - -void -CXWindowsPrimaryScreen::stop() -{ - exitMainLoop(); -} - -void -CXWindowsPrimaryScreen::open() -{ - assert(m_window == None); - - CClientInfo info; - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // get the display - CDisplayLock display(this); - - // initialize the clipboards - initClipboards(m_window); - - // miscellaneous initialization - m_atomScreenSaver = XInternAtom(display, "SCREENSAVER", False); - - // 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 notified of screen saver activation/deactivation - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // enter the screen - enterNoWarp(); - - // send screen info - m_receiver->onInfoChanged(info); -} - -void -CXWindowsPrimaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CXWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) -{ - log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); - assert(m_active == true); - assert(m_window != None); - - // enter the screen - enterNoWarp(); - - // warp to requested location - if (!forScreenSaver) { - 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); - } -} - -bool -CXWindowsPrimaryScreen::leave() -{ - log((CLOG_INFO "leaving primary")); - 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; + delete m_screen; } void @@ -193,7 +44,7 @@ CXWindowsPrimaryScreen::reconfigure(UInt32) void CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) { - CDisplayLock display(this); + CDisplayLock display(m_screen); // warp mouse warpCursorNoFlush(display, x, y); @@ -213,30 +64,10 @@ CXWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CXWindowsPrimaryScreen::setClipboard(ClipboardID id, - const IClipboard* clipboard) -{ - setDisplayClipboard(id, clipboard); -} - -void -CXWindowsPrimaryScreen::grabClipboard(ClipboardID id) -{ - setDisplayClipboard(id, NULL); -} - -void -CXWindowsPrimaryScreen::getClipboard(ClipboardID id, - IClipboard* clipboard) const -{ - getDisplayClipboard(id, clipboard); -} - KeyModifierMask CXWindowsPrimaryScreen::getToggleMask() const { - CDisplayLock display(this); + CDisplayLock display(m_screen); // query the pointer to get the keyboard state Window root, window; @@ -265,7 +96,7 @@ CXWindowsPrimaryScreen::getToggleMask() const bool CXWindowsPrimaryScreen::isLockedToScreen() const { - CDisplayLock display(this); + CDisplayLock display(m_screen); // query the pointer to get the button state Window root, window; @@ -295,11 +126,29 @@ CXWindowsPrimaryScreen::isLockedToScreen() const return false; } -bool -CXWindowsPrimaryScreen::onPreDispatch(const CEvent* event) +IScreen* +CXWindowsPrimaryScreen::getScreen() const { - // forward to superclass - return CXWindowsScreen::onPreDispatch(event); + return m_screen; +} + +void +CXWindowsPrimaryScreen::onError() +{ + // tell server to shutdown + m_receiver->onError(); +} + +void +CXWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + +bool +CXWindowsPrimaryScreen::onPreDispatch(const CEvent*) +{ + return false; } bool @@ -313,41 +162,28 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) case CreateNotify: { // select events on new window - CDisplayLock display(this); + CDisplayLock display(m_screen); selectEvents(display, xevent.xcreatewindow.window); } return true; case MappingNotify: - { - // keyboard mapping changed - CDisplayLock display(this); - XRefreshKeyboardMapping(&xevent.xmapping); - updateModifierMap(display); - } + // keyboard mapping changed + updateKeys(); 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); + m_receiver->onKeyDown(key, mask); if (key == XK_Caps_Lock && m_capsLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierCapsLock); + m_receiver->onKeyUp(key, mask | KeyModifierCapsLock); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyUp(key, mask | KeyModifierNumLock); + m_receiver->onKeyUp(key, mask | KeyModifierNumLock); } } } @@ -369,20 +205,24 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) filter.m_keycode = xevent.xkey.keycode; // now check for event - XEvent xevent2; - CDisplayLock display(this); - if (XCheckIfEvent(display, &xevent2, - &CXWindowsPrimaryScreen::findKeyEvent, - (XPointer)&filter) != True) { + bool hasPress; + { + XEvent xevent2; + CDisplayLock display(m_screen); + hasPress = (XCheckIfEvent(display, &xevent2, + &CXWindowsPrimaryScreen::findKeyEvent, + (XPointer)&filter) == True); + } + if (!hasPress) { // 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); + m_receiver->onKeyDown(key, mask); } else if (key == XK_Num_Lock && m_numLockHalfDuplex) { - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } - m_primaryReceiver->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); } else { // found a press event following so it's a repeat. @@ -390,7 +230,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // 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); + m_receiver->onKeyRepeat(key, mask, 1); } } } @@ -401,7 +241,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) log((CLOG_DEBUG1 "event: ButtonPress button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + m_receiver->onMouseDown(button); } } return true; @@ -411,15 +251,15 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) log((CLOG_DEBUG1 "event: ButtonRelease button=%d", xevent.xbutton.button)); const ButtonID button = mapButton(xevent.xbutton.button); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + m_receiver->onMouseUp(button); } else if (xevent.xbutton.button == 4) { // wheel forward (away from user) - m_primaryReceiver->onMouseWheel(120); + m_receiver->onMouseWheel(120); } else if (xevent.xbutton.button == 5) { // wheel backward (toward user) - m_primaryReceiver->onMouseWheel(-120); + m_receiver->onMouseWheel(-120); } } return true; @@ -443,14 +283,14 @@ CXWindowsPrimaryScreen::onEvent(CEvent* 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); + CDisplayLock display(m_screen); do { XMaskEvent(display, PointerMotionMask, &xevent); } while (!xevent.xmotion.send_event); } - else if (!m_active) { + else if (!isActive()) { // motion on primary screen - m_primaryReceiver->onMouseMovePrimary(m_x, m_y); + m_receiver->onMouseMovePrimary(m_x, m_y); } else { // motion on secondary screen. warp mouse back to @@ -470,7 +310,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) 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); + CDisplayLock display(m_screen); warpCursorNoFlush(display, m_xCenter, m_yCenter); } @@ -481,7 +321,7 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) // warping to the primary screen's enter position, // effectively overriding it. if (x != 0 || y != 0) { - m_primaryReceiver->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } } @@ -492,77 +332,117 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) } void -CXWindowsPrimaryScreen::onUnexpectedClose() +CXWindowsPrimaryScreen::onPreRun() { - // tell server to shutdown - m_primaryReceiver->onError(); -} - -void -CXWindowsPrimaryScreen::onLostClipboard(ClipboardID id) -{ - // tell server that the clipboard was grabbed locally - 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() +CXWindowsPrimaryScreen::onPreOpen() { - m_active = false; - hideWindow(); + assert(m_window == None); +} + +void +CXWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // check for peculiarities + // FIXME -- may have to get these from some database + m_numLockHalfDuplex = false; + m_capsLockHalfDuplex = false; +// m_numLockHalfDuplex = true; +// m_capsLockHalfDuplex = true; +} + +void +CXWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != None); +} + +void +CXWindowsPrimaryScreen::onEnterScreenSaver() +{ + CDisplayLock display(m_screen); + + // set keyboard focus to root window. the screensaver should then + // pick up key events for when the user enters a password to unlock. + XSetInputFocus(display, PointerRoot, PointerRoot, CurrentTime); +} + +void +CXWindowsPrimaryScreen::createWindow() +{ + assert(m_window == None); + + // get size of screen + SInt32 x, y, w, h; + m_screen->getShape(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 = m_screen->getBlankCursor(); + + { + // create the grab window + CDisplayLock display(m_screen); + m_window = XCreateWindow(display, m_screen->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, m_screen->getRoot()); + } + + // tell our superclass about the window + m_screen->setWindow(m_window); +} + +void +CXWindowsPrimaryScreen::destroyWindow() +{ + // display can be NULL if the server unexpectedly disconnected + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } } bool CXWindowsPrimaryScreen::showWindow() { - CDisplayLock display(this); + assert(m_window != None); + + CDisplayLock display(m_screen); // raise and show the input window XMapRaised(display, m_window); @@ -616,83 +496,51 @@ CXWindowsPrimaryScreen::showWindow() void CXWindowsPrimaryScreen::hideWindow() { - CDisplayLock display(this); + CDisplayLock display(m_screen); // unmap the grab window. this also ungrabs the mouse and keyboard. XUnmapWindow(display, m_window); } void -CXWindowsPrimaryScreen::checkClipboard() +CXWindowsPrimaryScreen::warpCursorToCenter() { - // do nothing, we're always up to date + warpCursor(m_xCenter, m_yCenter); } void -CXWindowsPrimaryScreen::createWindow() +CXWindowsPrimaryScreen::warpCursorNoFlush( + Display* display, SInt32 x, SInt32 y) { - assert(m_window == None); + assert(display != NULL); + assert(m_window != None); - // get size of screen - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); + // 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 = m_screen->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); - // 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(); + // warp mouse + XWarpPointer(display, None, m_screen->getRoot(), 0, 0, 0, 0, x, y); - // 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)); + // send an event that we can recognize after the mouse warp + XSendEvent(display, m_window, False, 0, &eventAfter); + XSync(display, False); - // 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; + log((CLOG_DEBUG2 "warped to %d,%d", x, y)); } void @@ -777,7 +625,7 @@ CXWindowsPrimaryScreen::mapKey(XKeyEvent* event) const KeySym keysym; char dummy[1]; - CDisplayLock display(this); + CDisplayLock display(m_screen); XLookupString(event, dummy, 0, &keysym, NULL); return static_cast(keysym); } @@ -795,8 +643,10 @@ CXWindowsPrimaryScreen::mapButton(unsigned int button) const } void -CXWindowsPrimaryScreen::updateModifierMap(Display* display) +CXWindowsPrimaryScreen::updateKeys() { + CDisplayLock display(m_screen); + // get modifier map from server XModifierKeymap* keymap = XGetModifierMapping(display); diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 8bf41373..6b261400 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -1,63 +1,59 @@ #ifndef CXWINDOWSPRIMARYSCREEN_H #define CXWINDOWSPRIMARYSCREEN_H -#include "CXWindowsScreen.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" +#include "IScreenEventHandler.h" #include "MouseTypes.h" +#if defined(X_DISPLAY_MISSING) +# error X11 is required to build synergy +#else +# include +#endif +class CXWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; -class CXWindowsPrimaryScreen : public CXWindowsScreen, public IPrimaryScreen { +class CXWindowsPrimaryScreen : + public CPrimaryScreen, public IScreenEventHandler { public: CXWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CXWindowsPrimaryScreen(); - // IPrimaryScreen overrides - virtual void run(); - virtual void stop(); - virtual void open(); - virtual void close(); - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, bool); - virtual bool leave(); + // CPrimaryScreen overrides virtual void reconfigure(UInt32 activeSides); - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute); - virtual void setClipboard(ClipboardID, const IClipboard*); - virtual void grabClipboard(ClipboardID); - virtual void getClipboard(ClipboardID, IClipboard*) const; + virtual void warpCursor(SInt32 x, SInt32 y); virtual KeyModifierMask getToggleMask() const; virtual bool isLockedToScreen() const; + virtual IScreen* getScreen() const; -protected: - // CXWindowsScreen overrides + // IScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual void onUnexpectedClose(); - virtual void onLostClipboard(ClipboardID); + +protected: + // CPrimaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPreEnter(); + virtual void onPreLeave(); + virtual void onEnterScreenSaver(); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); private: - SInt32 getJumpZoneSize() const; - - void warpCursorToCenter(); void warpCursorNoFlush(Display*, SInt32 xAbsolute, SInt32 yAbsolute); - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - void selectEvents(Display*, Window) const; void doSelectEvents(Display*, Window) const; @@ -65,8 +61,6 @@ private: KeyID mapKey(XKeyEvent*) const; ButtonID mapButton(unsigned int button) const; - void updateModifierMap(Display* display); - class CKeyEventInfo { public: int m_event; @@ -77,15 +71,12 @@ private: static Bool findKeyEvent(Display*, XEvent* xevent, XPointer arg); private: - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; + CXWindowsScreen* m_screen; + IPrimaryScreenReceiver* m_receiver; - bool m_active; + // our window Window m_window; - // atom for screen saver messages - Atom m_atomScreenSaver; - // note toggle keys that toggle on up/down (false) or on // transition (true) bool m_numLockHalfDuplex; diff --git a/server/IPrimaryScreen.h b/server/IPrimaryScreen.h deleted file mode 100644 index be63f11f..00000000 --- a/server/IPrimaryScreen.h +++ /dev/null @@ -1,95 +0,0 @@ -#ifndef IPRIMARYSCREEN_H -#define IPRIMARYSCREEN_H - -#include "IInterface.h" -#include "KeyTypes.h" -#include "ClipboardTypes.h" - -class IClipboard; - -class IPrimaryScreen : public IInterface { -public: - // manipulators - - // enter the screen's message loop. this returns when it detects - // the application should terminate or when stop() is called. - // the screen must be open()'d before run() and must not be - // close()'d until run() returns. - virtual void run() = 0; - - // cause run() to return - virtual void stop() = 0; - - // initialize the screen and start reporting events to the receiver - // (which is set through some interface of the derived class). - // events should be reported no matter where on the screen they - // occur but do not interfere with normal event dispatch. the - // screen saver engaging should be reported as an event. if that - // can't be detected then this object should disable the system's - // screen saver timer and should start the screen saver after - // idling for an appropriate time. - // - // open() must call receiver->onInfoChanged() to notify of the - // primary screen's initial resolution and jump zone size. it - // must also call receiver->onClipboardChanged() for each - // clipboard that the primary screen has. - virtual void open() = 0; - - // close the screen. should restore the screen saver timer if it - // was disabled. - virtual void close() = 0; - - // called when the user navigates back to the primary screen. - // warp the cursor to the given coordinates, unhide it, and - // ungrab the input devices. every call to enter has a matching - // call to leave() which preceeds it, however the screen should - // assume an implicit call to enter() in the call to open(). - // if warpCursor is false then do not warp the mouse. - // - // enter() must not call any receiver methods except onError(). - virtual void enter(SInt32 xAbsolute, SInt32 yAbsolute, - bool forScreenSaver) = 0; - - // called when the user navigates off the primary screen. hide the - // cursor and grab exclusive access to the input devices. return - // true iff successful. - // - // leave() must not call any receiver methods except onError(). - virtual bool leave() = 0; - - // called when the configuration has changed. activeSides is a - // bitmask of CConfig::EDirectionMask indicating which sides of - // the primary screen are linked to clients. - virtual void reconfigure(UInt32 activeSides) = 0; - - // warp the cursor to the given position - virtual void warpCursor(SInt32 xAbsolute, SInt32 yAbsolute) = 0; - - // set the screen's clipboard contents. this is usually called - // soon after an enter(). - // - // setClipboard() must not call any receiver methods except onError(). - virtual void setClipboard(ClipboardID, const IClipboard*) = 0; - - // synergy should own the clipboard - virtual void grabClipboard(ClipboardID) = 0; - - // accessors - - // return the contents of the given clipboard. - // - // getClipboard() must not call any receiver methods except onError(). - virtual void getClipboard(ClipboardID, IClipboard*) const = 0; - - // get the primary screen's current toggle modifier key state. - // the returned mask should have the corresponding bit set for - // each toggle key that is active. - virtual KeyModifierMask getToggleMask() const = 0; - - // return true if any key or button is being pressed or if there's - // any other reason that the user should not be allowed to switch - // screens. - virtual bool isLockedToScreen() const = 0; -}; - -#endif diff --git a/server/Makefile.am b/server/Makefile.am index 397ff64e..bcab55a3 100644 --- a/server/Makefile.am +++ b/server/Makefile.am @@ -9,6 +9,7 @@ synergyd_SOURCES = \ CConfig.cpp \ CHTTPServer.cpp \ CPrimaryClient.cpp \ + CPrimaryScreen.cpp \ CServer.cpp \ CXWindowsPrimaryScreen.cpp \ server.cpp \ @@ -17,9 +18,9 @@ synergyd_SOURCES = \ CConfig.h \ CHTTPServer.h \ CPrimaryClient.h \ + CPrimaryScreen.h \ CServer.h \ CXWindowsPrimaryScreen.h \ - IPrimaryScreen.h \ $(NULL) synergyd_LDADD = \ $(DEPTH)/platform/libplatform.a \ diff --git a/server/server.cpp b/server/server.cpp index 5e3ec8b3..9fcaee93 100644 --- a/server/server.cpp +++ b/server/server.cpp @@ -132,6 +132,7 @@ realMain(CMutex* mutex) } // clean up + s_server->close(); delete s_server; s_server = NULL; CLog::setLock(NULL); @@ -142,8 +143,11 @@ realMain(CMutex* mutex) if (!locked && mutex != NULL) { mutex->lock(); } - delete s_server; - s_server = NULL; + if (s_server != NULL) { + s_server->close(); + delete s_server; + s_server = NULL; + } CLog::setLock(NULL); s_logMutex = NULL; throw; diff --git a/synergy/IClient.h b/synergy/IClient.h index 2c363368..bf570772 100644 --- a/synergy/IClient.h +++ b/synergy/IClient.h @@ -7,6 +7,8 @@ #include "MouseTypes.h" #include "CString.h" +// the client interface. this provides all the methods necessary for +// the server to communicate with a client. class IClient : public IInterface { public: // manipulators @@ -23,11 +25,11 @@ public: // enter the screen. the cursor should be warped to xAbs,yAbs. // the client should record seqNum for future reporting of // clipboard changes. mask is the expected toggle button state. - // screenSaver is true if the screen is being entered because + // forScreensaver is true if the screen is being entered because // the screen saver is starting. virtual void enter(SInt32 xAbs, SInt32 yAbs, UInt32 seqNum, KeyModifierMask mask, - bool screenSaver) = 0; + bool forScreensaver) = 0; // leave the screen. returns false if the user may not leave the // client's screen. @@ -54,25 +56,25 @@ public: virtual void mouseUp(ButtonID) = 0; virtual void mouseMove(SInt32 xAbs, SInt32 yAbs) = 0; virtual void mouseWheel(SInt32 delta) = 0; - virtual void screenSaver(bool activate) = 0; + virtual void screensaver(bool activate) = 0; // accessors // get the client's identifier virtual CString getName() const = 0; + // get the size of jump zone + virtual SInt32 getJumpZoneSize() const = 0; + // get the screen's shape virtual void getShape(SInt32& x, SInt32& y, SInt32& width, SInt32& height) const = 0; - // get the center pixel - virtual void getCenter(SInt32& x, SInt32& y) const = 0; - // get the mouse position - virtual void getMousePos(SInt32& x, SInt32& y) const = 0; + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; - // get the size of jump zone - virtual SInt32 getJumpZoneSize() const = 0; + // get the center pixel + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; }; #endif diff --git a/synergy/IPrimaryScreenReceiver.h b/synergy/IPrimaryScreenReceiver.h index 60758141..ff816928 100644 --- a/synergy/IPrimaryScreenReceiver.h +++ b/synergy/IPrimaryScreenReceiver.h @@ -5,12 +5,22 @@ #include "KeyTypes.h" #include "MouseTypes.h" +// the interface for receiving notification of events on the primary +// screen. the server implements this interface to handle user input. +// platform dependent primary screen implementation will need to take +// an IPrimaryScreenReceiver* and notify it of events. class IPrimaryScreenReceiver : public IInterface { public: - // notify of serious error. this implies that the server should - // shutdown. + // called if the display is unexpectedly closing. + + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. virtual void onError() = 0; + // called when the screensaver is activated or deactivated + virtual void onScreensaver(bool activated) = 0; + // call to notify of events. onMouseMovePrimary() returns // true iff the mouse enters a jump zone and jumps. virtual void onKeyDown(KeyID, KeyModifierMask) = 0; @@ -21,8 +31,6 @@ public: virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; virtual void onMouseWheel(SInt32 delta) = 0; - virtual void onScreenSaver(bool activated) = 0; - }; #endif diff --git a/synergy/IScreen.h b/synergy/IScreen.h new file mode 100644 index 00000000..88697926 --- /dev/null +++ b/synergy/IScreen.h @@ -0,0 +1,66 @@ +#ifndef ISCREEN_H +#define ISCREEN_H + +#include "IInterface.h" +#include "ClipboardTypes.h" + +class IClipboard; + +// the interface for platform dependent screen implementations. each +// platform will derive a type from IScreen for interaction with the +// platform's screen that's common to primary and secondary screens. +class IScreen : public IInterface { +public: + // manipulators + + // open the screen + virtual void open() = 0; + + // runs an event loop and returns when exitMainLoop() is called. + // must be called between open() and close(). + virtual void mainLoop() = 0; + + // force mainLoop() to return + virtual void exitMainLoop() = 0; + + // close the screen + virtual void close() = 0; + + // set the contents of the clipboard + virtual bool setClipboard(ClipboardID, const IClipboard*) = 0; + + // check clipboard ownership and notify IScreenReceiver (set through + // some other interface) if any changed + virtual void checkClipboards() = 0; + + // open/close the screen saver. if notify is true then this object + // will call IScreenEventHandler's onScreenSaver() when the screensaver + // activates or deactivates until close. if notify is false then + // the screen saver is disabled on open and restored on close. + virtual void openScreenSaver(bool notify) = 0; + virtual void closeScreenSaver() = 0; + + // activate or deactivate the screen saver + virtual void screensaver(bool activate) = 0; + + // FIXME -- need explanation + virtual void syncDesktop() = 0; + + // accessors + + // get the contents of the clipboard + virtual bool getClipboard(ClipboardID, IClipboard*) const = 0; + + // get the shape of the screen + virtual void getShape(SInt32& x, SInt32& y, + SInt32& w, SInt32& h) const = 0; + + // get the current cursor coordinates + virtual void getCursorPos(SInt32& x, SInt32& y) const = 0; + + // get the cursor center position + // FIXME -- need better explanation + virtual void getCursorCenter(SInt32& x, SInt32& y) const = 0; +}; + +#endif diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h new file mode 100644 index 00000000..d4e04ab3 --- /dev/null +++ b/synergy/IScreenEventHandler.h @@ -0,0 +1,39 @@ +#ifndef ISCREENEVENTHANDLER_H +#define ISCREENEVENTHANDLER_H + +#include "IInterface.h" + +// the platform screen should define this +class CEvent; + +class IScreen; + +// the interface through which IScreen sends notification of events. +// each platform will derive two types from IScreenEventHandler, one +// for handling events on the primary screen and one for the +// secondary screen. the header file with the IScreen subclass for +// each platform should define the CEvent type, which depends on the +// type of native events for that platform. +class IScreenEventHandler : public IInterface { +public: + // manipulators + + // called if the screen is unexpectedly closing. this implies that + // the screen is no longer usable and that the program should + // close the screen and possibly terminate. + virtual void onError() = 0; + + // called when the screensaver is activated or deactivated + virtual void onScreensaver(bool activated) = 0; + + // 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; +}; + +#endif diff --git a/synergy/IScreenReceiver.h b/synergy/IScreenReceiver.h index 097e4405..02fcf936 100644 --- a/synergy/IScreenReceiver.h +++ b/synergy/IScreenReceiver.h @@ -6,6 +6,8 @@ #include "ProtocolTypes.h" #include "CString.h" +// the interface for types that receive screen resize and clipboard +// notifications (indirectly) from the system. class IScreenReceiver : public IInterface { public: // notify of client info change diff --git a/synergy/IServer.h b/synergy/IServer.h index 67033b59..f6a4e458 100644 --- a/synergy/IServer.h +++ b/synergy/IServer.h @@ -1,13 +1,19 @@ #ifndef ISERVER_H #define ISERVER_H -#include "IPrimaryScreenReceiver.h" +#include "IInterface.h" #include "ClipboardTypes.h" #include "CString.h" class CClientInfo; -class IServer : public IPrimaryScreenReceiver { +// the server interface. this provides all the methods necessary for +// clients to communicate with the server. note that the methods +// in this interface are similar to the methods in IScreenReceiver but +// include extra parameters. this interface is suitable for server-side +// client proxies. client-side objects should use the IScreenReceiver +// interface since the extra parameters are meaningless on the client-side. +class IServer : public IInterface { public: // manipulators @@ -23,18 +29,6 @@ public: // notify of new clipboard data virtual void onClipboardChanged(ClipboardID, UInt32 seqNum, const CString& data) = 0; - - // IPrimaryScreenReceiver overrides - virtual void onError() = 0; - virtual void onKeyDown(KeyID, KeyModifierMask) = 0; - virtual void onKeyUp(KeyID, KeyModifierMask) = 0; - virtual void onKeyRepeat(KeyID, KeyModifierMask, SInt32 count) = 0; - virtual void onMouseDown(ButtonID) = 0; - virtual void onMouseUp(ButtonID) = 0; - virtual bool onMouseMovePrimary(SInt32 x, SInt32 y) = 0; - virtual void onMouseMoveSecondary(SInt32 dx, SInt32 dy) = 0; - virtual void onMouseWheel(SInt32 delta) = 0; - virtual void onScreenSaver(bool activated) = 0; }; #endif diff --git a/synergy/Makefile.am b/synergy/Makefile.am index aedd0e68..9522e1cc 100644 --- a/synergy/Makefile.am +++ b/synergy/Makefile.am @@ -20,6 +20,8 @@ libsynergy_a_SOURCES = \ IClient.h \ IClipboard.h \ IPrimaryScreenReceiver.h\ + IScreen.h \ + IScreenEventHandler.h \ IScreenReceiver.h \ IScreenSaver.h \ IServer.h \