Main Page | Class Hierarchy | Alphabetical List | Class List | File List | Class Members

CSynergyHook.cpp

00001 /*
00002  * synergy -- mouse and keyboard sharing utility
00003  * Copyright (C) 2002 Chris Schoeneman
00004  * 
00005  * This package is free software; you can redistribute it and/or
00006  * modify it under the terms of the GNU General Public License
00007  * found in the file COPYING that should have accompanied this file.
00008  * 
00009  * This package is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  */
00014 
00015 #include "CSynergyHook.h"
00016 #include "ProtocolTypes.h"
00017 #include <zmouse.h>
00018 #include <tchar.h>
00019  
00020 #if _MSC_VER >= 1400
00021 // VS2005 hack - we don't use assert here because we don't want to link with the CRT.
00022 #undef assert
00023 #if _DEBUG
00024 #define assert(_X_) if (!(_X_)) __debugbreak()
00025 #else
00026 #define assert(_X_) __noop()
00027 #endif
00028 // VS2005 is a bit more smart than VC6 and optimize simple copy loop to
00029 // intrinsic memcpy.
00030 #pragma function(memcpy)
00031 #endif
00032 
00033 //
00034 // debugging compile flag.  when not zero the server doesn't grab
00035 // the keyboard when the mouse leaves the server screen.  this
00036 // makes it possible to use the debugger (via the keyboard) when
00037 // all user input would normally be caught by the hook procedures.
00038 //
00039 #define NO_GRAB_KEYBOARD 0
00040 
00041 //
00042 // debugging compile flag.  when not zero the server will not
00043 // install low level hooks.
00044 //
00045 #define NO_LOWLEVEL_HOOKS 0
00046 
00047 //
00048 // extra mouse wheel stuff
00049 //
00050 
00051 enum EWheelSupport {
00052     kWheelNone,
00053     kWheelOld,
00054     kWheelWin2000,
00055     kWheelModern
00056 };
00057 
00058 // declare extended mouse hook struct.  useable on win2k
00059 typedef struct tagMOUSEHOOKSTRUCTWin2000 {
00060     MOUSEHOOKSTRUCT mhs;
00061     DWORD mouseData;
00062 } MOUSEHOOKSTRUCTWin2000;
00063 
00064 #if !defined(SM_MOUSEWHEELPRESENT)
00065 #define SM_MOUSEWHEELPRESENT 75
00066 #endif
00067 
00068 // X button stuff
00069 #if !defined(WM_XBUTTONDOWN)
00070 #define WM_XBUTTONDOWN      0x020B
00071 #define WM_XBUTTONUP        0x020C
00072 #define WM_XBUTTONDBLCLK    0x020D
00073 #define WM_NCXBUTTONDOWN    0x00AB
00074 #define WM_NCXBUTTONUP      0x00AC
00075 #define WM_NCXBUTTONDBLCLK  0x00AD
00076 #define MOUSEEVENTF_XDOWN   0x0080
00077 #define MOUSEEVENTF_XUP     0x0100
00078 #define XBUTTON1            0x0001
00079 #define XBUTTON2            0x0002
00080 #endif
00081 
00082 
00083 //
00084 // globals
00085 //
00086 
00087 #if defined(_MSC_VER)
00088 #pragma comment(linker, "-section:shared,rws")
00089 #pragma data_seg("shared")
00090 #endif
00091 // all data in this shared section *must* be initialized
00092 
00093 static HINSTANCE        g_hinstance       = NULL;
00094 static DWORD            g_processID       = 0;
00095 static EWheelSupport    g_wheelSupport    = kWheelNone;
00096 static UINT             g_wmMouseWheel    = 0;
00097 static DWORD            g_threadID        = 0;
00098 static HHOOK            g_keyboard        = NULL;
00099 static HHOOK            g_mouse           = NULL;
00100 static HHOOK            g_getMessage      = NULL;
00101 static HHOOK            g_keyboardLL      = NULL;
00102 static HHOOK            g_mouseLL         = NULL;
00103 static bool             g_screenSaver     = false;
00104 static EHookMode        g_mode            = kHOOK_DISABLE;
00105 static UInt32           g_zoneSides       = 0;
00106 static SInt32           g_zoneSize        = 0;
00107 static SInt32           g_xScreen         = 0;
00108 static SInt32           g_yScreen         = 0;
00109 static SInt32           g_wScreen         = 0;
00110 static SInt32           g_hScreen         = 0;
00111 static WPARAM           g_deadVirtKey     = 0;
00112 static WPARAM           g_deadRelease     = 0;
00113 static LPARAM           g_deadLParam      = 0;
00114 static BYTE             g_deadKeyState[256] = { 0 };
00115 static DWORD            g_hookThread      = 0;
00116 static DWORD            g_attachedThread  = 0;
00117 static bool             g_fakeInput       = false;
00118 
00119 #if defined(_MSC_VER)
00120 #pragma data_seg()
00121 #endif
00122 
00123 // keep linker quiet about floating point stuff.  we don't use any
00124 // floating point operations but our includes may define some
00125 // (unused) floating point values.
00126 #ifndef _DEBUG
00127 extern "C" {
00128 int _fltused=0;
00129 }
00130 #endif
00131 
00132 
00133 //
00134 // internal functions
00135 //
00136 
00137 static
00138 void
00139 detachThread()
00140 {
00141     if (g_attachedThread != 0 && g_hookThread != g_attachedThread) {
00142         AttachThreadInput(g_hookThread, g_attachedThread, FALSE);
00143         g_attachedThread = 0;
00144     }
00145 }
00146 
00147 static
00148 bool
00149 attachThreadToForeground()
00150 {
00151     // only attach threads if using low level hooks.  a low level hook
00152     // runs in the thread that installed the hook but we have to make
00153     // changes that require being attached to the target thread (which
00154     // should be the foreground window).  a regular hook runs in the
00155     // thread that just removed the event from its queue so we're
00156     // already in the right thread.
00157     if (g_hookThread != 0) {
00158         HWND window    = GetForegroundWindow();
00159         DWORD threadID = GetWindowThreadProcessId(window, NULL);
00160         // skip if no change
00161         if (g_attachedThread != threadID) {
00162             // detach from previous thread
00163             detachThread();
00164 
00165             // attach to new thread
00166             if (threadID != 0 && threadID != g_hookThread) {
00167                 AttachThreadInput(g_hookThread, threadID, TRUE);
00168                 g_attachedThread = threadID;
00169             }
00170             return true;
00171         }
00172     }
00173     return false;
00174 }
00175 
00176 #if !NO_GRAB_KEYBOARD
00177 static
00178 WPARAM
00179 makeKeyMsg(UINT virtKey, char c, bool noAltGr)
00180 {
00181     return MAKEWPARAM(MAKEWORD(virtKey & 0xff, (BYTE)c), noAltGr ? 1 : 0);
00182 }
00183 
00184 static
00185 void
00186 keyboardGetState(BYTE keys[256])
00187 {
00188     // we have to use GetAsyncKeyState() rather than GetKeyState() because
00189     // we don't pass through most keys so the event synchronous state
00190     // doesn't get updated.  we do that because certain modifier keys have
00191     // side effects, like alt and the windows key.
00192     SHORT key;
00193     for (int i = 0; i < 256; ++i) {
00194         key     = GetAsyncKeyState(i);
00195         keys[i] = (BYTE)((key < 0) ? 0x80u : 0);
00196     }
00197     key = GetKeyState(VK_CAPITAL);
00198     keys[VK_CAPITAL] = (BYTE)(((key < 0) ? 0x80 : 0) | (key & 1));
00199 }
00200 
00201 static
00202 bool
00203 doKeyboardHookHandler(WPARAM wParam, LPARAM lParam)
00204 {
00205     // check for special events indicating if we should start or stop
00206     // passing events through and not report them to the server.  this
00207     // is used to allow the server to synthesize events locally but
00208     // not pick them up as user events.
00209     if (wParam == SYNERGY_HOOK_FAKE_INPUT_VIRTUAL_KEY &&
00210         ((lParam >> 16) & 0xffu) == SYNERGY_HOOK_FAKE_INPUT_SCANCODE) {
00211         // update flag
00212         g_fakeInput = ((lParam & 0x80000000u) == 0);
00213         PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00214                                 0xff000000u | wParam, lParam);
00215 
00216         // discard event
00217         return true;
00218     }
00219 
00220     // if we're expecting fake input then just pass the event through
00221     // and do not forward to the server
00222     if (g_fakeInput) {
00223         PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00224                                 0xfe000000u | wParam, lParam);
00225         return false;
00226     }
00227 
00228     // VK_RSHIFT may be sent with an extended scan code but right shift
00229     // is not an extended key so we reset that bit.
00230     if (wParam == VK_RSHIFT) {
00231         lParam &= ~0x01000000u;
00232     }
00233 
00234     // tell server about event
00235     PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG, wParam, lParam);
00236 
00237     // ignore dead key release
00238     if ((g_deadVirtKey == wParam || g_deadRelease == wParam) &&
00239         (lParam & 0x80000000u) != 0) {
00240         g_deadRelease = 0;
00241         PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00242                         wParam | 0x04000000, lParam);
00243         return false;
00244     }
00245 
00246     // we need the keyboard state for ToAscii()
00247     BYTE keys[256];
00248     keyboardGetState(keys);
00249 
00250     // ToAscii() maps ctrl+letter to the corresponding control code
00251     // and ctrl+backspace to delete.  we don't want those translations
00252     // so clear the control modifier state.  however, if we want to
00253     // simulate AltGr (which is ctrl+alt) then we must not clear it.
00254     UINT control = keys[VK_CONTROL] | keys[VK_LCONTROL] | keys[VK_RCONTROL];
00255     UINT menu    = keys[VK_MENU] | keys[VK_LMENU] | keys[VK_RMENU];
00256     if ((control & 0x80) == 0 || (menu & 0x80) == 0) {
00257         keys[VK_LCONTROL] = 0;
00258         keys[VK_RCONTROL] = 0;
00259         keys[VK_CONTROL]  = 0;
00260     }
00261     else {
00262         keys[VK_LCONTROL] = 0x80;
00263         keys[VK_RCONTROL] = 0x80;
00264         keys[VK_CONTROL]  = 0x80;
00265         keys[VK_LMENU]    = 0x80;
00266         keys[VK_RMENU]    = 0x80;
00267         keys[VK_MENU]     = 0x80;
00268     }
00269 
00270     // ToAscii() needs to know if a menu is active for some reason.
00271     // we don't know and there doesn't appear to be any way to find
00272     // out.  so we'll just assume a menu is active if the menu key
00273     // is down.
00274     // FIXME -- figure out some way to check if a menu is active
00275     UINT flags = 0;
00276     if ((menu & 0x80) != 0)
00277         flags |= 1;
00278 
00279     // if we're on the server screen then just pass numpad keys with alt
00280     // key down as-is.  we won't pick up the resulting character but the
00281     // local app will.  if on a client screen then grab keys as usual;
00282     // if the client is a windows system it'll synthesize the expected
00283     // character.  if not then it'll probably just do nothing.
00284     if (g_mode != kHOOK_RELAY_EVENTS) {
00285         // we don't use virtual keys because we don't know what the
00286         // state of the numlock key is.  we'll hard code the scan codes
00287         // instead.  hopefully this works across all keyboards.
00288         UINT sc = (lParam & 0x01ff0000u) >> 16;
00289         if (menu &&
00290             (sc >= 0x47u && sc <= 0x52u && sc != 0x4au && sc != 0x4eu)) {
00291             return false;
00292         }
00293     }
00294 
00295     WORD c        = 0;
00296 
00297     // map the key event to a character.  we have to put the dead
00298     // key back first and this has the side effect of removing it.
00299     if (g_deadVirtKey != 0) {
00300         if(ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
00301                     g_deadKeyState, &c, flags) == 2)
00302         {
00303             // If ToAscii returned 2, it means that we accidentally removed
00304             // a double dead key instead of restoring it. Thus, we call
00305             // ToAscii again with the same parameters to restore the
00306             // internal dead key state.
00307             ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
00308                     g_deadKeyState, &c, flags);
00309             
00310             // We need to keep track of this because g_deadVirtKey will be
00311             // cleared later on; this would cause the dead key release to
00312             // incorrectly restore the dead key state.
00313             g_deadRelease = g_deadVirtKey;
00314         }
00315     }
00316     
00317     UINT scanCode = ((lParam & 0x10ff0000u) >> 16);
00318     int n         = ToAscii(wParam, scanCode, keys, &c, flags);
00319 
00320     // if mapping failed and ctrl and alt are pressed then try again
00321     // with both not pressed.  this handles the case where ctrl and
00322     // alt are being used as individual modifiers rather than AltGr.
00323     // we note that's the case in the message sent back to synergy
00324     // because there's no simple way to deduce it after the fact.
00325     // we have to put the dead key back first, if there was one.
00326     bool noAltGr = false;
00327     if (n == 0 && (control & 0x80) != 0 && (menu & 0x80) != 0) {
00328         noAltGr = true;
00329         PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00330                             wParam | 0x05000000, lParam);
00331         if (g_deadVirtKey != 0) {
00332             if(ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
00333                             g_deadKeyState, &c, flags) == 2)
00334             {
00335                 ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
00336                             g_deadKeyState, &c, flags);
00337                 g_deadRelease = g_deadVirtKey;
00338             }
00339         }
00340         BYTE keys2[256];
00341         for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
00342             keys2[i] = keys[i];
00343         }
00344         keys2[VK_LCONTROL] = 0;
00345         keys2[VK_RCONTROL] = 0;
00346         keys2[VK_CONTROL]  = 0;
00347         keys2[VK_LMENU]    = 0;
00348         keys2[VK_RMENU]    = 0;
00349         keys2[VK_MENU]     = 0;
00350         n = ToAscii(wParam, scanCode, keys2, &c, flags);
00351     }
00352 
00353     PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00354                             wParam | ((c & 0xff) << 8) |
00355                             ((n & 0xff) << 16) | 0x06000000,
00356                             lParam);
00357     WPARAM charAndVirtKey = 0;
00358     bool clearDeadKey = false;
00359     switch (n) {
00360     default:
00361         // key is a dead key
00362 
00363         if(lParam & 0x80000000u)
00364             // This handles the obscure situation where a key has been
00365             // pressed which is both a dead key and a normal character
00366             // depending on which modifiers have been pressed. We
00367             // break here to prevent it from being considered a dead
00368             // key.
00369             break;
00370 
00371         g_deadVirtKey = wParam;
00372         g_deadLParam  = lParam;
00373         for (size_t i = 0; i < sizeof(keys) / sizeof(keys[0]); ++i) {
00374             g_deadKeyState[i] = keys[i];
00375         }
00376         break;
00377 
00378     case 0:
00379         // key doesn't map to a character.  this can happen if
00380         // non-character keys are pressed after a dead key.
00381         charAndVirtKey = makeKeyMsg(wParam, (char)0, noAltGr);
00382         break;
00383 
00384     case 1:
00385         // key maps to a character composed with dead key
00386         charAndVirtKey = makeKeyMsg(wParam, (char)LOBYTE(c), noAltGr);
00387         clearDeadKey   = true;
00388         break;
00389 
00390     case 2: {
00391         // previous dead key not composed.  send a fake key press
00392         // and release for the dead key to our window.
00393         WPARAM deadCharAndVirtKey =
00394                             makeKeyMsg(g_deadVirtKey, (char)LOBYTE(c), noAltGr);
00395         PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
00396                             deadCharAndVirtKey, g_deadLParam & 0x7fffffffu);
00397         PostThreadMessage(g_threadID, SYNERGY_MSG_KEY,
00398                             deadCharAndVirtKey, g_deadLParam | 0x80000000u);
00399 
00400         // use uncomposed character
00401         charAndVirtKey = makeKeyMsg(wParam, (char)HIBYTE(c), noAltGr);
00402         clearDeadKey   = true;
00403         break;
00404     }
00405     }
00406 
00407     // put back the dead key, if any, for the application to use
00408     if (g_deadVirtKey != 0) {
00409         ToAscii(g_deadVirtKey, (g_deadLParam & 0x10ff0000u) >> 16,
00410                             g_deadKeyState, &c, flags);
00411     }
00412 
00413     // clear out old dead key state
00414     if (clearDeadKey) {
00415         g_deadVirtKey = 0;
00416         g_deadLParam  = 0;
00417     }
00418 
00419     // forward message to our window.  do this whether or not we're
00420     // forwarding events to clients because this'll keep our thread's
00421     // key state table up to date.  that's important for querying
00422     // the scroll lock toggle state.
00423     // XXX -- with hot keys for actions we may only need to do this when
00424     // forwarding.
00425     if (charAndVirtKey != 0) {
00426         PostThreadMessage(g_threadID, SYNERGY_MSG_DEBUG,
00427                             charAndVirtKey | 0x07000000, lParam);
00428         PostThreadMessage(g_threadID, SYNERGY_MSG_KEY, charAndVirtKey, lParam);
00429     }
00430 
00431     if (g_mode == kHOOK_RELAY_EVENTS) {
00432         // let certain keys pass through
00433         switch (wParam) {
00434         case VK_CAPITAL:
00435         case VK_NUMLOCK:
00436         case VK_SCROLL:
00437             // pass event on.  we want to let these through to
00438             // the window proc because otherwise the keyboard
00439             // lights may not stay synchronized.
00440             break;
00441 
00442         case VK_HANGUL:
00443             // pass these modifiers if using a low level hook, discard
00444             // them if not.
00445             if (g_hookThread == 0) {
00446                 return true;
00447             }
00448             break;
00449 
00450         default:
00451             // discard
00452             return true;
00453         }
00454     }
00455 
00456     return false;
00457 }
00458 
00459 static
00460 bool
00461 keyboardHookHandler(WPARAM wParam, LPARAM lParam)
00462 {
00463     attachThreadToForeground();
00464     return doKeyboardHookHandler(wParam, lParam);
00465 }
00466 #endif
00467 
00468 static
00469 bool
00470 doMouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
00471 {
00472     switch (wParam) {
00473     case WM_LBUTTONDOWN:
00474     case WM_MBUTTONDOWN:
00475     case WM_RBUTTONDOWN:
00476     case WM_XBUTTONDOWN:
00477     case WM_LBUTTONDBLCLK:
00478     case WM_MBUTTONDBLCLK:
00479     case WM_RBUTTONDBLCLK:
00480     case WM_XBUTTONDBLCLK:
00481     case WM_LBUTTONUP:
00482     case WM_MBUTTONUP:
00483     case WM_RBUTTONUP:
00484     case WM_XBUTTONUP:
00485     case WM_NCLBUTTONDOWN:
00486     case WM_NCMBUTTONDOWN:
00487     case WM_NCRBUTTONDOWN:
00488     case WM_NCXBUTTONDOWN:
00489     case WM_NCLBUTTONDBLCLK:
00490     case WM_NCMBUTTONDBLCLK:
00491     case WM_NCRBUTTONDBLCLK:
00492     case WM_NCXBUTTONDBLCLK:
00493     case WM_NCLBUTTONUP:
00494     case WM_NCMBUTTONUP:
00495     case WM_NCRBUTTONUP:
00496     case WM_NCXBUTTONUP:
00497         // always relay the event.  eat it if relaying.
00498         PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_BUTTON, wParam, data);
00499         return (g_mode == kHOOK_RELAY_EVENTS);
00500 
00501     case WM_MOUSEWHEEL:
00502         if (g_mode == kHOOK_RELAY_EVENTS) {
00503             // relay event
00504             PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_WHEEL, data, 0);
00505         }
00506         return (g_mode == kHOOK_RELAY_EVENTS);
00507 
00508     case WM_NCMOUSEMOVE:
00509     case WM_MOUSEMOVE:
00510         if (g_mode == kHOOK_RELAY_EVENTS) {
00511             // relay and eat event
00512             PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
00513             return true;
00514         }
00515         else if (g_mode == kHOOK_WATCH_JUMP_ZONE) {
00516             // low level hooks can report bogus mouse positions that are
00517             // outside of the screen.  jeez.  naturally we end up getting
00518             // fake motion in the other direction to get the position back
00519             // on the screen, which plays havoc with switch on double tap.
00520             // CServer deals with that.  we'll clamp positions onto the
00521             // screen.  also, if we discard events for positions outside
00522             // of the screen then the mouse appears to get a bit jerky
00523             // near the edge.  we can either accept that or pass the bogus
00524             // events.  we'll try passing the events.
00525             bool bogus = false;
00526             if (x < g_xScreen) {
00527                 x     = g_xScreen;
00528                 bogus = true;
00529             }
00530             else if (x >= g_xScreen + g_wScreen) {
00531                 x     = g_xScreen + g_wScreen - 1;
00532                 bogus = true;
00533             }
00534             if (y < g_yScreen) {
00535                 y     = g_yScreen;
00536                 bogus = true;
00537             }
00538             else if (y >= g_yScreen + g_hScreen) {
00539                 y     = g_yScreen + g_hScreen - 1;
00540                 bogus = true;
00541             }
00542 
00543             // check for mouse inside jump zone
00544             bool inside = false;
00545             if (!inside && (g_zoneSides & kLeftMask) != 0) {
00546                 inside = (x < g_xScreen + g_zoneSize);
00547             }
00548             if (!inside && (g_zoneSides & kRightMask) != 0) {
00549                 inside = (x >= g_xScreen + g_wScreen - g_zoneSize);
00550             }
00551             if (!inside && (g_zoneSides & kTopMask) != 0) {
00552                 inside = (y < g_yScreen + g_zoneSize);
00553             }
00554             if (!inside && (g_zoneSides & kBottomMask) != 0) {
00555                 inside = (y >= g_yScreen + g_hScreen - g_zoneSize);
00556             }
00557 
00558             // relay the event
00559             PostThreadMessage(g_threadID, SYNERGY_MSG_MOUSE_MOVE, x, y);
00560 
00561             // if inside and not bogus then eat the event
00562             return inside && !bogus;
00563         }
00564     }
00565 
00566     // pass the event
00567     return false;
00568 }
00569 
00570 static
00571 bool
00572 mouseHookHandler(WPARAM wParam, SInt32 x, SInt32 y, SInt32 data)
00573 {
00574 //  attachThreadToForeground();
00575     return doMouseHookHandler(wParam, x, y, data);
00576 }
00577 
00578 #if !NO_GRAB_KEYBOARD
00579 static
00580 LRESULT CALLBACK
00581 keyboardHook(int code, WPARAM wParam, LPARAM lParam)
00582 {
00583     if (code >= 0) {
00584         // handle the message
00585         if (keyboardHookHandler(wParam, lParam)) {
00586             return 1;
00587         }
00588     }
00589 
00590     return CallNextHookEx(g_keyboard, code, wParam, lParam);
00591 }
00592 #endif
00593 
00594 static
00595 LRESULT CALLBACK
00596 mouseHook(int code, WPARAM wParam, LPARAM lParam)
00597 {
00598     if (code >= 0) {
00599         // decode message
00600         const MOUSEHOOKSTRUCT* info = (const MOUSEHOOKSTRUCT*)lParam;
00601         SInt32 x = (SInt32)info->pt.x;
00602         SInt32 y = (SInt32)info->pt.y;
00603         SInt32 w = 0;
00604         if (wParam == WM_MOUSEWHEEL) {
00605             // win2k and other systems supporting WM_MOUSEWHEEL in
00606             // the mouse hook are gratuitously different (and poorly
00607             // documented).  if a low-level mouse hook is in place
00608             // it should capture these events so we'll never see
00609             // them.
00610             switch (g_wheelSupport) {
00611             case kWheelModern:
00612                 w = static_cast<SInt16>(LOWORD(info->dwExtraInfo));
00613                 break;
00614 
00615             case kWheelWin2000: {
00616                 const MOUSEHOOKSTRUCTWin2000* info2k =
00617                         (const MOUSEHOOKSTRUCTWin2000*)lParam;
00618                 w = static_cast<SInt16>(HIWORD(info2k->mouseData));
00619                 break;
00620             }
00621 
00622             default:
00623                 break;
00624             }
00625         }
00626 
00627         // handle the message.  note that we don't handle X buttons
00628         // here.  that's okay because they're only supported on
00629         // win2k and winxp and up and on those platforms we'll get
00630         // get the mouse events through the low level hook.
00631         if (mouseHookHandler(wParam, x, y, w)) {
00632             return 1;
00633         }
00634     }
00635 
00636     return CallNextHookEx(g_mouse, code, wParam, lParam);
00637 }
00638 
00639 static
00640 LRESULT CALLBACK
00641 getMessageHook(int code, WPARAM wParam, LPARAM lParam)
00642 {
00643     if (code >= 0) {
00644         if (g_screenSaver) {
00645             MSG* msg = reinterpret_cast<MSG*>(lParam);
00646             if (msg->message == WM_SYSCOMMAND &&
00647                 msg->wParam  == SC_SCREENSAVE) {
00648                 // broadcast screen saver started message
00649                 PostThreadMessage(g_threadID,
00650                                 SYNERGY_MSG_SCREEN_SAVER, TRUE, 0);
00651             }
00652         }
00653         if (g_mode == kHOOK_RELAY_EVENTS) {
00654             MSG* msg = reinterpret_cast<MSG*>(lParam);
00655             if (g_wheelSupport == kWheelOld && msg->message == g_wmMouseWheel) {
00656                 // post message to our window
00657                 PostThreadMessage(g_threadID,
00658                                 SYNERGY_MSG_MOUSE_WHEEL,
00659                                 static_cast<SInt16>(msg->wParam & 0xffffu), 0);
00660 
00661                 // zero out the delta in the message so it's (hopefully)
00662                 // ignored
00663                 msg->wParam = 0;
00664             }
00665         }
00666     }
00667 
00668     return CallNextHookEx(g_getMessage, code, wParam, lParam);
00669 }
00670 
00671 #if (_WIN32_WINNT >= 0x0400) && defined(_MSC_VER) && !NO_LOWLEVEL_HOOKS
00672 
00673 //
00674 // low-level keyboard hook -- this allows us to capture and handle
00675 // alt+tab, alt+esc, ctrl+esc, and windows key hot keys.  on the down
00676 // side, key repeats are not reported to us.
00677 //
00678 
00679 #if !NO_GRAB_KEYBOARD
00680 static
00681 LRESULT CALLBACK
00682 keyboardLLHook(int code, WPARAM wParam, LPARAM lParam)
00683 {
00684     if (code >= 0) {
00685         // decode the message
00686         KBDLLHOOKSTRUCT* info = reinterpret_cast<KBDLLHOOKSTRUCT*>(lParam);
00687         WPARAM wParam = info->vkCode;
00688         LPARAM lParam = 1;                          // repeat code
00689         lParam      |= (info->scanCode << 16);      // scan code
00690         if (info->flags & LLKHF_EXTENDED) {
00691             lParam  |= (1lu << 24);                 // extended key
00692         }
00693         if (info->flags & LLKHF_ALTDOWN) {
00694             lParam  |= (1lu << 29);                 // context code
00695         }
00696         if (info->flags & LLKHF_UP) {
00697             lParam  |= (1lu << 31);                 // transition
00698         }
00699         // FIXME -- bit 30 should be set if key was already down but
00700         // we don't know that info.  as a result we'll never generate
00701         // key repeat events.
00702 
00703         // handle the message
00704         if (keyboardHookHandler(wParam, lParam)) {
00705             return 1;
00706         }
00707     }
00708 
00709     return CallNextHookEx(g_keyboardLL, code, wParam, lParam);
00710 }
00711 #endif
00712 
00713 //
00714 // low-level mouse hook -- this allows us to capture and handle mouse
00715 // events very early.  the earlier the better.
00716 //
00717 
00718 static
00719 LRESULT CALLBACK
00720 mouseLLHook(int code, WPARAM wParam, LPARAM lParam)
00721 {
00722     if (code >= 0) {
00723         // decode the message
00724         MSLLHOOKSTRUCT* info = reinterpret_cast<MSLLHOOKSTRUCT*>(lParam);
00725         SInt32 x = static_cast<SInt32>(info->pt.x);
00726         SInt32 y = static_cast<SInt32>(info->pt.y);
00727         SInt32 w = static_cast<SInt16>(HIWORD(info->mouseData));
00728 
00729         // handle the message
00730         if (mouseHookHandler(wParam, x, y, w)) {
00731             return 1;
00732         }
00733     }
00734 
00735     return CallNextHookEx(g_mouseLL, code, wParam, lParam);
00736 }
00737 
00738 #endif
00739 
00740 static
00741 EWheelSupport
00742 getWheelSupport()
00743 {
00744     // get operating system
00745     OSVERSIONINFO info;
00746     info.dwOSVersionInfoSize = sizeof(info);
00747     if (!GetVersionEx(&info)) {
00748         return kWheelNone;
00749     }
00750 
00751     // see if modern wheel is present
00752     if (GetSystemMetrics(SM_MOUSEWHEELPRESENT)) {
00753         // note if running on win2k
00754         if (info.dwPlatformId   == VER_PLATFORM_WIN32_NT &&
00755             info.dwMajorVersion == 5 &&
00756             info.dwMinorVersion == 0) {
00757             return kWheelWin2000;
00758         }
00759         return kWheelModern;
00760     }
00761 
00762     // not modern.  see if we've got old-style support.
00763 #if defined(MSH_WHEELSUPPORT)
00764     UINT wheelSupportMsg    = RegisterWindowMessage(MSH_WHEELSUPPORT);
00765     HWND wheelSupportWindow = FindWindow(MSH_WHEELMODULE_CLASS,
00766                                         MSH_WHEELMODULE_TITLE);
00767     if (wheelSupportWindow != NULL && wheelSupportMsg != 0) {
00768         if (SendMessage(wheelSupportWindow, wheelSupportMsg, 0, 0) != 0) {
00769             g_wmMouseWheel = RegisterWindowMessage(MSH_MOUSEWHEEL);
00770             if (g_wmMouseWheel != 0) {
00771                 return kWheelOld;
00772             }
00773         }
00774     }
00775 #endif
00776 
00777     // assume modern.  we don't do anything special in this case
00778     // except respond to WM_MOUSEWHEEL messages.  GetSystemMetrics()
00779     // can apparently return FALSE even if a mouse wheel is present
00780     // though i'm not sure exactly when it does that (WinME returns
00781     // FALSE for my logitech USB trackball).
00782     return kWheelModern;
00783 }
00784 
00785 
00786 //
00787 // external functions
00788 //
00789 
00790 BOOL WINAPI
00791 DllMain(HINSTANCE instance, DWORD reason, LPVOID)
00792 {
00793     if (reason == DLL_PROCESS_ATTACH) {
00794         DisableThreadLibraryCalls(instance);
00795         if (g_processID == 0) {
00796             g_hinstance = instance;
00797             g_processID = GetCurrentProcessId();
00798         }
00799     }
00800     else if (reason == DLL_PROCESS_DETACH) {
00801         if (g_processID == GetCurrentProcessId()) {
00802             uninstall();
00803             uninstallScreenSaver();
00804             g_processID = 0;
00805             g_hinstance = NULL;
00806         }
00807     }
00808     return TRUE;
00809 }
00810 
00811 extern "C" {
00812 
00813 // VS2005 hack to not link with the CRT
00814 #if _MSC_VER >= 1400
00815 BOOL WINAPI _DllMainCRTStartup(
00816         HINSTANCE instance, DWORD reason, LPVOID lpreserved)
00817 {
00818   return DllMain(instance, reason, lpreserved);
00819 }
00820 
00821 // VS2005 is a bit more bright than VC6 and optimize simple copy loop to
00822 // intrinsic memcpy.
00823 void *  __cdecl memcpy(void * _Dst, const void * _Src, size_t _MaxCount)
00824 {
00825   void * _DstBackup = _Dst;
00826   switch (_MaxCount & 3) {
00827   case 3:
00828     ((char*)_Dst)[0] = ((char*)_Src)[0];
00829     ++(char*&)_Dst;
00830     ++(char*&)_Src;
00831     --_MaxCount;
00832   case 2:
00833     ((char*)_Dst)[0] = ((char*)_Src)[0];
00834     ++(char*&)_Dst;
00835     ++(char*&)_Src;
00836     --_MaxCount;
00837   case 1:
00838     ((char*)_Dst)[0] = ((char*)_Src)[0];
00839     ++(char*&)_Dst;
00840     ++(char*&)_Src;
00841     --_MaxCount;
00842     break;
00843   case 0:
00844     break;
00845 
00846   default:
00847     __assume(0);
00848     break;
00849   }
00850 
00851   // I think it's faster on intel to deference than modify the pointer.
00852   const size_t max = _MaxCount / sizeof(UINT_PTR);
00853   for (size_t i = 0; i < max; ++i) {
00854     ((UINT_PTR*)_Dst)[i] = ((UINT_PTR*)_Src)[i];
00855   }
00856 
00857   (UINT_PTR*&)_Dst += max;
00858   (UINT_PTR*&)_Src += max;
00859 
00860   switch (_MaxCount & 3) {
00861   case 3:
00862     ((char*)_Dst)[0] = ((char*)_Src)[0];
00863     ++(char*&)_Dst;
00864     ++(char*&)_Src;
00865   case 2:
00866     ((char*)_Dst)[0] = ((char*)_Src)[0];
00867     ++(char*&)_Dst;
00868     ++(char*&)_Src;
00869   case 1:
00870     ((char*)_Dst)[0] = ((char*)_Src)[0];
00871     ++(char*&)_Dst;
00872     ++(char*&)_Src;
00873     break;
00874   case 0:
00875     break;
00876 
00877   default:
00878     __assume(0);
00879     break;
00880   }
00881 
00882   return _DstBackup;
00883 }
00884 #endif
00885 
00886 int
00887 init(DWORD threadID)
00888 {
00889     assert(g_hinstance != NULL);
00890 
00891     // try to open process that last called init() to see if it's
00892     // still running or if it died without cleaning up.
00893     if (g_processID != 0 && g_processID != GetCurrentProcessId()) {
00894         HANDLE process = OpenProcess(STANDARD_RIGHTS_REQUIRED,
00895                                 FALSE, g_processID);
00896         if (process != NULL) {
00897             // old process (probably) still exists so refuse to
00898             // reinitialize this DLL (and thus steal it from the
00899             // old process).
00900             CloseHandle(process);
00901             return 0;
00902         }
00903 
00904         // clean up after old process.  the system should've already
00905         // removed the hooks so we just need to reset our state.
00906         g_hinstance       = GetModuleHandle(_T("synrgyhk"));
00907         g_processID       = GetCurrentProcessId();
00908         g_wheelSupport    = kWheelNone;
00909         g_threadID        = 0;
00910         g_keyboard        = NULL;
00911         g_mouse           = NULL;
00912         g_getMessage      = NULL;
00913         g_keyboardLL      = NULL;
00914         g_mouseLL         = NULL;
00915         g_screenSaver     = false;
00916     }
00917 
00918     // save thread id.  we'll post messages to this thread's
00919     // message queue.
00920     g_threadID  = threadID;
00921 
00922     // set defaults
00923     g_mode      = kHOOK_DISABLE;
00924     g_zoneSides = 0;
00925     g_zoneSize  = 0;
00926     g_xScreen   = 0;
00927     g_yScreen   = 0;
00928     g_wScreen   = 0;
00929     g_hScreen   = 0;
00930 
00931     return 1;
00932 }
00933 
00934 int
00935 cleanup(void)
00936 {
00937     assert(g_hinstance != NULL);
00938 
00939     if (g_processID == GetCurrentProcessId()) {
00940         g_threadID = 0;
00941     }
00942 
00943     return 1;
00944 }
00945 
00946 EHookResult
00947 install()
00948 {
00949     assert(g_hinstance  != NULL);
00950     assert(g_keyboard   == NULL);
00951     assert(g_mouse      == NULL);
00952     assert(g_getMessage == NULL || g_screenSaver);
00953 
00954     // must be initialized
00955     if (g_threadID == 0) {
00956         return kHOOK_FAILED;
00957     }
00958 
00959     // discard old dead keys
00960     g_deadVirtKey = 0;
00961     g_deadLParam  = 0;
00962 
00963     // reset fake input flag
00964     g_fakeInput = false;
00965 
00966     // check for mouse wheel support
00967     g_wheelSupport = getWheelSupport();
00968 
00969     // install GetMessage hook (unless already installed)
00970     if (g_wheelSupport == kWheelOld && g_getMessage == NULL) {
00971         g_getMessage = SetWindowsHookEx(WH_GETMESSAGE,
00972                                 &getMessageHook,
00973                                 g_hinstance,
00974                                 0);
00975     }
00976 
00977     // install low-level hooks.  we require that they both get installed.
00978 #if (_WIN32_WINNT >= 0x0400) && defined(_MSC_VER) && !NO_LOWLEVEL_HOOKS
00979     g_mouseLL = SetWindowsHookEx(WH_MOUSE_LL,
00980                                 &mouseLLHook,
00981                                 g_hinstance,
00982                                 0);
00983 #if !NO_GRAB_KEYBOARD
00984     g_keyboardLL = SetWindowsHookEx(WH_KEYBOARD_LL,
00985                                 &keyboardLLHook,
00986                                 g_hinstance,
00987                                 0);
00988     if (g_mouseLL == NULL || g_keyboardLL == NULL) {
00989         if (g_keyboardLL != NULL) {
00990             UnhookWindowsHookEx(g_keyboardLL);
00991             g_keyboardLL = NULL;
00992         }
00993         if (g_mouseLL != NULL) {
00994             UnhookWindowsHookEx(g_mouseLL);
00995             g_mouseLL = NULL;
00996         }
00997     }
00998 #endif
00999 #endif
01000 
01001     // install regular hooks
01002     if (g_mouseLL == NULL) {
01003         g_mouse = SetWindowsHookEx(WH_MOUSE,
01004                                 &mouseHook,
01005                                 g_hinstance,
01006                                 0);
01007     }
01008 #if !NO_GRAB_KEYBOARD
01009     if (g_keyboardLL == NULL) {
01010         g_keyboard = SetWindowsHookEx(WH_KEYBOARD,
01011                                 &keyboardHook,
01012                                 g_hinstance,
01013                                 0);
01014     }
01015 #endif
01016 
01017     // check that we got all the hooks we wanted
01018     if ((g_getMessage == NULL && g_wheelSupport == kWheelOld) ||
01019 #if !NO_GRAB_KEYBOARD
01020         (g_keyboardLL == NULL && g_keyboard     == NULL) ||
01021 #endif
01022         (g_mouseLL    == NULL && g_mouse        == NULL)) {
01023         uninstall();
01024         return kHOOK_FAILED;
01025     }
01026 
01027     if (g_keyboardLL != NULL || g_mouseLL != NULL) {
01028         g_hookThread = GetCurrentThreadId();
01029         return kHOOK_OKAY_LL;
01030     }
01031 
01032     return kHOOK_OKAY;
01033 }
01034 
01035 int
01036 uninstall(void)
01037 {
01038     assert(g_hinstance != NULL);
01039 
01040     // discard old dead keys
01041     g_deadVirtKey = 0;
01042     g_deadLParam  = 0;
01043 
01044     // detach from thread
01045     detachThread();
01046 
01047     // uninstall hooks
01048     if (g_keyboardLL != NULL) {
01049         UnhookWindowsHookEx(g_keyboardLL);
01050         g_keyboardLL = NULL;
01051     }
01052     if (g_mouseLL != NULL) {
01053         UnhookWindowsHookEx(g_mouseLL);
01054         g_mouseLL = NULL;
01055     }
01056     if (g_keyboard != NULL) {
01057         UnhookWindowsHookEx(g_keyboard);
01058         g_keyboard = NULL;
01059     }
01060     if (g_mouse != NULL) {
01061         UnhookWindowsHookEx(g_mouse);
01062         g_mouse = NULL;
01063     }
01064     if (g_getMessage != NULL && !g_screenSaver) {
01065         UnhookWindowsHookEx(g_getMessage);
01066         g_getMessage = NULL;
01067     }
01068     g_wheelSupport = kWheelNone;
01069 
01070     return 1;
01071 }
01072 
01073 int
01074 installScreenSaver(void)
01075 {
01076     assert(g_hinstance != NULL);
01077 
01078     // must be initialized
01079     if (g_threadID == 0) {
01080         return 0;
01081     }
01082 
01083     // generate screen saver messages
01084     g_screenSaver = true;
01085 
01086     // install hook unless it's already installed
01087     if (g_getMessage == NULL) {
01088         g_getMessage = SetWindowsHookEx(WH_GETMESSAGE,
01089                                 &getMessageHook,
01090                                 g_hinstance,
01091                                 0);
01092     }
01093 
01094     return (g_getMessage != NULL) ? 1 : 0;
01095 }
01096 
01097 int
01098 uninstallScreenSaver(void)
01099 {
01100     assert(g_hinstance != NULL);
01101 
01102     // uninstall hook unless the mouse wheel hook is installed
01103     if (g_getMessage != NULL && g_wheelSupport != kWheelOld) {
01104         UnhookWindowsHookEx(g_getMessage);
01105         g_getMessage = NULL;
01106     }
01107 
01108     // screen saver hook is no longer installed
01109     g_screenSaver = false;
01110 
01111     return 1;
01112 }
01113 
01114 void
01115 setSides(UInt32 sides)
01116 {
01117     g_zoneSides = sides;
01118 }
01119 
01120 void
01121 setZone(SInt32 x, SInt32 y, SInt32 w, SInt32 h, SInt32 jumpZoneSize)
01122 {
01123     g_zoneSize = jumpZoneSize;
01124     g_xScreen  = x;
01125     g_yScreen  = y;
01126     g_wScreen  = w;
01127     g_hScreen  = h;
01128 }
01129 
01130 void
01131 setMode(EHookMode mode)
01132 {
01133     if (mode == g_mode) {
01134         // no change
01135         return;
01136     }
01137     g_mode = mode;
01138 }
01139 
01140 }

Generated on Fri Nov 6 00:21:14 2009 for synergy-plus by  doxygen 1.3.9.1