From 4b468620261e116c26a9202dd1b85299f277461a Mon Sep 17 00:00:00 2001 From: crs Date: Mon, 15 Jul 2002 15:01:36 +0000 Subject: [PATCH] checkpoint. refactored win32 code. had to edit and rename some files so this is only a checkpoint. --- client/CMSWindowsSecondaryScreen.cpp | 783 ++++------------------ client/CMSWindowsSecondaryScreen.h | 101 +-- client/CSecondaryScreen.cpp | 10 +- client/CSecondaryScreen.h | 4 +- client/CXWindowsSecondaryScreen.cpp | 36 +- client/CXWindowsSecondaryScreen.h | 1 + client/client.dsp | 8 +- platform/CMSWindowsScreen.cpp | 602 ++++++++++++++--- platform/CMSWindowsScreen.h | 134 ++-- {server => platform}/CSynergyHook.cpp | 0 {server => platform}/CSynergyHook.h | 0 platform/CXWindowsScreen.cpp | 6 +- platform/CXWindowsScreen.h | 5 +- platform/IMSWindowsScreenEventHandler.h | 26 + {server => platform}/makehook.dsp | 0 platform/platform.dsp | 4 + {server => platform}/synrgyhk.dsp | 0 server/CConfig.cpp | 8 +- server/CConfig.h | 6 +- server/CHTTPServer.cpp | 32 +- server/CMSWindowsPrimaryScreen.cpp | 847 ++++++------------------ server/CMSWindowsPrimaryScreen.h | 106 ++- server/CPrimaryScreen.cpp | 10 +- server/CPrimaryScreen.h | 8 +- server/CServer.cpp | 78 ++- server/CServer.h | 5 +- server/CXWindowsPrimaryScreen.cpp | 8 +- server/CXWindowsPrimaryScreen.h | 1 + server/server.dsp | 8 +- synergy/IScreen.h | 4 +- synergy/IScreenEventHandler.h | 5 + synergy/ProtocolTypes.h | 17 + synergy/synergy.dsp | 8 + 33 files changed, 1210 insertions(+), 1661 deletions(-) rename {server => platform}/CSynergyHook.cpp (100%) rename {server => platform}/CSynergyHook.h (100%) create mode 100755 platform/IMSWindowsScreenEventHandler.h rename {server => platform}/makehook.dsp (100%) rename {server => platform}/synrgyhk.dsp (100%) diff --git a/client/CMSWindowsSecondaryScreen.cpp b/client/CMSWindowsSecondaryScreen.cpp index e49ec3d4..78b514f4 100644 --- a/client/CMSWindowsSecondaryScreen.cpp +++ b/client/CMSWindowsSecondaryScreen.cpp @@ -1,12 +1,8 @@ #include "CMSWindowsSecondaryScreen.h" -#include "IScreenReceiver.h" -#include "CClipboard.h" -#include "CMSWindowsClipboard.h" -#include "CMSWindowsScreenSaver.h" +#include "CMSWindowsScreen.h" #include "CPlatform.h" #include "XScreen.h" #include "CLock.h" -#include "CThread.h" #include "CLog.h" #include @@ -24,155 +20,18 @@ CMSWindowsSecondaryScreen::CMSWindowsSecondaryScreen( IScreenReceiver* receiver) : - m_receiver(receiver), - m_threadID(0), - m_lastThreadID(0), - m_desk(NULL), - m_deskName(), + m_is95Family(CPlatform::isWindows95Family()), m_window(NULL), - m_active(false), - m_nextClipboardWindow(NULL) + m_mask(0) { - assert(m_receiver != NULL); - - m_is95Family = CPlatform::isWindows95Family(); - - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + m_screen = new CMSWindowsScreen(receiver, this); } CMSWindowsSecondaryScreen::~CMSWindowsSecondaryScreen() { assert(m_window == NULL); -} -void -CMSWindowsSecondaryScreen::run() -{ - assert(m_window != NULL); - - // must call run() from same thread as open() - assert(m_threadID == GetCurrentThreadId()); - - // change our priority - CThread::getCurrentThread().setPriority(-7); - - // 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 -CMSWindowsSecondaryScreen::stop() -{ - exitMainLoop(); -} - -void -CMSWindowsSecondaryScreen::open() -{ - assert(m_window == NULL); - - try { - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // initialize the clipboards; assume primary has all clipboards - for (ClipboardID id = 0; id < kClipboardEnd; ++id) { - grabClipboard(id); - } - - // get keyboard state - updateKeys(); - updateModifiers(); - - // disable the screen saver - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // hide the cursor - m_active = true; - leave(); -} - -void -CMSWindowsSecondaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); -} - -void -CMSWindowsSecondaryScreen::enter(SInt32 x, SInt32 y, KeyModifierMask mask) -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - assert(m_active == false); - - log((CLOG_INFO "entering screen at %d,%d mask=%04x", x, y, mask)); - - syncDesktop(); - - // now active - m_active = true; - - // update our keyboard state to reflect the local state - updateKeys(); - updateModifiers(); - - // toggle modifiers that don't match the desired state - if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { - toggleKey(VK_CAPITAL, KeyModifierCapsLock); - } - if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { - toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); - } - if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { - toggleKey(VK_SCROLL, KeyModifierScrollLock); - } - - // warp to requested location - warpCursor(x, y); - - // show mouse - hideWindow(); -} - -void -CMSWindowsSecondaryScreen::leave() -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - assert(m_active == true); - - log((CLOG_INFO "leaving screen")); - - syncDesktop(); - - // hide mouse - showWindow(); - - // not active anymore - m_active = false; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); + delete m_screen; } void @@ -182,8 +41,7 @@ CMSWindowsSecondaryScreen::keyDown(KeyID key, KeyModifierMask mask) UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key press and the final // modifier state. @@ -223,8 +81,7 @@ CMSWindowsSecondaryScreen::keyRepeat(KeyID key, UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key repeat and the final // modifier state. @@ -244,8 +101,7 @@ CMSWindowsSecondaryScreen::keyUp(KeyID key, KeyModifierMask mask) UINT virtualKey; CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // get the sequence of keys to simulate key release and the final // modifier state. @@ -302,8 +158,7 @@ void CMSWindowsSecondaryScreen::mouseDown(ButtonID button) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // map button id to button flag DWORD flags = mapButton(button, true); @@ -318,8 +173,7 @@ void CMSWindowsSecondaryScreen::mouseUp(ButtonID button) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); // map button id to button flag DWORD flags = mapButton(button, false); @@ -334,8 +188,7 @@ void CMSWindowsSecondaryScreen::mouseMove(SInt32 x, SInt32 y) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); + m_screen->syncDesktop(); warpCursor(x, y); } @@ -343,61 +196,50 @@ void CMSWindowsSecondaryScreen::mouseWheel(SInt32 delta) { CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); - + m_screen->syncDesktop(); mouse_event(MOUSEEVENTF_WHEEL, 0, 0, delta, 0); } -void -CMSWindowsSecondaryScreen::setClipboard(ClipboardID /*id*/, - const IClipboard* src) +IScreen* +CMSWindowsSecondaryScreen::getScreen() const { - CLock lock(&m_mutex); - assert(m_window != NULL); - - CMSWindowsClipboard dst(m_window); - CClipboard::copy(&dst, src); + return m_screen; } void -CMSWindowsSecondaryScreen::grabClipboard(ClipboardID /*id*/) +CMSWindowsSecondaryScreen::onError() { - CLock lock(&m_mutex); - assert(m_window != NULL); + // ignore +} - CMSWindowsClipboard clipboard(m_window); - if (clipboard.open(0)) { - clipboard.close(); +void +CMSWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + +bool +CMSWindowsSecondaryScreen::onPreDispatch(const CEvent*) +{ + return false; +} + +bool +CMSWindowsSecondaryScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_ACTIVATEAPP: + if (msg.wParam == FALSE) { + // some other app activated. hide the hider window. + ShowWindow(m_window, SW_HIDE); + } + break; } -} -void -CMSWindowsSecondaryScreen::screenSaver(bool activate) -{ - if (activate) { - getScreenSaver()->activate(); - } - else { - getScreenSaver()->deactivate(); - } -} - -void -CMSWindowsSecondaryScreen::getMousePos(SInt32& x, SInt32& y) const -{ - CLock lock(&m_mutex); - assert(m_window != NULL); - syncDesktop(); - - getCursorPos(x, y); -} - -void -CMSWindowsSecondaryScreen::getShape( - SInt32& x, SInt32& y, SInt32& w, SInt32& h) const -{ - getScreenShape(x, y, w, h); + return false; } SInt32 @@ -407,153 +249,63 @@ CMSWindowsSecondaryScreen::getJumpZoneSize() const } void -CMSWindowsSecondaryScreen::getClipboard(ClipboardID /*id*/, - IClipboard* dst) const +CMSWindowsSecondaryScreen::postCreateWindow(HWND window) +{ + m_window = window; + if (!isActive()) { + showWindow(); + } +} + +void +CMSWindowsSecondaryScreen::preDestroyWindow(HWND) +{ + // do nothing +} + +void +CMSWindowsSecondaryScreen::onPreRun() { - CLock lock(&m_mutex); assert(m_window != NULL); - - CMSWindowsClipboard src(m_window); - CClipboard::copy(dst, &src); } -bool -CMSWindowsSecondaryScreen::onPreDispatch(const CEvent* event) +void +CMSWindowsSecondaryScreen::onPreOpen() { - assert(event != NULL); - - // forward to superclass - if (CMSWindowsScreen::onPreDispatch(event)) { - return true; - } - - // handle event - const MSG* msg = &event->m_msg; - switch (msg->message) { - case WM_TIMER: - // if current desktop is not the input desktop then switch to it - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else { - switchDesktop(desk); - } - } - } - return true; - } - - return false; + assert(m_window == NULL); } -bool -CMSWindowsSecondaryScreen::onEvent(CEvent* event) +void +CMSWindowsSecondaryScreen::onPreEnter() { - assert(event != NULL); - - const MSG& msg = event->msg; - switch (msg.message) { - case WM_QUERYENDSESSION: - if (m_is95Family) { - event->m_result = TRUE; - return true; - } - break; - - case WM_ENDSESSION: - if (m_is95Family) { - if (msg.wParam == TRUE && msg.lParam == 0) { - stop(); - } - return true; - } - break; - - case WM_PAINT: - ValidateRect(msg.hwnd, NULL); - return true; - - case WM_ACTIVATEAPP: - if (msg.wParam == FALSE) { - // some other app activated. hide the hider window. - ShowWindow(m_window, SW_HIDE); - } - break; - - case WM_DRAWCLIPBOARD: - log((CLOG_DEBUG "clipboard was taken")); - - // first pass it on - if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - - // now notify client that somebody changed the clipboard (unless - // we're now the owner, in which case it's because we took - // ownership, or now it's owned by nobody, which will happen if - // we owned it and switched desktops because we destroy our - // window to do that). - try { - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore. this can happen if we receive this event - // before we've fully started up. - } - return true; - - case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)msg.wParam) { - m_nextClipboardWindow = (HWND)msg.lParam; - } - else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - return true; - - case WM_DISPLAYCHANGE: - { - // screen resolution may have changed. get old shape. - SInt32 xOld, yOld, wOld, hOld; - getScreenShape(xOld, yOld, wOld, hOld); - - // update shape - updateScreenShape(); - m_multimon = isMultimon(); - - // collect new screen info - CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // do nothing if resolution hasn't changed - if (info.m_x != xOld || info.m_y != yOld || - info.m_w != wOld || info.m_h != hOld) { - // send new screen info - m_receiver->onInfoChanged(info); - } - - return true; - } - } - - return false; + assert(m_window != NULL); } -CString -CMSWindowsSecondaryScreen::getCurrentDesktopName() const +void +CMSWindowsSecondaryScreen::onPreLeave() { - return m_deskName; + assert(m_window != NULL); +} + +void +CMSWindowsSecondaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } +} + +void +CMSWindowsSecondaryScreen::destroyWindow() +{ + // release keys that are logically pressed + releaseKeys(); + + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; } void @@ -580,9 +332,9 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) { // move the mouse directly to target position on NT family or if // not using multiple monitors. - if (!m_multimon || !m_is95Family) { + if (m_screen->isMultimon() || !m_is95Family) { SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); + m_screen->getShape(x0, y0, w, h); mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE, (DWORD)((65535.99 * (x - x0)) / (w - 1)), (DWORD)((65535.99 * (y - y0)) / (h - 1)), @@ -653,259 +405,70 @@ CMSWindowsSecondaryScreen::warpCursor(SInt32 x, SInt32 y) } void -CMSWindowsSecondaryScreen::checkClipboard() +CMSWindowsSecondaryScreen::updateKeys() { - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore - } + // clear key state + memset(m_keys, 0, sizeof(m_keys)); + + // we only care about the modifier key states + m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); + m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); + m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); + m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); + m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); + m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); + m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); + m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); + m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); + m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); + m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); + m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); + m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); + m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); + m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); + + // update active modifier mask + m_mask = 0; + if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { + m_mask |= KeyModifierShift; } + if ((m_keys[VK_LCONTROL] & 0x80) != 0 || + (m_keys[VK_RCONTROL] & 0x80) != 0) { + m_mask |= KeyModifierControl; + } + if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { + m_mask |= KeyModifierAlt; + } + if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { + m_mask |= KeyModifierMeta; + } + if ((m_keys[VK_CAPITAL] & 0x01) != 0) { + m_mask |= KeyModifierCapsLock; + } + if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { + m_mask |= KeyModifierNumLock; + } + if ((m_keys[VK_SCROLL] & 0x01) != 0) { + m_mask |= KeyModifierScrollLock; + } + log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); } void -CMSWindowsPrimaryScreen::createWindow() +CMSWindowsSecondaryScreen::setToggleState(KeyModifierMask mask) { - // save thread id - m_threadID = GetCurrentThreadId(); - - // note if using multiple monitors - m_multimon = isMultimon(); - - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } + // toggle modifiers that don't match the desired state + if ((mask & KeyModifierCapsLock) != (m_mask & KeyModifierCapsLock)) { + toggleKey(VK_CAPITAL, KeyModifierCapsLock); } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } + if ((mask & KeyModifierNumLock) != (m_mask & KeyModifierNumLock)) { + toggleKey(VK_NUMLOCK | 0x100, KeyModifierNumLock); } - - // poll input desktop to see if it changes (onPreDispatch() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); + if ((mask & KeyModifierScrollLock) != (m_mask & KeyModifierScrollLock)) { + toggleKey(VK_SCROLL, KeyModifierScrollLock); } } -void -CMSWindowsPrimaryScreen::destroyWindow() -{ - // remove timer - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - - // release keys that are logically pressed - releaseKeys(); - - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - // clear thread id - m_threadID = 0; - - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsSecondaryScreen::installScreenSaver() -{ - getScreenSaver()->disable(); -} - -void -CMSWindowsSecondaryScreen::uninstallScreenSaver() -{ - getScreenSaver()->enable(); -} - -bool -CMSWindowsSecondaryScreen::openDesktop() -{ - CLock lock(&m_mutex); - - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); - - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, 1, 1, NULL, NULL, - getInstance(), - NULL); - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - return true; -} - -void -CMSWindowsSecondaryScreen::closeDesktop() -{ - CLock lock(&m_mutex); - - if (m_window != NULL) { - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // destroy window - DestroyWindow(m_window); - m_window = NULL; - } -} - -bool -CMSWindowsSecondaryScreen::switchDesktop(HDESK desk) -{ - CLock lock(&m_mutex); - - bool ownClipboard = false; - if (m_window != NULL) { - // note if we own the clipboard - ownClipboard = (m_clipboardOwner == m_window); - - // remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // done with desktop - if (m_desk != NULL) { - CloseDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; - } - - // if no new desktop then we're done - if (desk == NULL) { - log((CLOG_INFO "disconnecting desktop")); - return true; - } - - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (SetThreadDesktop(desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // initialize clipboard owner to current owner. we don't want - // to take ownership of the clipboard just by starting up. - m_clipboardOwner = GetClipboardOwner(); - - // create the cursor hiding window. this window is used to hide the - // cursor when it's not on the screen. the window is hidden as soon - // as the cursor enters the screen or the display's real cursor is - // moved. - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), "Synergy", - WS_POPUP, - 0, 0, 1, 1, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // if we owned the desktop then set the clipboard owner - if (ownClipboard) { - m_clipboardOwner = GetClipboardOwner(); - } - - // save new desktop - m_desk = desk; - m_deskName = getDesktopName(m_desk); - log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); - - // get desktop up to date - if (!m_active) { - showWindow(); - } - - return true; -} - -void -CMSWindowsSecondaryScreen::syncDesktop() const -{ - // note -- mutex must be locked on entry - - // change calling thread's desktop - if (!m_is95Family) { - if (SetThreadDesktop(m_desk) == 0) { - log((CLOG_WARN "failed to set desktop: %d", GetLastError())); - } - } - - // attach input queues if not already attached. this has a habit - // of sucking up more and more CPU each time it's called (even if - // the threads are already attached). since we only expect one - // thread to call this more than once we can save just the last - // the attached thread. - DWORD threadID = GetCurrentThreadId(); - if (threadID != m_lastThreadID && threadID != m_threadID) { - m_lastThreadID = threadID; - AttachThreadInput(threadID, m_threadID, TRUE); - } -} - -bool -CMSWindowsSecondaryScreen::isMultimon() const -{ - SInt32 x0, y0, w, h; - getScreenShape(x0, y0, w, h); - return (w != GetSystemMetrics(SM_CXSCREEN) || - h != GetSystemMetrics(SM_CYSCREEN)); -} - // these tables map KeyID (a X windows KeySym) to virtual key codes. // if the key is an extended key then the entry is the virtual key // code | 0x100. keys that map to normal characters have a 0 entry @@ -1768,7 +1331,7 @@ CMSWindowsSecondaryScreen::releaseKeys() { CLock lock(&m_mutex); - syncDesktop(); + m_screen->syncDesktop(); // release left/right modifier keys first. if the platform doesn't // support them then they won't be set and the non-side-distinuishing @@ -1814,60 +1377,6 @@ CMSWindowsSecondaryScreen::releaseKeys() } } -void -CMSWindowsSecondaryScreen::updateKeys() -{ - // clear key state - memset(m_keys, 0, sizeof(m_keys)); - - // we only care about the modifier key states - m_keys[VK_LSHIFT] = static_cast(GetKeyState(VK_LSHIFT)); - m_keys[VK_RSHIFT] = static_cast(GetKeyState(VK_RSHIFT)); - m_keys[VK_SHIFT] = static_cast(GetKeyState(VK_SHIFT)); - m_keys[VK_LCONTROL] = static_cast(GetKeyState(VK_LCONTROL)); - m_keys[VK_RCONTROL] = static_cast(GetKeyState(VK_RCONTROL)); - m_keys[VK_CONTROL] = static_cast(GetKeyState(VK_CONTROL)); - m_keys[VK_LMENU] = static_cast(GetKeyState(VK_LMENU)); - m_keys[VK_RMENU] = static_cast(GetKeyState(VK_RMENU)); - m_keys[VK_MENU] = static_cast(GetKeyState(VK_MENU)); - m_keys[VK_LWIN] = static_cast(GetKeyState(VK_LWIN)); - m_keys[VK_RWIN] = static_cast(GetKeyState(VK_RWIN)); - m_keys[VK_APPS] = static_cast(GetKeyState(VK_APPS)); - m_keys[VK_CAPITAL] = static_cast(GetKeyState(VK_CAPITAL)); - m_keys[VK_NUMLOCK] = static_cast(GetKeyState(VK_NUMLOCK)); - m_keys[VK_SCROLL] = static_cast(GetKeyState(VK_SCROLL)); -} - -void -CMSWindowsSecondaryScreen::updateModifiers() -{ - // update active modifier mask - m_mask = 0; - if ((m_keys[VK_LSHIFT] & 0x80) != 0 || (m_keys[VK_RSHIFT] & 0x80) != 0) { - m_mask |= KeyModifierShift; - } - if ((m_keys[VK_LCONTROL] & 0x80) != 0 || - (m_keys[VK_RCONTROL] & 0x80) != 0) { - m_mask |= KeyModifierControl; - } - if ((m_keys[VK_LMENU] & 0x80) != 0 || (m_keys[VK_RMENU] & 0x80) != 0) { - m_mask |= KeyModifierAlt; - } - if ((m_keys[VK_LWIN] & 0x80) != 0 || (m_keys[VK_RWIN] & 0x80) != 0) { - m_mask |= KeyModifierMeta; - } - if ((m_keys[VK_CAPITAL] & 0x01) != 0) { - m_mask |= KeyModifierCapsLock; - } - if ((m_keys[VK_NUMLOCK] & 0x01) != 0) { - m_mask |= KeyModifierNumLock; - } - if ((m_keys[VK_SCROLL] & 0x01) != 0) { - m_mask |= KeyModifierScrollLock; - } - log((CLOG_DEBUG2 "modifiers on update: 0x%04x", m_mask)); -} - void CMSWindowsSecondaryScreen::toggleKey(UINT virtualKey, KeyModifierMask mask) { diff --git a/client/CMSWindowsSecondaryScreen.h b/client/CMSWindowsSecondaryScreen.h index c6f9c5e3..e9b956c7 100644 --- a/client/CMSWindowsSecondaryScreen.h +++ b/client/CMSWindowsSecondaryScreen.h @@ -7,28 +7,22 @@ #define _WIN32_WINNT 0x401 #endif -#include "CMSWindowsScreen.h" -#include "ISecondaryScreen.h" +#include "CSecondaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" #include "CMutex.h" #include "CString.h" #include "stdvector.h" +class CMSWindowsScreen; class IScreenReceiver; -class CMSWindowsSecondaryScreen : public CMSWindowsScreen, - public ISecondaryScreen { +class CMSWindowsSecondaryScreen : + public CSecondaryScreen, public IMSWindowsScreenEventHandler { public: CMSWindowsSecondaryScreen(IScreenReceiver*); virtual ~CMSWindowsSecondaryScreen(); - // 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); @@ -36,19 +30,30 @@ public: virtual void mouseUp(ButtonID); virtual void mouseMove(SInt32 xAbsolute, SInt32 yAbsolute); 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: - // CMSWindowsScreen overrides + // IMSWindowsScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual CString getCurrentDesktopName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + +protected: + // CSecondaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + 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 }; @@ -60,26 +65,6 @@ private: }; typedef std::vector Keystrokes; - void showWindow(); - void hideWindow(); - - // warp the mouse to the specified position - void warpCursor(SInt32 x, SInt32 y); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - // open/close desktop (for windows 95/98/me) bool openDesktop(); void closeDesktop(); @@ -87,9 +72,6 @@ private: // make desk the thread desktop (for windows NT/2000/XP) bool switchDesktop(HDESK desk); - // get calling thread to use the input desktop - void syncDesktop() const; - // returns true iff there appear to be multiple monitors bool isMultimon() const; @@ -100,8 +82,6 @@ private: void doKeystrokes(const Keystrokes&, SInt32 count); void releaseKeys(); - void updateKeys(); - void updateModifiers(); void toggleKey(UINT virtualKey, KeyModifierMask mask); UINT virtualKeyToScanCode(UINT& virtualKey); bool isExtendedKey(UINT virtualKey); @@ -109,37 +89,14 @@ private: private: CMutex m_mutex; - IScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; // true if windows 95/98/me bool m_is95Family; - // true if system appears to have multiple monitors - bool m_multimon; - - // the main loop's thread id - DWORD m_threadID; - - // the timer used to check for desktop switching - UINT m_timer; - - // the thread id of the last attached thread - mutable DWORD m_lastThreadID; - - // the current desk and it's name - HDESK m_desk; - CString m_deskName; - - // our window (for getting clipboard changes) + // our window HWND m_window; - // m_active is true if this screen has been entered - bool m_active; - - // clipboard stuff - HWND m_nextClipboardWindow; - HWND m_clipboardOwner; - // virtual key states BYTE m_keys[256]; diff --git a/client/CSecondaryScreen.cpp b/client/CSecondaryScreen.cpp index 12358b2a..a19bc4e4 100644 --- a/client/CSecondaryScreen.cpp +++ b/client/CSecondaryScreen.cpp @@ -74,7 +74,7 @@ CSecondaryScreen::open() updateKeys(); // disable the screen saver - getScreen()->openScreenSaver(false); + getScreen()->openScreensaver(false); // subclass hook onPostOpen(); @@ -93,7 +93,7 @@ void CSecondaryScreen::close() { onPreClose(); - getScreen()->closeScreenSaver(); + getScreen()->closeScreensaver(); destroyWindow(); getScreen()->close(); onPostClose(); @@ -182,12 +182,6 @@ CSecondaryScreen::getClipboard(ClipboardID id, getScreen()->getClipboard(id, clipboard); } -SInt32 -CSecondaryScreen::getJumpZoneSize() const -{ - return 0; -} - void CSecondaryScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { diff --git a/client/CSecondaryScreen.h b/client/CSecondaryScreen.h index 736fc4d9..4db665f1 100644 --- a/client/CSecondaryScreen.h +++ b/client/CSecondaryScreen.h @@ -78,8 +78,8 @@ public: 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; + // causes the cursor to jump to another screen. + virtual SInt32 getJumpZoneSize() const = 0; // get the shape (position of upper-left corner and size) of the // screen diff --git a/client/CXWindowsSecondaryScreen.cpp b/client/CXWindowsSecondaryScreen.cpp index 1207cd8f..d8e5ab66 100644 --- a/client/CXWindowsSecondaryScreen.cpp +++ b/client/CXWindowsSecondaryScreen.cpp @@ -145,12 +145,6 @@ CXWindowsSecondaryScreen::getScreen() const return m_screen; } -void -CXWindowsSecondaryScreen::onScreensaver(bool) -{ - // ignore -} - void CXWindowsSecondaryScreen::onError() { @@ -158,6 +152,12 @@ CXWindowsSecondaryScreen::onError() // FIXME -- forward this? to whom? } +void +CXWindowsSecondaryScreen::onScreensaver(bool) +{ + // ignore +} + bool CXWindowsSecondaryScreen::onPreDispatch(const CEvent*) { @@ -184,6 +184,12 @@ CXWindowsSecondaryScreen::onEvent(CEvent* event) } } +SInt32 +CXWindowsSecondaryScreen::getJumpZoneSize() const +{ + return 0; +} + void CXWindowsSecondaryScreen::onPreRun() { @@ -260,7 +266,7 @@ CXWindowsSecondaryScreen::createWindow() XTestGrabControl(display, True); } - // tell our superclass about the window + // tell generic screen about the window m_screen->setWindow(m_window); } @@ -275,15 +281,19 @@ CXWindowsSecondaryScreen::destroyWindow() // 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); } + + // destroy window + if (m_window != None) { + m_screen->setWindow(None); + CDisplayLock display(m_screen); + if (display != NULL) { + XDestroyWindow(display, m_window); + } + m_window = None; + } } void diff --git a/client/CXWindowsSecondaryScreen.h b/client/CXWindowsSecondaryScreen.h index d2797d65..6237996b 100644 --- a/client/CXWindowsSecondaryScreen.h +++ b/client/CXWindowsSecondaryScreen.h @@ -35,6 +35,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual SInt32 getJumpZoneSize() const; protected: // CSecondaryScreen overrides diff --git a/client/client.dsp b/client/client.dsp index 7311f204..d48c3aaf 100644 --- a/client/client.dsp +++ b/client/client.dsp @@ -110,6 +110,10 @@ SOURCE=.\CMSWindowsSecondaryScreen.cpp # End Source File # Begin Source File +SOURCE=.\CSecondaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CServerProxy.cpp # End Source File # End Group @@ -126,11 +130,11 @@ SOURCE=.\CMSWindowsSecondaryScreen.h # End Source File # Begin Source File -SOURCE=.\CServerProxy.h +SOURCE=.\CSecondaryScreen.h # End Source File # Begin Source File -SOURCE=.\ISecondaryScreen.h +SOURCE=.\CServerProxy.h # End Source File # Begin Source File diff --git a/platform/CMSWindowsScreen.cpp b/platform/CMSWindowsScreen.cpp index 3a6afd57..9cc1a096 100644 --- a/platform/CMSWindowsScreen.cpp +++ b/platform/CMSWindowsScreen.cpp @@ -1,5 +1,10 @@ #include "CMSWindowsScreen.h" +#include "CMSWindowsClipboard.h" #include "CMSWindowsScreenSaver.h" +#include "CClipboard.h" +#include "IMSWindowsScreenEventHandler.h" +#include "IScreenReceiver.h" +#include "XSynergy.h" #include "CThread.h" #include "CLock.h" #include "TMethodJob.h" @@ -8,7 +13,7 @@ #include // -// add backwards compatible multihead support (suppress bogus warning) +// add backwards compatible multihead support (and suppress bogus warning) // #pragma warning(push) #pragma warning(disable: 4706) // assignment within conditional @@ -23,21 +28,46 @@ HINSTANCE CMSWindowsScreen::s_instance = NULL; CMSWindowsScreen* CMSWindowsScreen::s_screen = NULL; -CMSWindowsScreen::CMSWindowsScreen() : - m_class(0), +CMSWindowsScreen::CMSWindowsScreen(IScreenReceiver* receiver, + IMSWindowsScreenEventHandler* eventHandler) : + m_receiver(receiver), + m_eventHandler(eventHandler), + m_class(NULL), + m_icon(NULL), m_cursor(NULL), + m_window(NULL), m_x(0), m_y(0), m_w(0), m_h(0), - m_thread(0), - m_screenSaver(NULL) + m_multimon(false), + m_threadID(0), + m_lastThreadID(0), + m_nextClipboardWindow(NULL), + m_clipboardOwner(NULL), + m_timer(0), + m_desk(NULL), + m_deskName(), + m_hookLibrary(NULL), + m_installScreensaver(NULL), + m_uninstallScreensaver(NULL), + m_screensaver(NULL), + m_screensaverNotify(false) { - assert(s_screen == NULL); + assert(s_screen == NULL); + assert(m_receiver != NULL); + assert(m_eventHandler != NULL); + s_screen = this; + + // make sure this thread has a message queue + MSG dummy; + PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); } CMSWindowsScreen::~CMSWindowsScreen() { - assert(m_class == 0); + assert(s_screen != NULL); + assert(m_class == 0); + s_screen = NULL; } @@ -47,11 +77,110 @@ CMSWindowsScreen::init(HINSTANCE instance) s_instance = instance; } +HWND +CMSWindowsScreen::openDesktop() +{ + // save thread id + m_threadID = GetCurrentThreadId(); + + // get the input desktop and switch to it + if (!switchDesktop(openInputDesktop())) { + return NULL; + } + + // poll input desktop to see if it changes (onPreDispatch() + // handles WM_TIMER) + m_timer = 0; + if (!m_is95Family) { + m_timer = SetTimer(NULL, 0, 200, NULL); + } + + return m_window; +} + +void +CMSWindowsScreen::closeDesktop() +{ + // remove timer + if (m_timer != 0) { + KillTimer(NULL, m_timer); + } + + // disconnect from desktop + switchDesktop(NULL); + + // clear thread id + m_threadID = 0; + + assert(m_window == NULL); + assert(m_desk == NULL); +} + +bool +CMSWindowsScreen::isMultimon() const +{ + return m_multimon; +} + +HINSTANCE +CMSWindowsScreen::getInstance() +{ + return s_instance; +} + +void +CMSWindowsScreen::open() +{ + assert(s_instance != NULL); + assert(m_class == 0); + + log((CLOG_DEBUG "opening display")); + + // create the transparent cursor + createBlankCursor(); + + // register a window class + WNDCLASSEX classInfo; + classInfo.cbSize = sizeof(classInfo); + classInfo.style = CS_DBLCLKS | CS_NOCLOSE; + classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; + classInfo.cbClsExtra = 0; + classInfo.cbWndExtra = 0; + classInfo.hInstance = s_instance; + classInfo.hIcon = NULL; + classInfo.hCursor = m_cursor; + classInfo.hbrBackground = NULL; + classInfo.lpszMenuName = NULL; + classInfo.lpszClassName = "Synergy"; + classInfo.hIconSm = NULL; + m_class = RegisterClassEx(&classInfo); + + // get screen shape + updateScreenShape(); + + // initialize the screen saver + m_screensaver = new CMSWindowsScreenSaver(); + + // load the hook library and get the screen saver functions + m_hookLibrary = LoadLibrary("synrgyhk"); + if (m_hookLibrary != NULL) { + m_installScreensaver = (InstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "installScreenSaver"); + m_uninstallScreensaver = (UninstallScreenSaverFunc)GetProcAddress( + m_hookLibrary, "uninstallScreenSaver"); + if (m_installScreensaver == NULL || m_uninstallScreensaver == NULL) { + // disable if either install or uninstall is unavailable + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + } + } +} + void CMSWindowsScreen::mainLoop() { - // save thread id for posting quit message - m_thread = GetCurrentThreadId(); + // must call mainLoop() from same thread as openDesktop() + assert(m_threadID == GetCurrentThreadId()); // event loop CEvent event; @@ -77,55 +206,25 @@ CMSWindowsScreen::mainLoop() void CMSWindowsScreen::exitMainLoop() { - PostThreadMessage(m_thread, WM_QUIT, 0, 0); -} - -bool -CMSWindowsScreen::onPreDispatch(const CEvent*) -{ - return false; + PostThreadMessage(m_threadID, WM_QUIT, 0, 0); } void -CMSWindowsScreen::openDisplay() +CMSWindowsScreen::close() { assert(s_instance != NULL); - assert(m_class == 0); - // create the transparent cursor - createBlankCursor(); - - // register a window class - WNDCLASSEX classInfo; - classInfo.cbSize = sizeof(classInfo); - classInfo.style = CS_DBLCLKS | CS_NOCLOSE; - classInfo.lpfnWndProc = &CMSWindowsScreen::wndProc; - classInfo.cbClsExtra = 0; - classInfo.cbWndExtra = 0; - classInfo.hInstance = s_instance; - classInfo.hIcon = NULL; - classInfo.hCursor = m_cursor; - classInfo.hbrBackground = NULL; - classInfo.lpszMenuName = NULL; - classInfo.lpszClassName = "Synergy"; - classInfo.hIconSm = NULL; - m_class = RegisterClassEx(&classInfo); - - // get screen shape - updateScreenShape(); - - // initialize the screen saver - m_screenSaver = new CMSWindowsScreenSaver(); -} - -void -CMSWindowsScreen::closeDisplay() -{ - assert(s_instance != NULL); + // done with hook library + if (m_hookLibrary != NULL) { + FreeLibrary(m_hookLibrary); + m_installScreensaver = NULL; + m_uninstallScreensaver = NULL; + m_hookLibrary = NULL; + } // done with screen saver - delete m_screenSaver; - m_screenSaver = NULL; + delete m_screensaver; + m_screensaver = NULL; // unregister the window class if (m_class != 0) { @@ -142,30 +241,130 @@ CMSWindowsScreen::closeDisplay() log((CLOG_DEBUG "closed display")); } -HINSTANCE -CMSWindowsScreen::getInstance() +bool +CMSWindowsScreen::setClipboard(ClipboardID, const IClipboard* src) { - return s_instance; -} - -ATOM -CMSWindowsScreen::getClass() const -{ - return m_class; + CMSWindowsClipboard dst(m_window); + if (src != NULL) { + // save clipboard data + return CClipboard::copy(&dst, src); + } + else { + // assert clipboard ownership + if (!dst.open(0)) { + return false; + } + dst.empty(); + dst.close(); + return true; + } } void -CMSWindowsScreen::updateScreenShape() +CMSWindowsScreen::checkClipboards() { - m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); - m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); - m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); - m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); - log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + // if we think we own the clipboard but we don't then somebody + // grabbed the clipboard on this screen without us knowing. + // tell the server that this screen grabbed the clipboard. + // + // this works around bugs in the clipboard viewer chain. + // sometimes NT will simply never send WM_DRAWCLIPBOARD + // messages for no apparent reason and rebooting fixes the + // problem. since we don't want a broken clipboard until the + // next reboot we do this double check. clipboard ownership + // won't be reflected on other screens until we leave but at + // least the clipboard itself will work. + HWND clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != clipboardOwner) { + try { + m_clipboardOwner = clipboardOwner; + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore + } + } } void -CMSWindowsScreen::getScreenShape(SInt32& x, SInt32& y, +CMSWindowsScreen::openScreensaver(bool notify) +{ + assert(m_screensaver != NULL); + + m_screensaverNotify = notify; + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + else { + m_screensaver->disable(); + } +} + +void +CMSWindowsScreen::closeScreensaver() +{ + if (m_screensaver != NULL) { + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + else { + m_screensaver->enable(); + } + } + m_screensaverNotify = false; +} + +void +CMSWindowsScreen::screensaver(bool activate) +{ + assert(m_screensaver != NULL); + if (activate) { + m_screensaver->activate(); + } + else { + m_screensaver->deactivate(); + } +} + +void +CMSWindowsScreen::syncDesktop() +{ + // change calling thread's desktop + if (!m_is95Family) { + if (SetThreadDesktop(m_desk) == 0) { + log((CLOG_WARN "failed to set desktop: %d", GetLastError())); + } + } + + // attach input queues if not already attached. this has a habit + // of sucking up more and more CPU each time it's called (even if + // the threads are already attached). since we only expect one + // thread to call this more than once we can save just the last + // the attached thread. + DWORD threadID = GetCurrentThreadId(); + if (threadID != m_lastThreadID && threadID != m_threadID) { + m_lastThreadID = threadID; + AttachThreadInput(threadID, m_threadID, TRUE); + } +} + +bool +CMSWindowsScreen::getClipboard(ClipboardID, IClipboard* dst) const +{ + CMSWindowsClipboard src(m_window); + CClipboard::copy(dst, &src); + return true; +} + +void +CMSWindowsScreen::getShape(SInt32& x, SInt32& y, SInt32& w, SInt32& h) const { assert(m_class != 0); @@ -196,10 +395,153 @@ CMSWindowsScreen::getCursorCenter(SInt32& x, SInt32& y) const y = GetSystemMetrics(SM_CYSCREEN) >> 1; } -HCURSOR -CMSWindowsScreen::getBlankCursor() const +void +CMSWindowsScreen::updateScreenShape() { - return m_cursor; + // get shape + m_x = GetSystemMetrics(SM_XVIRTUALSCREEN); + m_y = GetSystemMetrics(SM_YVIRTUALSCREEN); + m_w = GetSystemMetrics(SM_CXVIRTUALSCREEN); + m_h = GetSystemMetrics(SM_CYVIRTUALSCREEN); + log((CLOG_INFO "screen shape: %d,%d %dx%d", m_x, m_y, m_w, m_h)); + + // check for multiple monitors + m_multimon = (m_w != GetSystemMetrics(SM_CXSCREEN) || + m_h != GetSystemMetrics(SM_CYSCREEN)); +} + +bool +CMSWindowsScreen::onPreDispatch(const CEvent* event) +{ + // handle event + const MSG* msg = &event->m_msg; + switch (msg->message) { + case SYNERGY_MSG_SCREEN_SAVER: + if (msg->wParam != 0) { + if (m_screensaver->checkStarted(msg->message, FALSE, 0)) { + m_eventHandler->onScreensaver(true); + } + } + else { + m_eventHandler->onScreensaver(false); + } + return true; + + case WM_TIMER: + // if current desktop is not the input desktop then switch to it. + // windows 95 doesn't support multiple desktops so don't bother + // to check under it. + if (!m_is95Family) { + HDESK desk = openInputDesktop(); + if (desk != NULL) { + if (isCurrentDesktop(desk)) { + CloseDesktop(desk); + } + else { + switchDesktop(desk); + } + } + } + return true; + } + + return m_eventHandler->onPreDispatch(event); +} + +bool +CMSWindowsScreen::onEvent(CEvent* event) +{ + assert(event != NULL); + + const MSG& msg = event->m_msg; + switch (msg.message) { + case WM_QUERYENDSESSION: + if (m_is95Family) { + event->m_result = TRUE; + return true; + } + break; + + case WM_ENDSESSION: + if (m_is95Family) { + if (msg.wParam == TRUE && msg.lParam == 0) { + exitMainLoop(); + } + return true; + } + break; + + case WM_PAINT: + ValidateRect(msg.hwnd, NULL); + return true; + + case WM_DRAWCLIPBOARD: + log((CLOG_DEBUG "clipboard was taken")); + + // first pass it on + if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + + // now notify client that somebody changed the clipboard (unless + // we're now the owner, in which case it's because we took + // ownership, or now it's owned by nobody, which will happen if + // we owned it and switched desktops because we destroy our + // window to do that). + try { + m_clipboardOwner = GetClipboardOwner(); + if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { + m_receiver->onGrabClipboard(kClipboardClipboard); + m_receiver->onGrabClipboard(kClipboardSelection); + } + } + catch (XBadClient&) { + // ignore. this can happen if we receive this event + // before we've fully started up. + } + return true; + + case WM_CHANGECBCHAIN: + if (m_nextClipboardWindow == (HWND)msg.wParam) { + m_nextClipboardWindow = (HWND)msg.lParam; + } + else if (m_nextClipboardWindow != NULL) { + SendMessage(m_nextClipboardWindow, + msg.message, msg.wParam, msg.lParam); + } + return true; + + case WM_DISPLAYCHANGE: + { + // screen resolution may have changed. get old shape. + SInt32 xOld, yOld, wOld, hOld; + getShape(xOld, yOld, wOld, hOld); + + // update shape + updateScreenShape(); + + // collect new screen info + CClientInfo info; + getShape(info.m_x, info.m_y, info.m_w, info.m_h); + getCursorPos(info.m_mx, info.m_my); + info.m_zoneSize = m_eventHandler->getJumpZoneSize(); + + // do nothing if resolution hasn't changed + if (info.m_x != xOld || info.m_y != yOld || + info.m_w != wOld || info.m_h != hOld) { + // forward event + m_eventHandler->onEvent(event); + + // send new screen info + m_receiver->onInfoChanged(info); + } + + return true; + } + } + + return m_eventHandler->onEvent(event); } void @@ -217,13 +559,121 @@ CMSWindowsScreen::createBlankCursor() delete[] cursorAND; } +bool +CMSWindowsScreen::switchDesktop(HDESK desk) +{ + // did we own the clipboard? + bool ownClipboard = (m_clipboardOwner == m_window && m_window != NULL); + + // destroy old window + if (m_window != NULL) { + // first remove clipboard snooper + ChangeClipboardChain(m_window, m_nextClipboardWindow); + m_nextClipboardWindow = NULL; + + // we no longer own the clipboard + if (ownClipboard) { + m_clipboardOwner = NULL; + } + + // let client clean up before we destroy the window + m_eventHandler->preDestroyWindow(m_window); + + // now destroy window + DestroyWindow(m_window); + m_window = NULL; + + // done with desk + if (!m_is95Family) { + CloseDesktop(m_desk); + } + m_desk = NULL; + m_deskName = ""; + } + + // if no new desktop then we're done + if (desk == NULL) { + log((CLOG_INFO "disconnecting desktop")); + return true; + } + + // uninstall screen saver hooks + if (m_screensaverNotify) { + if (m_uninstallScreensaver != NULL) { + m_uninstallScreensaver(); + } + } + + // set the desktop. can only do this when there are no windows + // and hooks on the current desktop owned by this thread. + if (SetThreadDesktop(desk) == 0) { + log((CLOG_ERR "failed to set desktop: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // create the window + m_window = CreateWindowEx(WS_EX_TOPMOST | + WS_EX_TRANSPARENT | + WS_EX_TOOLWINDOW, + (LPCTSTR)m_class, + "Synergy", + WS_POPUP, + 0, 0, 1, 1, + NULL, NULL, + getInstance(), + NULL); + if (m_window == NULL) { + log((CLOG_ERR "failed to create window: %d", GetLastError())); + if (!m_is95Family) { + CloseDesktop(desk); + } + return false; + } + + // reinstall screen saver hooks + if (m_screensaverNotify) { + if (m_installScreensaver != NULL) { + m_installScreensaver(); + } + } + + // install our clipboard snooper + m_nextClipboardWindow = SetClipboardViewer(m_window); + + // reassert clipboard ownership + if (ownClipboard) { + // FIXME -- take clipboard ownership, but we should also set + // the clipboard data. + } + m_clipboardOwner = GetClipboardOwner(); + + // save new desktop + m_desk = desk; + m_deskName = getDesktopName(m_desk); + log((CLOG_INFO "switched to desktop \"%s\"", m_deskName.c_str())); + + // let client prepare the window + m_eventHandler->postCreateWindow(m_window); + + return true; +} + HDESK CMSWindowsScreen::openInputDesktop() const { - return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, + if (m_is95Family) { + // there's only one desktop on windows 95 et al. + return GetThreadDesktop(GetCurrentThreadId()); + } + else { + return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE, DESKTOP_CREATEWINDOW | DESKTOP_HOOKCONTROL | GENERIC_WRITE); + } } CString @@ -232,6 +682,9 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const if (desk == NULL) { return CString(); } + else if (m_is95Family) { + return "desktop"; + } else { DWORD size; GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size); @@ -246,14 +699,7 @@ CMSWindowsScreen::getDesktopName(HDESK desk) const bool CMSWindowsScreen::isCurrentDesktop(HDESK desk) const { - return CStringUtil::CaselessCmp::equal(getDesktopName(desk), - getCurrentDesktopName()); -} - -CMSWindowsScreenSaver* -CMSWindowsScreen::getScreenSaver() const -{ - return m_screenSaver; + return CStringUtil::CaselessCmp::equal(getDesktopName(desk), m_deskName); } LRESULT CALLBACK diff --git a/platform/CMSWindowsScreen.h b/platform/CMSWindowsScreen.h index c45b258a..9a2f7c92 100644 --- a/platform/CMSWindowsScreen.h +++ b/platform/CMSWindowsScreen.h @@ -1,7 +1,8 @@ #ifndef CMSWINDOWSSCREEN_H #define CMSWINDOWSSCREEN_H -#include "IClipboard.h" +#include "IScreen.h" +#include "CSynergyHook.h" #include "CMutex.h" #include "CString.h" #define WIN32_LEAN_AND_MEAN @@ -16,52 +17,69 @@ public: LRESULT m_result; }; -class CMSWindowsScreen { +class IScreenReceiver; +class IMSWindowsScreenEventHandler; + +class CMSWindowsScreen : public IScreen { public: - CMSWindowsScreen(); + CMSWindowsScreen(IScreenReceiver*, IMSWindowsScreenEventHandler*); virtual ~CMSWindowsScreen(); // manipulators static void init(HINSTANCE); + // open the desktop and create and return the window. returns NULL + // on failure. + HWND openDesktop(); + + // close the window and desktop + void closeDesktop(); + // accessors + // returns true iff the system appears to have multiple monitors + bool isMultimon() const; + // get the application instance handle static HINSTANCE getInstance(); -protected: - // runs an event loop and returns when exitMainLoop() is called + // IScreen overrides + // note -- this class expects the hook DLL to have been loaded + // and initialized before open() is called. + void open(); void mainLoop(); - - // force mainLoop() to return 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; - // 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 registered window class atom - ATOM getClass() const; - +private: // update screen size cache void updateScreenShape(); - // get the size of the screen - void getScreenShape(SInt32& x, SInt32& y, - SInt32& width, SInt32& height) const; + // internal pre-dispatch event processing + bool onPreDispatch(const CEvent* event); - // get the current cursor position - void getCursorPos(SInt32& x, SInt32& y) const; + // internal (post-dispatch) event processing + bool onEvent(CEvent* event); - // get the cursor center position - void getCursorCenter(SInt32& x, SInt32& y) const; + // create the transparent cursor + void createBlankCursor(); + + // switch to the given desktop. this destroys the window and unhooks + // all hooks, switches the desktop, then creates the window and rehooks + // all hooks (because you can't switch the thread's desktop if it has + // any windows or hooks). + bool switchDesktop(HDESK desk); // get the input desktop. caller must CloseDesktop() the result. // do not call under windows 95/98/me. @@ -74,38 +92,56 @@ protected: // windows 95/98/me. bool isCurrentDesktop(HDESK desk) const; - // get the screen saver object - CMSWindowsScreenSaver* - 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); - - // called by mainLoop(). iff the event was handled return true and - // store the result, if any, in m_result, which defaults to zero. - virtual bool onEvent(CEvent* event) = 0; - - // called by isCurrentDesktop() to get the current desktop name - virtual CString getCurrentDesktopName() const = 0; - -private: - // create the transparent cursor - void createBlankCursor(); - // our window proc static LRESULT CALLBACK wndProc(HWND, UINT, WPARAM, LPARAM); private: static HINSTANCE s_instance; + + IScreenReceiver* m_receiver; + IMSWindowsScreenEventHandler* m_eventHandler; + ATOM m_class; HICON m_icon; HCURSOR m_cursor; + + // true if windows 95/98/me + bool m_is95Family; + + // our window + HWND m_window; + + // screen shape SInt32 m_x, m_y; SInt32 m_w, m_h; - DWORD m_thread; - CMSWindowsScreenSaver* m_screenSaver; + + // true if system appears to have multiple monitors + bool m_multimon; + + // the main loop's thread id + DWORD m_threadID; + + // the thread id of the last attached thread + DWORD m_lastThreadID; + + // clipboard stuff + HWND m_nextClipboardWindow; + HWND m_clipboardOwner; + + // the timer used to check for desktop switching + UINT m_timer; + + // the current desk and it's name + HDESK m_desk; + CString m_deskName; + + // screen saver stuff + HINSTANCE m_hookLibrary; + InstallScreenSaverFunc m_installScreensaver; + UninstallScreenSaverFunc m_uninstallScreensaver; + CMSWindowsScreenSaver* m_screensaver; + bool m_screensaverNotify; + static CMSWindowsScreen* s_screen; }; diff --git a/server/CSynergyHook.cpp b/platform/CSynergyHook.cpp similarity index 100% rename from server/CSynergyHook.cpp rename to platform/CSynergyHook.cpp diff --git a/server/CSynergyHook.h b/platform/CSynergyHook.h similarity index 100% rename from server/CSynergyHook.h rename to platform/CSynergyHook.h diff --git a/platform/CXWindowsScreen.cpp b/platform/CXWindowsScreen.cpp index e9b46721..ed573cbf 100644 --- a/platform/CXWindowsScreen.cpp +++ b/platform/CXWindowsScreen.cpp @@ -10,8 +10,6 @@ #include "CThread.h" #include "CLog.h" #include "IJob.h" -//#include "CString.h" -//#include #include #if defined(X_DISPLAY_MISSING) # error X11 is required to build synergy @@ -310,7 +308,7 @@ CXWindowsScreen::checkClipboards() } void -CXWindowsScreen::openScreenSaver(bool notify) +CXWindowsScreen::openScreensaver(bool notify) { CLock lock(&m_mutex); assert(m_screensaver != NULL); @@ -325,7 +323,7 @@ CXWindowsScreen::openScreenSaver(bool notify) } void -CXWindowsScreen::closeScreenSaver() +CXWindowsScreen::closeScreensaver() { CLock lock(&m_mutex); if (m_screensaver != NULL) { diff --git a/platform/CXWindowsScreen.h b/platform/CXWindowsScreen.h index e164c583..5c7bf18b 100644 --- a/platform/CXWindowsScreen.h +++ b/platform/CXWindowsScreen.h @@ -2,7 +2,6 @@ #define CXWINDOWSSCREEN_H #include "IScreen.h" -#include "ClipboardTypes.h" #include "CMutex.h" #include "CStopwatch.h" #include "stdvector.h" @@ -59,8 +58,8 @@ public: void close(); bool setClipboard(ClipboardID, const IClipboard*); void checkClipboards(); - void openScreenSaver(bool notify); - void closeScreenSaver(); + void openScreensaver(bool notify); + void closeScreensaver(); void screensaver(bool activate); void syncDesktop(); bool getClipboard(ClipboardID, IClipboard*) const; diff --git a/platform/IMSWindowsScreenEventHandler.h b/platform/IMSWindowsScreenEventHandler.h new file mode 100755 index 00000000..33804a96 --- /dev/null +++ b/platform/IMSWindowsScreenEventHandler.h @@ -0,0 +1,26 @@ +#ifndef IMSWINDOWSSCREENEVENTHANDLER_H +#define IMSWINDOWSSCREENEVENTHANDLER_H + +#include "IScreenEventHandler.h" +#define WIN32_LEAN_AND_MEAN +#include + +class IMSWindowsScreenEventHandler : public IScreenEventHandler { +public: + // manipulators + + // called after the window is created + virtual void postCreateWindow(HWND) = 0; + + // called before the window is destroyed + virtual void preDestroyWindow(HWND) = 0; + + // IScreenEventHandler overrides + virtual void onError() = 0; + virtual void onScreensaver(bool activated) = 0; + virtual bool onPreDispatch(const CEvent* event) = 0; + virtual bool onEvent(CEvent* event) = 0; + virtual SInt32 getJumpZoneSize() const = 0; +}; + +#endif diff --git a/server/makehook.dsp b/platform/makehook.dsp similarity index 100% rename from server/makehook.dsp rename to platform/makehook.dsp diff --git a/platform/platform.dsp b/platform/platform.dsp index 7ebcc139..ad92fece 100644 --- a/platform/platform.dsp +++ b/platform/platform.dsp @@ -127,6 +127,10 @@ SOURCE=.\CWin32Platform.h # End Source File # Begin Source File +SOURCE=.\IMSWindowsScreenEventHandler.h +# End Source File +# Begin Source File + SOURCE=.\IPlatform.h # End Source File # End Group diff --git a/server/synrgyhk.dsp b/platform/synrgyhk.dsp similarity index 100% rename from server/synrgyhk.dsp rename to platform/synrgyhk.dsp diff --git a/server/CConfig.cpp b/server/CConfig.cpp index 19d368a7..082f7a47 100644 --- a/server/CConfig.cpp +++ b/server/CConfig.cpp @@ -624,22 +624,22 @@ operator<<(std::ostream& s, const CConfig& config) screen != config.end(); ++screen) { s << "\t" << screen->c_str() << ":" << std::endl; - neighbor = config.getNeighbor(*screen, CConfig::kLeft); + neighbor = config.getNeighbor(*screen, kLeft); if (!neighbor.empty()) { s << "\t\tleft=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kRight); + neighbor = config.getNeighbor(*screen, kRight); if (!neighbor.empty()) { s << "\t\tright=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kTop); + neighbor = config.getNeighbor(*screen, kTop); if (!neighbor.empty()) { s << "\t\tup=" << neighbor.c_str() << std::endl; } - neighbor = config.getNeighbor(*screen, CConfig::kBottom); + neighbor = config.getNeighbor(*screen, kBottom); if (!neighbor.empty()) { s << "\t\tdown=" << neighbor.c_str() << std::endl; } diff --git a/server/CConfig.h b/server/CConfig.h index a12d1a03..a67d211f 100644 --- a/server/CConfig.h +++ b/server/CConfig.h @@ -1,6 +1,7 @@ #ifndef CCONFIG_H #define CCONFIG_H +#include "ProtocolTypes.h" #include "CNetworkAddress.h" #include "XBase.h" #include "stdmap.h" @@ -20,11 +21,6 @@ struct iterator_traits { }; class CConfig { -public: - enum EDirection { kLeft, kRight, kTop, kBottom, - kFirstDirection = kLeft, kLastDirection = kBottom }; - enum EDirectionMask { kLeftMask = 1, kRightMask = 2, - kTopMask = 4, kBottomMask = 8 }; private: class CCell { public: diff --git a/server/CHTTPServer.cpp b/server/CHTTPServer.cpp index c626462d..1ac810ac 100644 --- a/server/CHTTPServer.cpp +++ b/server/CHTTPServer.cpp @@ -649,7 +649,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) // insert the screen's neighbors // FIXME -- handle edge wrapping CString neighbor; - neighbor = config.getNeighbor(name, CConfig::kLeft); + neighbor = config.getNeighbor(name, kLeft); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert left neighbor, adding a column if necessary if (x == 0 || get(x - 1, y) != neighbor) { @@ -659,7 +659,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kRight); + neighbor = config.getNeighbor(name, kRight); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert right neighbor, adding a column if necessary if (x == m_w - 1 || get(x + 1, y) != neighbor) { @@ -668,7 +668,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kTop); + neighbor = config.getNeighbor(name, kTop); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert top neighbor, adding a row if necessary if (y == 0 || get(x, y - 1) != neighbor) { @@ -678,7 +678,7 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } screenStack.push_back(neighbor); } - neighbor = config.getNeighbor(name, CConfig::kBottom); + neighbor = config.getNeighbor(name, kBottom); if (!neighbor.empty() && doneSet.count(neighbor) == 0) { // insert bottom neighbor, adding a row if necessary if (y == m_h - 1 || get(x, y + 1) != neighbor) { @@ -699,25 +699,25 @@ CHTTPServer::CScreenArray::convertFrom(const CConfig& config) } CString neighbor; - neighbor = config.getNeighbor(name, CConfig::kLeft); + neighbor = config.getNeighbor(name, kLeft); if ((x == 0 && !neighbor.empty()) || (x > 0 && get(x - 1, y) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kRight); + neighbor = config.getNeighbor(name, kRight); if ((x == m_w - 1 && !neighbor.empty()) || (x < m_w - 1 && get(x + 1, y) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kTop); + neighbor = config.getNeighbor(name, kTop); if ((y == 0 && !neighbor.empty()) || (y > 0 && get(x, y - 1) != neighbor)) { return false; } - neighbor = config.getNeighbor(name, CConfig::kBottom); + neighbor = config.getNeighbor(name, kBottom); if ((y == m_h - 1 && !neighbor.empty()) || (y < m_h - 1 && get(x, y + 1) != neighbor)) { return false; @@ -764,24 +764,16 @@ CHTTPServer::CScreenArray::convertTo(CConfig& config) const continue; } if (x > x0 && isSet(x - 1, y)) { - config.connect(get(x, y), - CConfig::kLeft, - get(x - 1, y)); + config.connect(get(x, y), kLeft, get(x - 1, y)); } if (x < x1 && isSet(x + 1, y)) { - config.connect(get(x, y), - CConfig::kRight, - get(x + 1, y)); + config.connect(get(x, y), kRight, get(x + 1, y)); } if (y > y0 && isSet(x, y - 1)) { - config.connect(get(x, y), - CConfig::kTop, - get(x, y - 1)); + config.connect(get(x, y), kTop, get(x, y - 1)); } if (y < y1 && isSet(x, y + 1)) { - config.connect(get(x, y), - CConfig::kBottom, - get(x, y + 1)); + config.connect(get(x, y), kBottom, get(x, y + 1)); } } } diff --git a/server/CMSWindowsPrimaryScreen.cpp b/server/CMSWindowsPrimaryScreen.cpp index f1a2dad7..3906bcaf 100644 --- a/server/CMSWindowsPrimaryScreen.cpp +++ b/server/CMSWindowsPrimaryScreen.cpp @@ -1,14 +1,8 @@ #include "CMSWindowsPrimaryScreen.h" -#include "IScreenReceiver.h" +#include "CMSWindowsScreen.h" #include "IPrimaryScreenReceiver.h" -#include "CMSWindowsClipboard.h" -#include "CMSWindowsScreenSaver.h" #include "CPlatform.h" -#include "CClipboard.h" -#include "ProtocolTypes.h" #include "XScreen.h" -#include "XSynergy.h" -#include "CThread.h" #include "CLog.h" #include @@ -19,21 +13,16 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( IScreenReceiver* receiver, IPrimaryScreenReceiver* primaryReceiver) : - m_receiver(receiver), - m_primaryReceiver(primaryReceiver), + CPrimaryScreen(receiver), + m_receiver(primaryReceiver), + m_is95Family(CPlatform::isWindows95Family()), m_threadID(0), - m_timer(0), - m_desk(NULL), - m_deskName(), m_window(NULL), - m_active(false), m_mark(0), m_markReceived(0), - m_nextClipboardWindow(NULL), - m_clipboardOwner(NULL) + m_mouseMoveIgnore(0) { - assert(m_receiver != NULL); - assert(m_primaryReceiver != NULL); + assert(m_receiver != NULL); // load the hook library m_hookLibrary = LoadLibrary("synrgyhk"); @@ -60,22 +49,8 @@ CMSWindowsPrimaryScreen::CMSWindowsPrimaryScreen( throw XScreenOpenFailure(); } - // get the screen saver functions - m_installScreenSaver = (InstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "installScreenSaver"); - m_uninstallScreenSaver = (UninstallScreenSaverFunc)GetProcAddress( - m_hookLibrary, "uninstallScreenSaver"); - if (m_installScreenSaver == NULL || m_uninstallScreenSaver == NULL) { - // disable uninstall if install is unavailable - m_uninstallScreenSaver = NULL; - } - - // detect operating system - m_is95Family = CPlatform::isWindows95Family(); - - // make sure this thread has a message queue - MSG dummy; - PeekMessage(&dummy, NULL, WM_USER, WM_USER, PM_NOREMOVE); + // create screen + m_screen = new CMSWindowsScreen(receiver, this); } CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() @@ -83,169 +58,10 @@ CMSWindowsPrimaryScreen::~CMSWindowsPrimaryScreen() assert(m_hookLibrary != NULL); assert(m_window == NULL); - // done with hook library + delete m_screen; FreeLibrary(m_hookLibrary); } -void -CMSWindowsPrimaryScreen::run() -{ - // must call run() from same thread as open() - assert(m_threadID == GetCurrentThreadId()); - - // 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 -CMSWindowsPrimaryScreen::stop() -{ - exitMainLoop(); -} - -void -CMSWindowsPrimaryScreen::open() -{ - assert(m_window == NULL); - - CClientInfo info; - try { - // initialize hook library - m_threadID = GetCurrentThreadId(); - m_init(m_threadID); - - // open the display - openDisplay(); - - // create and prepare our window - createWindow(); - - // set jump zones - m_setZone(info.m_x, info.m_y, info.m_w, info.m_h, info.m_zoneSize); - - // initialize marks - m_mark = 0; - m_markReceived = 0; - nextMark(); - - // collect screen info - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // save mouse position - m_x = info.m_mx; - m_y = info.m_my; - - // compute center pixel of primary screen - getCursorCenter(m_xCenter, m_yCenter); - - // get keyboard state - updateKeys(); - - // get notified of screen saver activation/deactivation - installScreenSaver(); - } - catch (...) { - close(); - throw; - } - - // enter the screen - enterNoWarp(); - - // send screen info - m_receiver->onInfoChanged(info); -} - -void -CMSWindowsPrimaryScreen::close() -{ - uninstallScreenSaver(); - destroyWindow(); - closeDisplay(); - m_cleanup(); - m_threadID = 0; -} - -void -CMSWindowsPrimaryScreen::enter(SInt32 x, SInt32 y, bool forScreenSaver) -{ - log((CLOG_INFO "entering primary at %d,%d%s", x, y, forScreenSaver ? " for screen saver" : "")); - assert(m_active == true); - assert(m_window != NULL); - - // enter the screen - enterNoWarp(); - - // warp to requested location - if (!forScreenSaver) { - warpCursor(x, y); - } -} - -bool -CMSWindowsPrimaryScreen::leave() -{ - log((CLOG_INFO "leaving primary")); - assert(m_active == false); - assert(m_window != NULL); - - // all messages prior to now are invalid - nextMark(); - - // show our window - if (!showWindow()) { - return false; - } - - // relay all mouse and keyboard events - m_setRelay(true); - - // get state of keys as we leave - updateKeys(); - - // warp mouse to center of screen - warpCursorToCenter(); - - // ignore this many mouse motion events (not including the already - // queued events). on (at least) the win2k login desktop, one - // motion event is reported using a position from before the above - // warpCursor(). i don't know why it does that and other desktops - // don't have the same problem. anyway, simply ignoring that event - // works around it. -// FIXME -- is this true now that we're using mouse_event? - m_mouseMoveIgnore = 1; - - // disable ctrl+alt+del, alt+tab, etc - if (m_is95Family) { - DWORD dummy = 0; - SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); - } - - // discard messages until after the warp - nextMark(); - - // local client now active - m_active = true; - - // make sure our idea of clipboard ownership is correct - checkClipboard(); - - return true; -} - void CMSWindowsPrimaryScreen::reconfigure(UInt32 activeSides) { @@ -264,41 +80,6 @@ CMSWindowsPrimaryScreen::warpCursor(SInt32 x, SInt32 y) m_y = y; } -void -CMSWindowsPrimaryScreen::setClipboard(ClipboardID /*id*/, - const IClipboard* src) -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard dst(m_window); - CClipboard::copy(&dst, src); -} - -void -CMSWindowsPrimaryScreen::grabClipboard(ClipboardID /*id*/) -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard clipboard(m_window); - if (clipboard.open(0)) { - // FIXME -- don't we need to empty it? - clipboard.close(); - } -} - -void -CMSWindowsPrimaryScreen::getClipboard(ClipboardID /*id*/, - IClipboard* dst) const -{ - // FIXME -- this is identical to CMSWindowsSecondaryScreen's code - assert(m_window != NULL); - - CMSWindowsClipboard src(m_window); - CClipboard::copy(dst, &src); -} - KeyModifierMask CMSWindowsPrimaryScreen::getToggleMask() const { @@ -334,7 +115,7 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const // that have been pulled off the queue. in general, we won't get // these key messages because they're not for our window. if any // key (or mouse button) is down then we're locked to the screen. - if (m_active) { + if (isActive()) { // use shadow keyboard state in m_keys for (UInt32 i = 0; i < 256; ++i) { if ((m_keys[i] & 0x80) != 0) { @@ -358,16 +139,29 @@ CMSWindowsPrimaryScreen::isLockedToScreen() const return false; } +IScreen* +CMSWindowsPrimaryScreen::getScreen() const +{ + return m_screen; +} + +void +CMSWindowsPrimaryScreen::onError() +{ + // ignore +} + +void +CMSWindowsPrimaryScreen::onScreensaver(bool activated) +{ + m_receiver->onScreensaver(activated); +} + bool -CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) +CMSWindowsPrimaryScreen::onPreDispatch(const CEvent* event) { assert(event != NULL); - // forward to superclass - if (CMSWindowsScreen::onPreTranslate(event)) { - return true; - } - // handle event const MSG* msg = &event->m_msg; switch (msg->message) { @@ -386,11 +180,11 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) const SInt32 repeat = (SInt32)(msg->lParam & 0xffff); if (repeat >= 2) { log((CLOG_DEBUG1 "event: key repeat key=%d mask=0x%04x count=%d", key, mask, repeat)); - m_primaryReceiver->onKeyRepeat(key, mask, repeat); + m_receiver->onKeyRepeat(key, mask, repeat); } else { log((CLOG_DEBUG1 "event: key press key=%d mask=0x%04x", key, mask)); - m_primaryReceiver->onKeyDown(key, mask); + m_receiver->onKeyDown(key, mask); } // update key state @@ -399,7 +193,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) else { // key release log((CLOG_DEBUG1 "event: key release key=%d mask=0x%04x", key, mask)); - m_primaryReceiver->onKeyUp(key, mask); + m_receiver->onKeyUp(key, mask); // update key state updateKey(msg->wParam, false); @@ -428,7 +222,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) case WM_RBUTTONDOWN: log((CLOG_DEBUG1 "event: button press button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseDown(button); + m_receiver->onMouseDown(button); m_keys[s_vkButton[button]] |= 0x80; } break; @@ -438,7 +232,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) case WM_RBUTTONUP: log((CLOG_DEBUG1 "event: button release button=%d", button)); if (button != kButtonNone) { - m_primaryReceiver->onMouseUp(button); + m_receiver->onMouseUp(button); m_keys[s_vkButton[button]] &= ~0x80; } break; @@ -450,7 +244,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) // ignore message if posted prior to last mark change if (m_markReceived == m_mark) { log((CLOG_ERR "event: button wheel delta=%d %d", msg->wParam, msg->lParam)); - m_primaryReceiver->onMouseWheel(msg->wParam); + m_receiver->onMouseWheel(msg->wParam); } return true; @@ -459,8 +253,8 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) if (m_markReceived == m_mark) { SInt32 x = static_cast(msg->wParam); SInt32 y = static_cast(msg->lParam); - if (!m_active) { - m_primaryReceiver->onMouseMovePrimary(x, y); + if (!isActive()) { + m_receiver->onMouseMovePrimary(x, y); } else { // compute motion delta. this is relative to the @@ -480,7 +274,7 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) warpCursorToCenter(); // send motion - m_primaryReceiver->onMouseMoveSecondary(x, y); + m_receiver->onMouseMoveSecondary(x, y); } } else { @@ -495,32 +289,6 @@ CMSWindowsPrimaryScreen::onPreTranslate(const CEvent* event) m_x = static_cast(msg->wParam); m_y = static_cast(msg->lParam); return true; - - case SYNERGY_MSG_SCREEN_SAVER: - if (msg->wParam != 0) { - if (getScreenSaver()->checkStarted(msg->message, FALSE, 0)) { - m_primaryReceiver->onScreenSaver(true); - } - } - else { - m_primaryReceiver->onScreenSaver(false); - } - return true; - - case WM_TIMER: - // if current desktop is not the input desktop then switch to it - if (!m_is95Family) { - HDESK desk = openInputDesktop(); - if (desk != NULL) { - if (isCurrentDesktop(desk)) { - CloseDesktop(desk); - } - else { - switchDesktop(desk); - } - } - } - return true; } return false; @@ -531,111 +299,29 @@ CMSWindowsPrimaryScreen::onEvent(CEvent* event) { assert(event != NULL); - const MSG& msg = event->msg; + const MSG& msg = event->m_msg; switch (msg.message) { - case WM_QUERYENDSESSION: - if (m_is95Family) { - event->m_result = TRUE; - return true; - } - break; - - case WM_ENDSESSION: - if (m_is95Family) { - if (msg.wParam == TRUE && msg.lParam == 0) { - stop(); - } - return true; - } - break; - - case WM_PAINT: - ValidateRect(msg.hwnd, NULL); - return true; - - case WM_DRAWCLIPBOARD: - log((CLOG_DEBUG "clipboard was taken")); - - // first pass it on - if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - - // now notify server that somebody changed the clipboard. - // skip that if we're the new owner. - try { - m_clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore. this can happen if we receive this event - // before we've fully started up. - } - return true; - - case WM_CHANGECBCHAIN: - if (m_nextClipboardWindow == (HWND)msg.wParam) { - m_nextClipboardWindow = (HWND)msg.lParam; - } - else if (m_nextClipboardWindow != NULL) { - SendMessage(m_nextClipboardWindow, - msg.message, msg.wParam, msg.lParam); - } - return true; - case WM_DISPLAYCHANGE: - { - // screen resolution may have changed. get old shape. - SInt32 xOld, yOld, wOld, hOld; - getScreenShape(xOld, yOld, wOld, hOld); + // recompute center pixel of primary screen + m_screen->getCursorCenter(m_xCenter, m_yCenter); - // update shape - updateScreenShape(); - - // collect new screen info - CClientInfo info; - getScreenShape(info.m_x, info.m_y, info.m_w, info.m_h); - getCursorPos(info.m_mx, info.m_my); - info.m_zoneSize = getJumpZoneSize(); - - // do nothing if resolution hasn't changed - if (info.m_x != xOld || info.m_y != yOld || - info.m_w != wOld || info.m_h != hOld) { - // recompute center pixel of primary screen - getCursorCenter(m_xCenter, m_yCenter); - - // warp mouse to center if active - if (m_active) { - warpCursorToCenter(); - } - - // tell hook about resize if not active - else { - m_setZone(info.m_x, info.m_y, - info.m_w, info.m_h, info.m_zoneSize); - } - - // send new screen info - m_receiver->onInfoChanged(info); - } - - return true; + // warp mouse to center if active + if (isActive()) { + warpCursorToCenter(); } + + // tell hook about resize if not active + else { + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + } + return true; } return false; } -CString -CMSWindowsPrimaryScreen::getCurrentDesktopName() const -{ - return m_deskName; -} - SInt32 CMSWindowsPrimaryScreen::getJumpZoneSize() const { @@ -643,25 +329,96 @@ CMSWindowsPrimaryScreen::getJumpZoneSize() const } void -CMSWindowsPrimaryScreen::warpCursorToCenter() +CMSWindowsPrimaryScreen::postCreateWindow(HWND window) { - // warp to center. the extra info tells the hook DLL to send - // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. + // save window + m_window = window; + + // install hooks + m_install(); + + // resize window + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the screen) causes all other windows to redraw. SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, - (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), - (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), - 0, - 0x12345678); -// FIXME -- ignore mouse until we get warp notification? + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); + + if (isActive()) { + // hide the cursor + showWindow(); + } + else { + // watch jump zones + m_setRelay(false); + + // all messages prior to now are invalid + nextMark(); + } } void -CMSWindowsPrimaryScreen::enterNoWarp() +CMSWindowsPrimaryScreen::preDestroyWindow(HWND) { - // not active anymore - m_active = false; + // hide the window if it's visible + if (isActive()) { + hideWindow(); + } + + // uninstall hooks + m_uninstall(); +} + +void +CMSWindowsPrimaryScreen::onPreRun() +{ + // must call run() from same thread as open() + assert(m_threadID == GetCurrentThreadId()); + assert(m_window != NULL); +} + +void +CMSWindowsPrimaryScreen::onPreOpen() +{ + assert(m_window == NULL); + + // initialize hook library + m_threadID = GetCurrentThreadId(); + m_init(m_threadID); +} + +void +CMSWindowsPrimaryScreen::onPostOpen() +{ + // get cursor info + m_screen->getCursorPos(m_x, m_y); + m_screen->getCursorCenter(m_xCenter, m_yCenter); + + // set jump zones + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + m_setZone(x, y, w, h, getJumpZoneSize()); + + // initialize marks + m_mark = 0; + m_markReceived = 0; + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPostClose() +{ + m_cleanup(); + m_threadID = 0; +} + +void +CMSWindowsPrimaryScreen::onPreEnter() +{ + assert(m_window != NULL); // reset motion ignore count m_mouseMoveIgnore = 0; @@ -674,14 +431,78 @@ CMSWindowsPrimaryScreen::enterNoWarp() // watch jump zones m_setRelay(false); +} - // restore active window and hide our window - hideWindow(); +void +CMSWindowsPrimaryScreen::onPostEnter() +{ + // all messages prior to now are invalid + nextMark(); +} + +void +CMSWindowsPrimaryScreen::onPreLeave() +{ + assert(m_window != NULL); // all messages prior to now are invalid nextMark(); } +void +CMSWindowsPrimaryScreen::onPostLeave(bool success) +{ + if (success) { + // relay all mouse and keyboard events + m_setRelay(true); + + // ignore this many mouse motion events (not including the already + // queued events). on (at least) the win2k login desktop, one + // motion event is reported using a position from before the above + // warpCursor(). i don't know why it does that and other desktops + // don't have the same problem. anyway, simply ignoring that event + // works around it. +// FIXME -- is this true now that we're using mouse_event? + m_mouseMoveIgnore = 1; + + // disable ctrl+alt+del, alt+tab, etc + if (m_is95Family) { + DWORD dummy = 0; + SystemParametersInfo(SPI_SETSCREENSAVERRUNNING, TRUE, &dummy, 0); + } + + // discard messages until after the warp + nextMark(); + } +} + +void +CMSWindowsPrimaryScreen::createWindow() +{ + // open the desktop and the window + m_window = m_screen->openDesktop(); + if (m_window == NULL) { + throw XScreenOpenFailure(); + } + + // note -- we use a fullscreen window to grab input. it should + // be possible to use a 1x1 window but i've run into problems + // with losing keyboard input (focus?) in that case. + // unfortunately, hiding the full screen window (when entering + // the scren causes all other windows to redraw). + SInt32 x, y, w, h; + m_screen->getShape(x, y, w, h); + MoveWindow(m_window, x, y, w, h, FALSE); +} + +void +CMSWindowsPrimaryScreen::destroyWindow() +{ + // close the desktop and the window + m_screen->closeDesktop(); + m_window = NULL; +} + bool CMSWindowsPrimaryScreen::showWindow() { @@ -733,262 +554,18 @@ CMSWindowsPrimaryScreen::hideWindow() } void -CMSWindowsPrimaryScreen::checkClipboard() +CMSWindowsPrimaryScreen::warpCursorToCenter() { - // if we think we own the clipboard but we don't then somebody - // grabbed the clipboard on this screen without us knowing. - // tell the server that this screen grabbed the clipboard. - // - // this works around bugs in the clipboard viewer chain. - // sometimes NT will simply never send WM_DRAWCLIPBOARD - // messages for no apparent reason and rebooting fixes the - // problem. since we don't want a broken clipboard until the - // next reboot we do this double check. clipboard ownership - // won't be reflected on other screens until we leave but at - // least the clipboard itself will work. - HWND clipboardOwner = GetClipboardOwner(); - if (m_clipboardOwner != clipboardOwner) { - try { - m_clipboardOwner = clipboardOwner; - if (m_clipboardOwner != m_window && m_clipboardOwner != NULL) { - m_receiver->onGrabClipboard(kClipboardClipboard); - m_receiver->onGrabClipboard(kClipboardSelection); - } - } - catch (XBadClient&) { - // ignore - } - } -} - -void -CMSWindowsPrimaryScreen::createWindow() -{ - // get the input desktop and switch to it - if (m_is95Family) { - if (!openDesktop()) { - throw XScreenOpenFailure(); - } - } - else { - if (!switchDesktop(openInputDesktop())) { - throw XScreenOpenFailure(); - } - } - - // poll input desktop to see if it changes (preTranslateMessage() - // handles WM_TIMER) - m_timer = 0; - if (!m_is95Family) { - m_timer = SetTimer(NULL, 0, 200, NULL); - } -} - -void -CMSWindowsPrimaryScreen::destroyWindow() -{ - // remove timer - if (m_timer != 0) { - KillTimer(NULL, m_timer); - } - - // disconnect from desktop - if (m_is95Family) { - closeDesktop(); - } - else { - switchDesktop(NULL); - } - - assert(m_window == NULL); - assert(m_desk == NULL); -} - -void -CMSWindowsPrimaryScreen::installScreenSaver() -{ - // install the screen saver hook - if (m_installScreenSaver != NULL) { - m_installScreenSaver(); - } -} - -void -CMSWindowsPrimaryScreen::uninstallScreenSaver() -{ - // uninstall the screen saver hook - if (m_uninstallScreenSaver != NULL) { - m_uninstallScreenSaver(); - } -} - -bool -CMSWindowsPrimaryScreen::openDesktop() -{ - // install hooks - m_install(); - - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the scren causes all other windows to redraw). + // warp to center. the extra info tells the hook DLL to send + // SYNERGY_MSG_POST_WARP instead of SYNERGY_MSG_MOUSE_MOVE. SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | - WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), - "Synergy", - WS_POPUP, - x, y, w, h, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - m_uninstall(); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - return true; -} - -void -CMSWindowsPrimaryScreen::closeDesktop() -{ - // destroy old window - if (m_window != NULL) { - // restore active window and hide ours - if (m_active) { - hideWindow(); - } - - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // we no longer own the clipboard - if (m_clipboardOwner == m_window) { - m_clipboardOwner = NULL; - } - - // now destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // unhook - m_uninstall(); -} - -bool -CMSWindowsPrimaryScreen::switchDesktop(HDESK desk) -{ - // did we own the clipboard? - bool ownClipboard = (m_clipboardOwner == m_window); - - // destroy old window - if (m_window != NULL) { - // restore active window and hide ours - if (m_active) { - hideWindow(); - } - - // first remove clipboard snooper - ChangeClipboardChain(m_window, m_nextClipboardWindow); - m_nextClipboardWindow = NULL; - - // we no longer own the clipboard - if (ownClipboard) { - m_clipboardOwner = NULL; - } - - // now destroy window - DestroyWindow(m_window); - m_window = NULL; - } - - // unhook - if (m_desk != NULL) { - m_uninstall(); - CloseDesktop(m_desk); - m_desk = NULL; - m_deskName = ""; - } - - // if no new desktop then we're done - if (desk == NULL) { - log((CLOG_INFO "disconnecting desktop")); - return true; - } - - // set the desktop. can only do this when there are no windows - // and hooks on the current desktop owned by this thread. - if (SetThreadDesktop(desk) == 0) { - log((CLOG_ERR "failed to set desktop: %d", GetLastError())); - CloseDesktop(desk); - return false; - } - - // install hooks - m_install(); - - // note -- we use a fullscreen window to grab input. it should - // be possible to use a 1x1 window but i've run into problems - // with losing keyboard input (focus?) in that case. - // unfortunately, hiding the full screen window (when entering - // the scren causes all other windows to redraw). - SInt32 x, y, w, h; - getScreenShape(x, y, w, h); - - // create the window - m_window = CreateWindowEx(WS_EX_TOPMOST | - WS_EX_TRANSPARENT | - WS_EX_TOOLWINDOW, - (LPCTSTR)getClass(), - "Synergy", - WS_POPUP, - x, y, w, h, NULL, NULL, - getInstance(), - NULL); - if (m_window == NULL) { - log((CLOG_ERR "failed to create window: %d", GetLastError())); - m_uninstall(); - CloseDesktop(desk); - return false; - } - - // install our clipboard snooper - m_nextClipboardWindow = SetClipboardViewer(m_window); - - // reassert clipboard ownership - if (ownClipboard) { - // FIXME - } - - // save new desktop - m_desk = desk; - m_deskName = getDesktopName(m_desk); - log((CLOG_INFO "switched to desktop %s", m_deskName.c_str())); - - // get active window and show ours - if (m_active) { - showWindow(); - } - else { - // watch jump zones - m_setRelay(false); - - // all messages prior to now are invalid - nextMark(); - } - - return true; + m_screen->getShape(x, y, w, h); + mouse_event(MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_MOVE, + (DWORD)((65535.99 * (m_xCenter - x)) / (w - 1)), + (DWORD)((65535.99 * (m_yCenter - y)) / (h - 1)), + 0, + 0x12345678); +// FIXME -- ignore mouse until we get warp notification? } void diff --git a/server/CMSWindowsPrimaryScreen.h b/server/CMSWindowsPrimaryScreen.h index 7856dccb..5414d633 100644 --- a/server/CMSWindowsPrimaryScreen.h +++ b/server/CMSWindowsPrimaryScreen.h @@ -1,74 +1,61 @@ #ifndef CMSWINDOWSPRIMARYSCREEN_H #define CMSWINDOWSPRIMARYSCREEN_H -#include "CMSWindowsScreen.h" -#include "IPrimaryScreen.h" +#include "CPrimaryScreen.h" +#include "IMSWindowsScreenEventHandler.h" #include "CSynergyHook.h" #include "MouseTypes.h" #include "CString.h" +class CMSWindowsScreen; class IScreenReceiver; class IPrimaryScreenReceiver; -class CMSWindowsPrimaryScreen : public CMSWindowsScreen, public IPrimaryScreen { +class CMSWindowsPrimaryScreen : + public CPrimaryScreen, public IMSWindowsScreenEventHandler { public: typedef bool (CMSWindowsPrimaryScreen::*HookMethod)(int, WPARAM, LPARAM); CMSWindowsPrimaryScreen(IScreenReceiver*, IPrimaryScreenReceiver*); virtual ~CMSWindowsPrimaryScreen(); - // 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; - -protected: - // CMSWindowsScreen overrides - virtual bool onPreTranslate(const CEvent* event); + virtual IScreen* getScreen() const; + + // IMSWindowsScreenEventHandler overrides + virtual void onError(); + virtual void onScreensaver(bool activated); + virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); - virtual CString getCurrentDesktopName() const; + virtual SInt32 getJumpZoneSize() const; + virtual void postCreateWindow(HWND); + virtual void preDestroyWindow(HWND); + +protected: + // CPrimaryScreen overrides + virtual void onPreRun(); + virtual void onPreOpen(); + virtual void onPostOpen(); + virtual void onPostClose(); + virtual void onPreEnter(); + virtual void onPostEnter(); + virtual void onPreLeave(); + virtual void onPostLeave(bool); + + virtual void createWindow(); + virtual void destroyWindow(); + virtual bool showWindow(); + virtual void hideWindow(); + virtual void warpCursorToCenter(); + + virtual void updateKeys(); private: - SInt32 getJumpZoneSize() const; - - // warp mouse to center of primary display (used when computing - // motion deltas while mouse is on secondary screen). - void warpCursorToCenter(); - void enterNoWarp(); - bool showWindow(); - void hideWindow(); - - // check clipboard ownership and, if necessary, tell the receiver - // of a grab. - void checkClipboard(); - - // create/destroy window - // also attach to desktop; this destroys and recreates the window - // as necessary. - void createWindow(); - void destroyWindow(); - - // start/stop watch for screen saver changes - void installScreenSaver(); - void uninstallScreenSaver(); - - // open/close desktop (for windows 95/98/me) - bool openDesktop(); - void closeDesktop(); - - // make desk the thread desktop (for windows NT/2000/XP) - bool switchDesktop(HDESK desk); // discard posted messages void nextMark(); @@ -77,12 +64,11 @@ private: KeyID mapKey(WPARAM keycode, LPARAM info, KeyModifierMask* maskOut); ButtonID mapButton(WPARAM button) const; - void updateKeys(); void updateKey(UINT vkCode, bool press); private: - IScreenReceiver* m_receiver; - IPrimaryScreenReceiver* m_primaryReceiver; + IPrimaryScreenReceiver* m_receiver; + CMSWindowsScreen* m_screen; // true if windows 95/98/me bool m_is95Family; @@ -90,27 +76,13 @@ private: // the main loop's thread id DWORD m_threadID; - // the timer used to check for desktop switching - UINT m_timer; - - // the current desk and it's name - HDESK m_desk; - CString m_deskName; - - // our window (for getting clipboard changes) + // our window HWND m_window; - // m_active is true the hooks are relaying events - bool m_active; - // used to discard queued messages that are no longer needed UInt32 m_mark; UInt32 m_markReceived; - // clipboard stuff - HWND m_nextClipboardWindow; - HWND m_clipboardOwner; - // map of key state BYTE m_keys[256]; @@ -132,8 +104,6 @@ private: SetSidesFunc m_setSides; SetZoneFunc m_setZone; SetRelayFunc m_setRelay; - InstallScreenSaverFunc m_installScreenSaver; - UninstallScreenSaverFunc m_uninstallScreenSaver; // stuff for restoring active window HWND m_lastForegroundWindow; diff --git a/server/CPrimaryScreen.cpp b/server/CPrimaryScreen.cpp index 5dafa752..a649a227 100644 --- a/server/CPrimaryScreen.cpp +++ b/server/CPrimaryScreen.cpp @@ -73,7 +73,7 @@ CPrimaryScreen::open() updateKeys(); // get notified of screen saver activation/deactivation - getScreen()->openScreenSaver(true); + getScreen()->openScreensaver(true); // subclass hook onPostOpen(); @@ -94,7 +94,7 @@ void CPrimaryScreen::close() { onPreClose(); - getScreen()->closeScreenSaver(); + getScreen()->closeScreensaver(); destroyWindow(); getScreen()->close(); onPostClose(); @@ -197,12 +197,6 @@ CPrimaryScreen::getClipboard(ClipboardID id, getScreen()->getClipboard(id, clipboard); } -SInt32 -CPrimaryScreen::getJumpZoneSize() const -{ - return 1; -} - void CPrimaryScreen::onPreRun() { diff --git a/server/CPrimaryScreen.h b/server/CPrimaryScreen.h index 06b020dd..9040222d 100644 --- a/server/CPrimaryScreen.h +++ b/server/CPrimaryScreen.h @@ -42,8 +42,8 @@ public: 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. + // bitmask of 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 @@ -66,8 +66,8 @@ public: 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; + // causes the cursor to jump to another screen. + virtual SInt32 getJumpZoneSize() const = 0; // get the primary screen's current toggle modifier key state. // the returned mask should have the corresponding bit set for diff --git a/server/CServer.cpp b/server/CServer.cpp index f13af415..1491f6c1 100644 --- a/server/CServer.cpp +++ b/server/CServer.cpp @@ -197,21 +197,17 @@ CServer::getActivePrimarySides() const { // note -- m_mutex must be locked on entry UInt32 sides = 0; - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kLeft).empty()) { - sides |= CConfig::kLeftMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kLeft).empty()) { + sides |= kLeftMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kRight).empty()) { - sides |= CConfig::kRightMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kRight).empty()) { + sides |= kRightMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kTop).empty()) { - sides |= CConfig::kTopMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kTop).empty()) { + sides |= kTopMask; } - if (!m_config.getNeighbor(getPrimaryScreenName(), - CConfig::kBottom).empty()) { - sides |= CConfig::kBottomMask; + if (!m_config.getNeighbor(getPrimaryScreenName(), kBottom).empty()) { + sides |= kBottomMask; } return sides; } @@ -505,25 +501,25 @@ CServer::onMouseMovePrimaryNoLock(SInt32 x, SInt32 y) SInt32 zoneSize = m_active->getJumpZoneSize(); // see if we should change screens - CConfig::EDirection dir; + EDirection dir; if (x < ax + zoneSize) { x -= zoneSize; - dir = CConfig::kLeft; + dir = kLeft; log((CLOG_DEBUG1 "switch to left")); } else if (x >= ax + aw - zoneSize) { x += zoneSize; - dir = CConfig::kRight; + dir = kRight; log((CLOG_DEBUG1 "switch to right")); } else if (y < ay + zoneSize) { y -= zoneSize; - dir = CConfig::kTop; + dir = kTop; log((CLOG_DEBUG1 "switch to top")); } else if (y >= ay + ah - zoneSize) { y += zoneSize; - dir = CConfig::kBottom; + dir = kBottom; log((CLOG_DEBUG1 "switch to bottom")); } else { @@ -584,24 +580,24 @@ CServer::onMouseMoveSecondaryNoLock(SInt32 dx, SInt32 dy) IClient* newScreen = NULL; if (!isLockedToScreenNoLock()) { // find direction of neighbor - CConfig::EDirection dir; + EDirection dir; if (m_x < ax) { - dir = CConfig::kLeft; + dir = kLeft; } else if (m_x > ax + aw - 1) { - dir = CConfig::kRight; + dir = kRight; } else if (m_y < ay) { - dir = CConfig::kTop; + dir = kTop; } else if (m_y > ay + ah - 1) { - dir = CConfig::kBottom; + dir = kBottom; } else { newScreen = m_active; // keep compiler quiet about unset variable - dir = CConfig::kLeft; + dir = kLeft; } // get neighbor if we should switch @@ -760,7 +756,7 @@ CServer::switchScreen(IClient* dst, SInt32 x, SInt32 y, bool forScreensaver) } IClient* -CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const +CServer::getNeighbor(IClient* src, EDirection dir) const { assert(src != NULL); @@ -793,7 +789,7 @@ CServer::getNeighbor(IClient* src, CConfig::EDirection dir) const IClient* CServer::getNeighbor(IClient* src, - CConfig::EDirection srcSide, SInt32& x, SInt32& y) const + EDirection srcSide, SInt32& x, SInt32& y) const { assert(src != NULL); @@ -816,7 +812,7 @@ CServer::getNeighbor(IClient* src, // actual to canonical position on entry to and from canonical to // actual on exit from the search. switch (srcSide) { - case CConfig::kLeft: + case kLeft: x -= dx; while (dst != NULL && dst != lastGoodScreen) { lastGoodScreen = dst; @@ -832,7 +828,7 @@ CServer::getNeighbor(IClient* src, x += dx; break; - case CConfig::kRight: + case kRight: x -= dx; while (dst != NULL) { x -= dw; @@ -848,7 +844,7 @@ CServer::getNeighbor(IClient* src, x += dx; break; - case CConfig::kTop: + case kTop: y -= dy; while (dst != NULL) { lastGoodScreen = dst; @@ -864,7 +860,7 @@ CServer::getNeighbor(IClient* src, y += dy; break; - case CConfig::kBottom: + case kBottom: y -= dy; while (dst != NULL) { y -= dh; @@ -892,26 +888,26 @@ CServer::getNeighbor(IClient* src, if (dst == m_primaryClient) { const CString dstName(dst->getName()); switch (srcSide) { - case CConfig::kLeft: - if (!m_config.getNeighbor(dstName, CConfig::kRight).empty() && + case kLeft: + if (!m_config.getNeighbor(dstName, kRight).empty() && x > dx + dw - 1 - dst->getJumpZoneSize()) x = dx + dw - 1 - dst->getJumpZoneSize(); break; - case CConfig::kRight: - if (!m_config.getNeighbor(dstName, CConfig::kLeft).empty() && + case kRight: + if (!m_config.getNeighbor(dstName, kLeft).empty() && x < dx + dst->getJumpZoneSize()) x = dx + dst->getJumpZoneSize(); break; - case CConfig::kTop: - if (!m_config.getNeighbor(dstName, CConfig::kBottom).empty() && + case kTop: + if (!m_config.getNeighbor(dstName, kBottom).empty() && y > dy + dh - 1 - dst->getJumpZoneSize()) y = dy + dh - 1 - dst->getJumpZoneSize(); break; - case CConfig::kBottom: - if (!m_config.getNeighbor(dstName, CConfig::kTop).empty() && + case kBottom: + if (!m_config.getNeighbor(dstName, kTop).empty() && y < dy + dst->getJumpZoneSize()) y = dy + dst->getJumpZoneSize(); break; @@ -925,8 +921,8 @@ CServer::getNeighbor(IClient* src, // should be set 120 pixels from the top (again 20% from the // top). switch (srcSide) { - case CConfig::kLeft: - case CConfig::kRight: + case kLeft: + case kRight: y -= sy; if (y < 0) { y = 0; @@ -941,8 +937,8 @@ CServer::getNeighbor(IClient* src, y += dy; break; - case CConfig::kTop: - case CConfig::kBottom: + case kTop: + case kBottom: x -= sx; if (x < 0) { x = 0; diff --git a/server/CServer.h b/server/CServer.h index 7ddb4956..b8d49038 100644 --- a/server/CServer.h +++ b/server/CServer.h @@ -96,15 +96,14 @@ private: SInt32 x, SInt32 y, bool forScreenSaver); // lookup neighboring screen - IClient* getNeighbor(IClient*, CConfig::EDirection) const; + IClient* getNeighbor(IClient*, EDirection) const; // lookup neighboring screen. given a position relative to the // source screen, find the screen we should move onto and where. // if the position is sufficiently far from the source then we // cross multiple screens. if there is no suitable screen then // return NULL and x,y are not modified. - IClient* getNeighbor(IClient*, - CConfig::EDirection, + IClient* getNeighbor(IClient*, EDirection, SInt32& x, SInt32& y) const; // open/close the primary screen diff --git a/server/CXWindowsPrimaryScreen.cpp b/server/CXWindowsPrimaryScreen.cpp index dff594c8..1f7fa7b1 100644 --- a/server/CXWindowsPrimaryScreen.cpp +++ b/server/CXWindowsPrimaryScreen.cpp @@ -331,6 +331,12 @@ CXWindowsPrimaryScreen::onEvent(CEvent* event) return false; } +SInt32 +CXWindowsPrimaryScreen::getJumpZoneSize() const +{ + return 1; +} + void CXWindowsPrimaryScreen::onPreRun() { @@ -419,7 +425,7 @@ CXWindowsPrimaryScreen::createWindow() selectEvents(display, m_screen->getRoot()); } - // tell our superclass about the window + // tell generic screen about the window m_screen->setWindow(m_window); } diff --git a/server/CXWindowsPrimaryScreen.h b/server/CXWindowsPrimaryScreen.h index 6b261400..2a97a51c 100644 --- a/server/CXWindowsPrimaryScreen.h +++ b/server/CXWindowsPrimaryScreen.h @@ -32,6 +32,7 @@ public: virtual void onScreensaver(bool activated); virtual bool onPreDispatch(const CEvent* event); virtual bool onEvent(CEvent* event); + virtual SInt32 getJumpZoneSize() const; protected: // CPrimaryScreen overrides diff --git a/server/server.dsp b/server/server.dsp index 7586b091..61acba15 100644 --- a/server/server.dsp +++ b/server/server.dsp @@ -118,6 +118,10 @@ SOURCE=.\CPrimaryClient.cpp # End Source File # Begin Source File +SOURCE=.\CPrimaryScreen.cpp +# End Source File +# Begin Source File + SOURCE=.\CServer.cpp # End Source File # Begin Source File @@ -158,11 +162,11 @@ SOURCE=.\CPrimaryClient.h # End Source File # Begin Source File -SOURCE=.\CServer.h +SOURCE=.\CPrimaryScreen.h # End Source File # Begin Source File -SOURCE=.\IPrimaryScreen.h +SOURCE=.\CServer.h # End Source File # Begin Source File diff --git a/synergy/IScreen.h b/synergy/IScreen.h index 88697926..861f69c5 100644 --- a/synergy/IScreen.h +++ b/synergy/IScreen.h @@ -37,8 +37,8 @@ public: // 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; + virtual void openScreensaver(bool notify) = 0; + virtual void closeScreensaver() = 0; // activate or deactivate the screen saver virtual void screensaver(bool activate) = 0; diff --git a/synergy/IScreenEventHandler.h b/synergy/IScreenEventHandler.h index d4e04ab3..123956be 100644 --- a/synergy/IScreenEventHandler.h +++ b/synergy/IScreenEventHandler.h @@ -34,6 +34,11 @@ public: // 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; + + // accessors + + // called to get the jump zone size + virtual SInt32 getJumpZoneSize() const = 0; }; #endif diff --git a/synergy/ProtocolTypes.h b/synergy/ProtocolTypes.h index a47f7d73..391c8ea3 100644 --- a/synergy/ProtocolTypes.h +++ b/synergy/ProtocolTypes.h @@ -16,6 +16,23 @@ static const double kHeartRate = 2.0; // time without a heartbeat that we call death static const double kHeartDeath = 3.0 * kHeartRate; +// direction constants +enum EDirection { + kLeft, + kRight, + kTop, + kBottom, + kFirstDirection = kLeft, + kLastDirection = kBottom +}; +enum EDirectionMask { + kLeftMask = 1 << kLeft, + kRightMask = 1 << kRight, + kTopMask = 1 << kTop, + kBottomMask = 1 << kBottom +}; + + // // message codes (trailing NUL is not part of code). in comments, $n // refers to the n'th argument (counting from one). message codes are diff --git a/synergy/synergy.dsp b/synergy/synergy.dsp index f7d26af0..eac75dde 100644 --- a/synergy/synergy.dsp +++ b/synergy/synergy.dsp @@ -155,6 +155,14 @@ SOURCE=.\IPrimaryScreenReceiver.h # End Source File # Begin Source File +SOURCE=.\IScreen.h +# End Source File +# Begin Source File + +SOURCE=.\IScreenEventHandler.h +# End Source File +# Begin Source File + SOURCE=.\IScreenReceiver.h # End Source File # Begin Source File