• Main Page
  • Classes
  • Files
  • File List

CSynergyHook.cpp

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

Generated on Thu Jun 20 2013 00:00:05 for Synergy by  doxygen 1.7.1