• Main Page
  • Classes
  • Files
  • File List

CMSWindowsDesks.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2012 Bolton Software Ltd.
00004  * Copyright (C) 2004 Chris Schoeneman
00005  * 
00006  * This package is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * found in the file COPYING that should have accompanied this file.
00009  * 
00010  * This package is distributed in the hope that it will be useful,
00011  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00013  * GNU General Public License for more details.
00014  *
00015  * You should have received a copy of the GNU General Public License
00016  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
00017  */
00018 
00019 #include "CMSWindowsDesks.h"
00020 #include "CMSWindowsScreen.h"
00021 #include "CSynergyHook.h"
00022 #include "IScreenSaver.h"
00023 #include "XScreen.h"
00024 #include "CLock.h"
00025 #include "CThread.h"
00026 #include "CLog.h"
00027 #include "IEventQueue.h"
00028 #include "IJob.h"
00029 #include "TMethodEventJob.h"
00030 #include "TMethodJob.h"
00031 #include "CArchMiscWindows.h"
00032 #include <malloc.h>
00033 #include "IEventQueue.h"
00034 
00035 // these are only defined when WINVER >= 0x0500
00036 #if !defined(SPI_GETMOUSESPEED)
00037 #define SPI_GETMOUSESPEED 112
00038 #endif
00039 #if !defined(SPI_SETMOUSESPEED)
00040 #define SPI_SETMOUSESPEED 113
00041 #endif
00042 #if !defined(SPI_GETSCREENSAVERRUNNING)
00043 #define SPI_GETSCREENSAVERRUNNING 114
00044 #endif
00045 
00046 // X button stuff
00047 #if !defined(WM_XBUTTONDOWN)
00048 #define WM_XBUTTONDOWN      0x020B
00049 #define WM_XBUTTONUP        0x020C
00050 #define WM_XBUTTONDBLCLK    0x020D
00051 #define WM_NCXBUTTONDOWN    0x00AB
00052 #define WM_NCXBUTTONUP      0x00AC
00053 #define WM_NCXBUTTONDBLCLK  0x00AD
00054 #define MOUSEEVENTF_XDOWN   0x0080
00055 #define MOUSEEVENTF_XUP     0x0100
00056 #define XBUTTON1            0x0001
00057 #define XBUTTON2            0x0002
00058 #endif
00059 #if !defined(VK_XBUTTON1)
00060 #define VK_XBUTTON1         0x05
00061 #define VK_XBUTTON2         0x06
00062 #endif
00063 
00064 // <unused>; <unused>
00065 #define SYNERGY_MSG_SWITCH          SYNERGY_HOOK_LAST_MSG + 1
00066 // <unused>; <unused>
00067 #define SYNERGY_MSG_ENTER           SYNERGY_HOOK_LAST_MSG + 2
00068 // <unused>; <unused>
00069 #define SYNERGY_MSG_LEAVE           SYNERGY_HOOK_LAST_MSG + 3
00070 // wParam = flags, HIBYTE(lParam) = virtual key, LOBYTE(lParam) = scan code
00071 #define SYNERGY_MSG_FAKE_KEY        SYNERGY_HOOK_LAST_MSG + 4
00072  // flags, XBUTTON id
00073 #define SYNERGY_MSG_FAKE_BUTTON     SYNERGY_HOOK_LAST_MSG + 5
00074 // x; y
00075 #define SYNERGY_MSG_FAKE_MOVE       SYNERGY_HOOK_LAST_MSG + 6
00076 // xDelta; yDelta
00077 #define SYNERGY_MSG_FAKE_WHEEL      SYNERGY_HOOK_LAST_MSG + 7
00078 // POINT*; <unused>
00079 #define SYNERGY_MSG_CURSOR_POS      SYNERGY_HOOK_LAST_MSG + 8
00080 // IKeyState*; <unused>
00081 #define SYNERGY_MSG_SYNC_KEYS       SYNERGY_HOOK_LAST_MSG + 9
00082 // install; <unused>
00083 #define SYNERGY_MSG_SCREENSAVER     SYNERGY_HOOK_LAST_MSG + 10
00084 // dx; dy
00085 #define SYNERGY_MSG_FAKE_REL_MOVE   SYNERGY_HOOK_LAST_MSG + 11
00086 // enable; <unused>
00087 #define SYNERGY_MSG_FAKE_INPUT      SYNERGY_HOOK_LAST_MSG + 12
00088 
00089 //
00090 // CMSWindowsDesks
00091 //
00092 
00093 CMSWindowsDesks::CMSWindowsDesks(
00094         bool isPrimary, bool noHooks, HINSTANCE hookLibrary,
00095         const IScreenSaver* screensaver, IEventQueue& eventQueue,
00096         IJob* updateKeys, bool stopOnDeskSwitch) :
00097     m_isPrimary(isPrimary),
00098     m_noHooks(noHooks),
00099     m_is95Family(CArchMiscWindows::isWindows95Family()),
00100     m_isModernFamily(CArchMiscWindows::isWindowsModern()),
00101     m_isOnScreen(m_isPrimary),
00102     m_x(0), m_y(0),
00103     m_w(0), m_h(0),
00104     m_xCenter(0), m_yCenter(0),
00105     m_multimon(false),
00106     m_timer(NULL),
00107     m_screensaver(screensaver),
00108     m_screensaverNotify(false),
00109     m_activeDesk(NULL),
00110     m_activeDeskName(),
00111     m_mutex(),
00112     m_deskReady(&m_mutex, false),
00113     m_updateKeys(updateKeys),
00114     m_eventQueue(eventQueue),
00115     m_stopOnDeskSwitch(stopOnDeskSwitch)
00116 {
00117     if (hookLibrary != NULL)
00118         queryHookLibrary(hookLibrary);
00119 
00120     m_cursor    = createBlankCursor();
00121     m_deskClass = createDeskWindowClass(m_isPrimary);
00122     m_keyLayout = GetKeyboardLayout(GetCurrentThreadId());
00123     resetOptions();
00124 }
00125 
00126 CMSWindowsDesks::~CMSWindowsDesks()
00127 {
00128     disable();
00129     destroyClass(m_deskClass);
00130     destroyCursor(m_cursor);
00131     delete m_updateKeys;
00132 }
00133 
00134 void
00135 CMSWindowsDesks::enable()
00136 {
00137     m_threadID = GetCurrentThreadId();
00138 
00139     // set the active desk and (re)install the hooks
00140     checkDesk();
00141 
00142     // install the desk timer.  this timer periodically checks
00143     // which desk is active and reinstalls the hooks as necessary.
00144     // we wouldn't need this if windows notified us of a desktop
00145     // change but as far as i can tell it doesn't.
00146     m_timer = m_eventQueue.newTimer(0.2, NULL);
00147     m_eventQueue.adoptHandler(CEvent::kTimer, m_timer,
00148                             new TMethodEventJob<CMSWindowsDesks>(
00149                                 this, &CMSWindowsDesks::handleCheckDesk));
00150 
00151     updateKeys();
00152 }
00153 
00154 void
00155 CMSWindowsDesks::disable()
00156 {
00157     // remove timer
00158     if (m_timer != NULL) {
00159         m_eventQueue.removeHandler(CEvent::kTimer, m_timer);
00160         m_eventQueue.deleteTimer(m_timer);
00161         m_timer = NULL;
00162     }
00163 
00164     // destroy desks
00165     removeDesks();
00166 
00167     m_isOnScreen = m_isPrimary;
00168 }
00169 
00170 void
00171 CMSWindowsDesks::enter()
00172 {
00173     sendMessage(SYNERGY_MSG_ENTER, 0, 0);
00174 }
00175 
00176 void
00177 CMSWindowsDesks::leave(HKL keyLayout)
00178 {
00179     sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)keyLayout, 0);
00180 }
00181 
00182 void
00183 CMSWindowsDesks::resetOptions()
00184 {
00185     m_leaveForegroundOption = false;
00186 }
00187 
00188 void
00189 CMSWindowsDesks::setOptions(const COptionsList& options)
00190 {
00191     for (UInt32 i = 0, n = (UInt32)options.size(); i < n; i += 2) {
00192         if (options[i] == kOptionWin32KeepForeground) {
00193             m_leaveForegroundOption = (options[i + 1] != 0);
00194             LOG((CLOG_DEBUG1 "%s the foreground window", m_leaveForegroundOption ? "don\'t grab" : "grab"));
00195         }
00196     }
00197 }
00198 
00199 void
00200 CMSWindowsDesks::updateKeys()
00201 {
00202     sendMessage(SYNERGY_MSG_SYNC_KEYS, 0, 0);
00203 }
00204 
00205 void
00206 CMSWindowsDesks::setShape(SInt32 x, SInt32 y,
00207                 SInt32 width, SInt32 height,
00208                 SInt32 xCenter, SInt32 yCenter, bool isMultimon)
00209 {
00210     m_x        = x;
00211     m_y        = y;
00212     m_w        = width;
00213     m_h        = height;
00214     m_xCenter  = xCenter;
00215     m_yCenter  = yCenter;
00216     m_multimon = isMultimon;
00217 }
00218 
00219 void
00220 CMSWindowsDesks::installScreensaverHooks(bool install)
00221 {
00222     if (m_isPrimary && m_screensaverNotify != install) {
00223         m_screensaverNotify = install;
00224         sendMessage(SYNERGY_MSG_SCREENSAVER, install, 0);
00225     }
00226 }
00227 
00228 void
00229 CMSWindowsDesks::fakeInputBegin()
00230 {
00231     sendMessage(SYNERGY_MSG_FAKE_INPUT, 1, 0);
00232 }
00233 
00234 void
00235 CMSWindowsDesks::fakeInputEnd()
00236 {
00237     sendMessage(SYNERGY_MSG_FAKE_INPUT, 0, 0);
00238 }
00239 
00240 void
00241 CMSWindowsDesks::getCursorPos(SInt32& x, SInt32& y) const
00242 {
00243     POINT pos;
00244     sendMessage(SYNERGY_MSG_CURSOR_POS, reinterpret_cast<WPARAM>(&pos), 0);
00245     x = pos.x;
00246     y = pos.y;
00247 }
00248 
00249 void
00250 CMSWindowsDesks::fakeKeyEvent(
00251                 KeyButton button, UINT virtualKey,
00252                 bool press, bool /*isAutoRepeat*/) const
00253 {
00254     // win 95 family doesn't understand handed modifier virtual keys
00255     if (m_is95Family) {
00256         switch (virtualKey) {
00257         case VK_LSHIFT:
00258         case VK_RSHIFT:
00259             virtualKey = VK_SHIFT;
00260             break;
00261 
00262         case VK_LCONTROL:
00263         case VK_RCONTROL:
00264             virtualKey = VK_CONTROL;
00265             break;
00266 
00267         case VK_LMENU:
00268         case VK_RMENU:
00269             virtualKey = VK_MENU;
00270             break;
00271         }
00272     }
00273 
00274     // synthesize event
00275     DWORD flags = 0;
00276     if (((button & 0x100u) != 0)) {
00277         flags |= KEYEVENTF_EXTENDEDKEY;
00278     }
00279     if (!press) {
00280         flags |= KEYEVENTF_KEYUP;
00281     }
00282     sendMessage(SYNERGY_MSG_FAKE_KEY, flags,
00283                             MAKEWORD(static_cast<BYTE>(button & 0xffu),
00284                                 static_cast<BYTE>(virtualKey & 0xffu)));
00285 }
00286 
00287 void
00288 CMSWindowsDesks::fakeMouseButton(ButtonID button, bool press)
00289 {
00290     // the system will swap the meaning of left/right for us if
00291     // the user has configured a left-handed mouse but we don't
00292     // want it to swap since we want the handedness of the
00293     // server's mouse.  so pre-swap for a left-handed mouse.
00294     if (GetSystemMetrics(SM_SWAPBUTTON)) {
00295         switch (button) {
00296         case kButtonLeft:
00297             button = kButtonRight;
00298             break;
00299 
00300         case kButtonRight:
00301             button = kButtonLeft;
00302             break;
00303         }
00304     }
00305 
00306     // map button id to button flag and button data
00307     DWORD data = 0;
00308     DWORD flags;
00309     switch (button) {
00310     case kButtonLeft:
00311         flags = press ? MOUSEEVENTF_LEFTDOWN : MOUSEEVENTF_LEFTUP;
00312         break;
00313 
00314     case kButtonMiddle:
00315         flags = press ? MOUSEEVENTF_MIDDLEDOWN : MOUSEEVENTF_MIDDLEUP;
00316         break;
00317 
00318     case kButtonRight:
00319         flags = press ? MOUSEEVENTF_RIGHTDOWN : MOUSEEVENTF_RIGHTUP;
00320         break;
00321 
00322     case kButtonExtra0 + 0:
00323         data = XBUTTON1;
00324         flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
00325         break;
00326 
00327     case kButtonExtra0 + 1:
00328         data = XBUTTON2;
00329         flags = press ? MOUSEEVENTF_XDOWN : MOUSEEVENTF_XUP;
00330         break;
00331 
00332     default:
00333         return;
00334     }
00335 
00336     // do it
00337     sendMessage(SYNERGY_MSG_FAKE_BUTTON, flags, data);
00338 }
00339 
00340 void
00341 CMSWindowsDesks::fakeMouseMove(SInt32 x, SInt32 y) const
00342 {
00343     sendMessage(SYNERGY_MSG_FAKE_MOVE,
00344                             static_cast<WPARAM>(x),
00345                             static_cast<LPARAM>(y));
00346 }
00347 
00348 void
00349 CMSWindowsDesks::fakeMouseRelativeMove(SInt32 dx, SInt32 dy) const
00350 {
00351     sendMessage(SYNERGY_MSG_FAKE_REL_MOVE,
00352                             static_cast<WPARAM>(dx),
00353                             static_cast<LPARAM>(dy));
00354 }
00355 
00356 void
00357 CMSWindowsDesks::fakeMouseWheel(SInt32 xDelta, SInt32 yDelta) const
00358 {
00359     sendMessage(SYNERGY_MSG_FAKE_WHEEL, xDelta, yDelta);
00360 }
00361 
00362 void
00363 CMSWindowsDesks::sendMessage(UINT msg, WPARAM wParam, LPARAM lParam) const
00364 {
00365     if (m_activeDesk != NULL && m_activeDesk->m_window != NULL) {
00366         PostThreadMessage(m_activeDesk->m_threadID, msg, wParam, lParam);
00367         waitForDesk();
00368     }
00369 }
00370 
00371 void
00372 CMSWindowsDesks::queryHookLibrary(HINSTANCE hookLibrary)
00373 {
00374     // look up functions
00375     if (m_isPrimary && !m_noHooks) {
00376         m_install   = (InstallFunc)GetProcAddress(hookLibrary, "install");
00377         m_uninstall = (UninstallFunc)GetProcAddress(hookLibrary, "uninstall");
00378         m_installScreensaver   =
00379                   (InstallScreenSaverFunc)GetProcAddress(
00380                                 hookLibrary, "installScreenSaver");
00381         m_uninstallScreensaver =
00382                   (UninstallScreenSaverFunc)GetProcAddress(
00383                                 hookLibrary, "uninstallScreenSaver");
00384         if (m_install              == NULL ||
00385             m_uninstall            == NULL ||
00386             m_installScreensaver   == NULL ||
00387             m_uninstallScreensaver == NULL) {
00388             LOG((CLOG_ERR "Invalid hook library"));
00389             throw XScreenOpenFailure();
00390         }
00391     }
00392     else {
00393         m_install              = NULL;
00394         m_uninstall            = NULL;
00395         m_installScreensaver   = NULL;
00396         m_uninstallScreensaver = NULL;
00397     }
00398 }
00399 
00400 HCURSOR
00401 CMSWindowsDesks::createBlankCursor() const
00402 {
00403     // create a transparent cursor
00404     int cw = GetSystemMetrics(SM_CXCURSOR);
00405     int ch = GetSystemMetrics(SM_CYCURSOR);
00406     UInt8* cursorAND = new UInt8[ch * ((cw + 31) >> 2)];
00407     UInt8* cursorXOR = new UInt8[ch * ((cw + 31) >> 2)];
00408     memset(cursorAND, 0xff, ch * ((cw + 31) >> 2));
00409     memset(cursorXOR, 0x00, ch * ((cw + 31) >> 2));
00410     HCURSOR c = CreateCursor(CMSWindowsScreen::getWindowInstance(),
00411                             0, 0, cw, ch, cursorAND, cursorXOR);
00412     delete[] cursorXOR;
00413     delete[] cursorAND;
00414     return c;
00415 }
00416 
00417 void
00418 CMSWindowsDesks::destroyCursor(HCURSOR cursor) const
00419 {
00420     if (cursor != NULL) {
00421         DestroyCursor(cursor);
00422     }
00423 }
00424 
00425 ATOM
00426 CMSWindowsDesks::createDeskWindowClass(bool isPrimary) const
00427 {
00428     WNDCLASSEX classInfo;
00429     classInfo.cbSize        = sizeof(classInfo);
00430     classInfo.style         = CS_DBLCLKS | CS_NOCLOSE;
00431     classInfo.lpfnWndProc   = isPrimary ?
00432                                 &CMSWindowsDesks::primaryDeskProc :
00433                                 &CMSWindowsDesks::secondaryDeskProc;
00434     classInfo.cbClsExtra    = 0;
00435     classInfo.cbWndExtra    = 0;
00436     classInfo.hInstance     = CMSWindowsScreen::getWindowInstance();
00437     classInfo.hIcon         = NULL;
00438     classInfo.hCursor       = m_cursor;
00439     classInfo.hbrBackground = NULL;
00440     classInfo.lpszMenuName  = NULL;
00441     classInfo.lpszClassName = "SynergyDesk";
00442     classInfo.hIconSm       = NULL;
00443     return RegisterClassEx(&classInfo);
00444 }
00445 
00446 void
00447 CMSWindowsDesks::destroyClass(ATOM windowClass) const
00448 {
00449     if (windowClass != 0) {
00450         UnregisterClass(reinterpret_cast<LPCTSTR>(windowClass),
00451                             CMSWindowsScreen::getWindowInstance());
00452     }
00453 }
00454 
00455 HWND
00456 CMSWindowsDesks::createWindow(ATOM windowClass, const char* name) const
00457 {
00458     HWND window = CreateWindowEx(WS_EX_TOPMOST |
00459                                     WS_EX_TRANSPARENT |
00460                                     WS_EX_TOOLWINDOW,
00461                                 reinterpret_cast<LPCTSTR>(windowClass),
00462                                 name,
00463                                 WS_POPUP,
00464                                 0, 0, 1, 1,
00465                                 NULL, NULL,
00466                                 CMSWindowsScreen::getWindowInstance(),
00467                                 NULL);
00468     if (window == NULL) {
00469         LOG((CLOG_ERR "failed to create window: %d", GetLastError()));
00470         throw XScreenOpenFailure();
00471     }
00472     return window;
00473 }
00474 
00475 void
00476 CMSWindowsDesks::destroyWindow(HWND hwnd) const
00477 {
00478     if (hwnd != NULL) {
00479         DestroyWindow(hwnd);
00480     }
00481 }
00482 
00483 LRESULT CALLBACK
00484 CMSWindowsDesks::primaryDeskProc(
00485                 HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
00486 {
00487     return DefWindowProc(hwnd, msg, wParam, lParam);
00488 }
00489 
00490 LRESULT CALLBACK
00491 CMSWindowsDesks::secondaryDeskProc(
00492                 HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
00493 {
00494     // would like to detect any local user input and hide the hider
00495     // window but for now we just detect mouse motion.
00496     bool hide = false;
00497     switch (msg) {
00498     case WM_MOUSEMOVE:
00499         if (LOWORD(lParam) != 0 || HIWORD(lParam) != 0) {
00500             hide = true;
00501         }
00502         break;
00503     }
00504 
00505     if (hide && IsWindowVisible(hwnd)) {
00506         ReleaseCapture();
00507         SetWindowPos(hwnd, HWND_BOTTOM, 0, 0, 0, 0,
00508                             SWP_NOMOVE | SWP_NOSIZE |
00509                             SWP_NOACTIVATE | SWP_HIDEWINDOW);
00510     }
00511 
00512     return DefWindowProc(hwnd, msg, wParam, lParam);
00513 }
00514 
00515 void
00516 CMSWindowsDesks::deskMouseMove(SInt32 x, SInt32 y) const
00517 {
00518     // motion is simple (i.e. it's on the primary monitor) if there
00519     // is only one monitor.  it's also simple if we're not on the
00520     // windows 95 family since those platforms don't have a broken
00521     // mouse_event() function (see the comment below).
00522     bool simple = (!m_multimon || !m_is95Family);
00523     if (!simple) {
00524         // also simple if motion is within the primary monitor
00525         simple = (x >= 0 && x < GetSystemMetrics(SM_CXSCREEN) &&
00526                   y >= 0 && y < GetSystemMetrics(SM_CYSCREEN));
00527     }
00528 
00529     // move the mouse directly to target position if motion is simple
00530     if (simple) {
00531         // when using absolute positioning with mouse_event(),
00532         // the normalized device coordinates range over only
00533         // the primary screen.
00534         SInt32 w = GetSystemMetrics(SM_CXSCREEN);
00535         SInt32 h = GetSystemMetrics(SM_CYSCREEN);
00536         mouse_event(MOUSEEVENTF_MOVE | MOUSEEVENTF_ABSOLUTE,
00537                                 (DWORD)((65535.0f * x) / (w - 1) + 0.5f),
00538                                 (DWORD)((65535.0f * y) / (h - 1) + 0.5f),
00539                                 0, 0);
00540     }
00541 
00542     // windows 98 and Me are broken.  you cannot set the absolute
00543     // position of the mouse except on the primary monitor but you
00544     // can do relative moves onto any monitor.  this is, in microsoft's
00545     // words, "by design."  apparently the designers of windows 2000
00546     // we're a little less lazy and did it right.
00547     //
00548     // microsoft recommends in Q193003 to absolute position the cursor
00549     // somewhere on the primary monitor then relative move to the
00550     // desired location.  this doesn't work for us because when the
00551     // user drags a scrollbar, a window, etc. it causes the dragged
00552     // item to jump back and forth between the position on the primary
00553     // monitor and the desired position.  while it always ends up in
00554     // the right place, the effect is disconcerting.
00555     //
00556     // instead we'll get the cursor's current position and do just a
00557     // relative move from there to the desired position.
00558     else {
00559         POINT pos;
00560         GetCursorPos(&pos);
00561         deskMouseRelativeMove(x - pos.x, y - pos.y);
00562     }
00563 }
00564 
00565 void
00566 CMSWindowsDesks::deskMouseRelativeMove(SInt32 dx, SInt32 dy) const
00567 {
00568     // relative moves are subject to cursor acceleration which we don't
00569     // want.so we disable acceleration, do the relative move, then
00570     // restore acceleration.  there's a slight chance we'll end up in
00571     // the wrong place if the user moves the cursor using this system's
00572     // mouse while simultaneously moving the mouse on the server
00573     // system.  that defeats the purpose of synergy so we'll assume
00574     // that won't happen.  even if it does, the next mouse move will
00575     // correct the position.
00576 
00577     // save mouse speed & acceleration
00578     int oldSpeed[4];
00579     bool accelChanged =
00580                 SystemParametersInfo(SPI_GETMOUSE,0, oldSpeed, 0) &&
00581                 SystemParametersInfo(SPI_GETMOUSESPEED, 0, oldSpeed + 3, 0);
00582 
00583     // use 1:1 motion
00584     if (accelChanged) {
00585         int newSpeed[4] = { 0, 0, 0, 1 };
00586         accelChanged =
00587                 SystemParametersInfo(SPI_SETMOUSE, 0, newSpeed, 0) ||
00588                 SystemParametersInfo(SPI_SETMOUSESPEED, 0, newSpeed + 3, 0);
00589     }
00590 
00591     // move relative to mouse position
00592     mouse_event(MOUSEEVENTF_MOVE, dx, dy, 0, 0);
00593 
00594     // restore mouse speed & acceleration
00595     if (accelChanged) {
00596         SystemParametersInfo(SPI_SETMOUSE, 0, oldSpeed, 0);
00597         SystemParametersInfo(SPI_SETMOUSESPEED, 0, oldSpeed + 3, 0);
00598     }
00599 }
00600 
00601 void
00602 CMSWindowsDesks::deskEnter(CDesk* desk)
00603 {
00604     if (!m_isPrimary) {
00605         ReleaseCapture();
00606     }
00607     ShowCursor(TRUE);
00608     SetWindowPos(desk->m_window, HWND_BOTTOM, 0, 0, 0, 0,
00609                             SWP_NOMOVE | SWP_NOSIZE |
00610                             SWP_NOACTIVATE | SWP_HIDEWINDOW);
00611 
00612     // restore the foreground window
00613     // XXX -- this raises the window to the top of the Z-order.  we
00614     // want it to stay wherever it was to properly support X-mouse
00615     // (mouse over activation) but i've no idea how to do that.
00616     // the obvious workaround of using SetWindowPos() to move it back
00617     // after being raised doesn't work.
00618     DWORD thisThread =
00619         GetWindowThreadProcessId(desk->m_window, NULL);
00620     DWORD thatThread =
00621         GetWindowThreadProcessId(desk->m_foregroundWindow, NULL);
00622     AttachThreadInput(thatThread, thisThread, TRUE);
00623     SetForegroundWindow(desk->m_foregroundWindow);
00624     AttachThreadInput(thatThread, thisThread, FALSE);
00625     EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE);
00626     desk->m_foregroundWindow = NULL;
00627 }
00628 
00629 void
00630 CMSWindowsDesks::deskLeave(CDesk* desk, HKL keyLayout)
00631 {
00632     ShowCursor(FALSE);
00633     if (m_isPrimary) {
00634         // map a window to hide the cursor and to use whatever keyboard
00635         // layout we choose rather than the keyboard layout of the last
00636         // active window.
00637         int x, y, w, h;
00638         if (desk->m_lowLevel) {
00639             // with a low level hook the cursor will never budge so
00640             // just a 1x1 window is sufficient.
00641             x = m_xCenter;
00642             y = m_yCenter;
00643             w = 1;
00644             h = 1;
00645         }
00646         else {
00647             // with regular hooks the cursor will jitter as it's moved
00648             // by the user then back to the center by us.  to be sure
00649             // we never lose it, cover all the monitors with the window.
00650             x = m_x;
00651             y = m_y;
00652             w = m_w;
00653             h = m_h;
00654         }
00655         SetWindowPos(desk->m_window, HWND_TOPMOST, x, y, w, h,
00656                             SWP_NOACTIVATE | SWP_SHOWWINDOW);
00657 
00658         // if not using low-level hooks we have to also activate the
00659         // window to ensure we don't lose keyboard focus.
00660         // FIXME -- see if this can be avoided.  if so then always
00661         // disable the window (see handling of SYNERGY_MSG_SWITCH).
00662         if (!desk->m_lowLevel) {
00663             SetActiveWindow(desk->m_window);
00664         }
00665 
00666         // if using low-level hooks then disable the foreground window
00667         // so it can't mess up any of our keyboard events.  the console
00668         // program, for example, will cause characters to be reported as
00669         // unshifted, regardless of the shift key state.  interestingly
00670         // we do see the shift key go down and up.
00671         //
00672         // note that we must enable the window to activate it and we
00673         // need to disable the window on deskEnter.
00674         else {
00675             desk->m_foregroundWindow = getForegroundWindow();
00676             if (desk->m_foregroundWindow != NULL) {
00677                 EnableWindow(desk->m_window, TRUE);
00678                 SetActiveWindow(desk->m_window);
00679                 DWORD thisThread =
00680                     GetWindowThreadProcessId(desk->m_window, NULL);
00681                 DWORD thatThread =
00682                     GetWindowThreadProcessId(desk->m_foregroundWindow, NULL);
00683                 AttachThreadInput(thatThread, thisThread, TRUE);
00684                 SetForegroundWindow(desk->m_window);
00685                 AttachThreadInput(thatThread, thisThread, FALSE);
00686             }
00687         }
00688 
00689         // switch to requested keyboard layout
00690         ActivateKeyboardLayout(keyLayout, 0);
00691     }
00692     else {
00693         // move hider window under the cursor center, raise, and show it
00694         SetWindowPos(desk->m_window, HWND_TOPMOST,
00695                             m_xCenter, m_yCenter, 1, 1,
00696                             SWP_NOACTIVATE | SWP_SHOWWINDOW);
00697 
00698         // watch for mouse motion.  if we see any then we hide the
00699         // hider window so the user can use the physically attached
00700         // mouse if desired.  we'd rather not capture the mouse but
00701         // we aren't notified when the mouse leaves our window.
00702         SetCapture(desk->m_window);
00703 
00704         // warp the mouse to the cursor center
00705         LOG((CLOG_DEBUG2 "warping cursor to center: %+d,%+d", m_xCenter, m_yCenter));
00706         deskMouseMove(m_xCenter, m_yCenter);
00707     }
00708 }
00709 
00710 void
00711 CMSWindowsDesks::deskThread(void* vdesk)
00712 {
00713     MSG msg;
00714 
00715     // use given desktop for this thread
00716     CDesk* desk              = reinterpret_cast<CDesk*>(vdesk);
00717     desk->m_threadID         = GetCurrentThreadId();
00718     desk->m_window           = NULL;
00719     desk->m_foregroundWindow = NULL;
00720     if (desk->m_desk != NULL && SetThreadDesktop(desk->m_desk) != 0) {
00721         // create a message queue
00722         PeekMessage(&msg, NULL, 0,0, PM_NOREMOVE);
00723 
00724         // create a window.  we use this window to hide the cursor.
00725         try {
00726             desk->m_window = createWindow(m_deskClass, "SynergyDesk");
00727             LOG((CLOG_DEBUG "desk %s window is 0x%08x", desk->m_name.c_str(), desk->m_window));
00728         }
00729         catch (...) {
00730             // ignore
00731             LOG((CLOG_DEBUG "can't create desk window for %s", desk->m_name.c_str()));
00732         }
00733     }
00734 
00735     // tell main thread that we're ready
00736     {
00737         CLock lock(&m_mutex);
00738         m_deskReady = true;
00739         m_deskReady.broadcast();
00740     }
00741 
00742     while (GetMessage(&msg, NULL, 0, 0)) {
00743         switch (msg.message) {
00744         default:
00745             TranslateMessage(&msg);
00746             DispatchMessage(&msg);
00747             continue;
00748 
00749         case SYNERGY_MSG_SWITCH:
00750             if (m_isPrimary && !m_noHooks) {
00751                 m_uninstall();
00752                 if (m_screensaverNotify) {
00753                     m_uninstallScreensaver();
00754                     m_installScreensaver();
00755                 }
00756                 switch (m_install()) {
00757                 case kHOOK_FAILED:
00758                     // we won't work on this desk
00759                     desk->m_lowLevel = false;
00760                     break;
00761 
00762                 case kHOOK_OKAY:
00763                     desk->m_lowLevel = false;
00764                     break;
00765 
00766                 case kHOOK_OKAY_LL:
00767                     desk->m_lowLevel = true;
00768                     break;
00769                 }
00770 
00771                 // a window on the primary screen with low-level hooks
00772                 // should never activate.
00773                 if (desk->m_window)
00774                     EnableWindow(desk->m_window, desk->m_lowLevel ? FALSE : TRUE);
00775             }
00776             break;
00777 
00778         case SYNERGY_MSG_ENTER:
00779             m_isOnScreen = true;
00780             deskEnter(desk);
00781             break;
00782 
00783         case SYNERGY_MSG_LEAVE:
00784             m_isOnScreen = false;
00785             m_keyLayout  = (HKL)msg.wParam;
00786             deskLeave(desk, m_keyLayout);
00787             break;
00788 
00789         case SYNERGY_MSG_FAKE_KEY:
00790             keybd_event(HIBYTE(msg.lParam), LOBYTE(msg.lParam), (DWORD)msg.wParam, 0);
00791             break;
00792 
00793         case SYNERGY_MSG_FAKE_BUTTON:
00794             if (msg.wParam != 0) {
00795                 mouse_event((DWORD)msg.wParam, 0, 0, (DWORD)msg.lParam, 0);
00796             }
00797             break;
00798 
00799         case SYNERGY_MSG_FAKE_MOVE:
00800             deskMouseMove(static_cast<SInt32>(msg.wParam),
00801                             static_cast<SInt32>(msg.lParam));
00802             break;
00803 
00804         case SYNERGY_MSG_FAKE_REL_MOVE:
00805             deskMouseRelativeMove(static_cast<SInt32>(msg.wParam),
00806                             static_cast<SInt32>(msg.lParam));
00807             break;
00808 
00809         case SYNERGY_MSG_FAKE_WHEEL:
00810             // XXX -- add support for x-axis scrolling
00811             if (msg.lParam != 0) {
00812                 mouse_event(MOUSEEVENTF_WHEEL, 0, 0, (DWORD)msg.lParam, 0);
00813             }
00814             break;
00815 
00816         case SYNERGY_MSG_CURSOR_POS: {
00817             POINT* pos = reinterpret_cast<POINT*>(msg.wParam);
00818             if (!GetCursorPos(pos)) {
00819                 pos->x = m_xCenter;
00820                 pos->y = m_yCenter;
00821             }
00822             break;
00823         }
00824 
00825         case SYNERGY_MSG_SYNC_KEYS:
00826             m_updateKeys->run();
00827             break;
00828 
00829         case SYNERGY_MSG_SCREENSAVER:
00830             if (!m_noHooks) {
00831                 if (msg.wParam != 0) {
00832                     m_installScreensaver();
00833                 }
00834                 else {
00835                     m_uninstallScreensaver();
00836                 }
00837             }
00838             break;
00839 
00840         case SYNERGY_MSG_FAKE_INPUT:
00841             keybd_event(SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY,
00842                                 SYNERGY_HOOK_FAKE_INPUT_SCANCODE,
00843                                 msg.wParam ? 0 : KEYEVENTF_KEYUP, 0);
00844             break;
00845         }
00846 
00847         // notify that message was processed
00848         CLock lock(&m_mutex);
00849         m_deskReady = true;
00850         m_deskReady.broadcast();
00851     }
00852 
00853     // clean up
00854     deskEnter(desk);
00855     if (desk->m_window != NULL) {
00856         DestroyWindow(desk->m_window);
00857     }
00858     if (desk->m_desk != NULL) {
00859         closeDesktop(desk->m_desk);
00860     }
00861 }
00862 
00863 CMSWindowsDesks::CDesk*
00864 CMSWindowsDesks::addDesk(const CString& name, HDESK hdesk)
00865 {
00866     CDesk* desk      = new CDesk;
00867     desk->m_name     = name;
00868     desk->m_desk     = hdesk;
00869     desk->m_targetID = GetCurrentThreadId();
00870     desk->m_thread   = new CThread(new TMethodJob<CMSWindowsDesks>(
00871                         this, &CMSWindowsDesks::deskThread, desk));
00872     waitForDesk();
00873     m_desks.insert(std::make_pair(name, desk));
00874     return desk;
00875 }
00876 
00877 void
00878 CMSWindowsDesks::removeDesks()
00879 {
00880     for (CDesks::iterator index = m_desks.begin();
00881                             index != m_desks.end(); ++index) {
00882         CDesk* desk = index->second;
00883         PostThreadMessage(desk->m_threadID, WM_QUIT, 0, 0);
00884         desk->m_thread->wait();
00885         delete desk->m_thread;
00886         delete desk;
00887     }
00888     m_desks.clear();
00889     m_activeDesk     = NULL;
00890     m_activeDeskName = "";
00891 }
00892 
00893 void
00894 CMSWindowsDesks::checkDesk()
00895 {
00896     // get current desktop.  if we already know about it then return.
00897     CDesk* desk;
00898     HDESK hdesk  = openInputDesktop();
00899     CString name = getDesktopName(hdesk);
00900     CDesks::const_iterator index = m_desks.find(name);
00901     if (index == m_desks.end()) {
00902         desk = addDesk(name, hdesk);
00903         // hold on to hdesk until thread exits so the desk can't
00904         // be removed by the system
00905     }
00906     else {
00907         closeDesktop(hdesk);
00908         desk = index->second;
00909     }
00910 
00911     // if we are told to shut down on desk switch, and this is not the 
00912     // first switch, then shut down.
00913     if (m_stopOnDeskSwitch && m_activeDesk != NULL && name != m_activeDeskName) {
00914         LOG((CLOG_DEBUG "shutting down because of desk switch to \"%s\"", name.c_str()));
00915         EVENTQUEUE->addEvent(CEvent(CEvent::kQuit));
00916         return;
00917     }
00918 
00919     // if active desktop changed then tell the old and new desk threads
00920     // about the change.  don't switch desktops when the screensaver is
00921     // active becaue we'd most likely switch to the screensaver desktop
00922     // which would have the side effect of forcing the screensaver to
00923     // stop.
00924     if (name != m_activeDeskName && !m_screensaver->isActive()) {
00925         // show cursor on previous desk
00926         bool wasOnScreen = m_isOnScreen;
00927         if (!wasOnScreen) {
00928             sendMessage(SYNERGY_MSG_ENTER, 0, 0);
00929         }
00930 
00931         // check for desk accessibility change.  we don't get events
00932         // from an inaccessible desktop so when we switch from an
00933         // inaccessible desktop to an accessible one we have to
00934         // update the keyboard state.
00935         LOG((CLOG_DEBUG "switched to desk \"%s\"", name.c_str()));
00936         bool syncKeys = false;
00937         bool isAccessible = isDeskAccessible(desk);
00938         if (isDeskAccessible(m_activeDesk) != isAccessible) {
00939             if (isAccessible) {
00940                 LOG((CLOG_DEBUG "desktop is now accessible"));
00941                 syncKeys = true;
00942             }
00943             else {
00944                 LOG((CLOG_DEBUG "desktop is now inaccessible"));
00945             }
00946         }
00947 
00948         // switch desk
00949         m_activeDesk     = desk;
00950         m_activeDeskName = name;
00951         sendMessage(SYNERGY_MSG_SWITCH, 0, 0);
00952 
00953         // hide cursor on new desk
00954         if (!wasOnScreen) {
00955             sendMessage(SYNERGY_MSG_LEAVE, (WPARAM)m_keyLayout, 0);
00956         }
00957 
00958         // update keys if necessary
00959         if (syncKeys) {
00960             updateKeys();
00961         }
00962     }
00963     else if (name != m_activeDeskName) {
00964         // screen saver might have started
00965         PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, TRUE, 0);
00966     }
00967 }
00968 
00969 bool
00970 CMSWindowsDesks::isDeskAccessible(const CDesk* desk) const
00971 {
00972     return (desk != NULL && desk->m_desk != NULL);
00973 }
00974 
00975 void
00976 CMSWindowsDesks::waitForDesk() const
00977 {
00978     CMSWindowsDesks* self = const_cast<CMSWindowsDesks*>(this);
00979 
00980     CLock lock(&m_mutex);
00981     while (!(bool)m_deskReady) {
00982         m_deskReady.wait();
00983     }
00984     self->m_deskReady = false;
00985 }
00986 
00987 void
00988 CMSWindowsDesks::handleCheckDesk(const CEvent&, void*)
00989 {
00990     checkDesk();
00991 
00992     // also check if screen saver is running if on a modern OS and
00993     // this is the primary screen.
00994     if (m_isPrimary && m_isModernFamily) {
00995         BOOL running;
00996         SystemParametersInfo(SPI_GETSCREENSAVERRUNNING, 0, &running, FALSE);
00997         PostThreadMessage(m_threadID, SYNERGY_MSG_SCREEN_SAVER, running, 0);
00998     }
00999 }
01000 
01001 HDESK
01002 CMSWindowsDesks::openInputDesktop()
01003 {
01004     if (m_is95Family) {
01005         // there's only one desktop on windows 95 et al.
01006         return GetThreadDesktop(GetCurrentThreadId());
01007     }
01008     else {
01009         return OpenInputDesktop(DF_ALLOWOTHERACCOUNTHOOK, TRUE,
01010                                 DESKTOP_CREATEWINDOW |
01011                                     DESKTOP_HOOKCONTROL |
01012                                     GENERIC_WRITE);
01013     }
01014 }
01015 
01016 void
01017 CMSWindowsDesks::closeDesktop(HDESK desk)
01018 {
01019     // on 95/98/me we don't need to close the desktop returned by
01020     // openInputDesktop().
01021     if (desk != NULL && !m_is95Family) {
01022         CloseDesktop(desk);
01023     }
01024 }
01025 
01026 CString
01027 CMSWindowsDesks::getDesktopName(HDESK desk)
01028 {
01029     if (desk == NULL) {
01030         return CString();
01031     }
01032     else if (m_is95Family) {
01033         return "desktop";
01034     }
01035     else {
01036         DWORD size;
01037         GetUserObjectInformation(desk, UOI_NAME, NULL, 0, &size);
01038         TCHAR* name = (TCHAR*)alloca(size + sizeof(TCHAR));
01039         GetUserObjectInformation(desk, UOI_NAME, name, size, &size);
01040         CString result(name);
01041         return result;
01042     }
01043 }
01044 
01045 HWND
01046 CMSWindowsDesks::getForegroundWindow() const
01047 {
01048     // Ideally we'd return NULL as much as possible, only returning
01049     // the actual foreground window when we know it's going to mess
01050     // up our keyboard input.  For now we'll just let the user
01051     // decide.
01052     if (m_leaveForegroundOption) {
01053         return NULL;
01054     }
01055     return GetForegroundWindow();
01056 }

Generated on Sat May 25 2013 00:00:04 for Synergy by  doxygen 1.7.1