├── .cvsignore ├── .gitignore ├── .project ├── CHANGELOG.txt ├── HookManager.py ├── LICENSE.txt ├── MANIFEST.in ├── README.md ├── README.txt ├── __init__.py ├── aa hook.py ├── cpyHook.i ├── doc.py ├── example.py └── setup.py /.cvsignore: -------------------------------------------------------------------------------- 1 | build 2 | dist 3 | _cpyHook.pyd 4 | cpyHook_wrap.c 5 | cpyHook.py 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.swp 2 | *.un~ 3 | *.pyc 4 | build/ 5 | dist/ 6 | cpyHook.py 7 | cpyHook_wrap.c 8 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | pyHook 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /CHANGELOG.txt: -------------------------------------------------------------------------------- 1 | pyHook Change Log 2 | 3 | ---------- 4 | 1.5.1 (2008-10-07) 5 | 6 | - Back out the bugfix for deadkeys, since it broke ascii conversion. 7 | - Allow the processing of negative ncode key events, because, why not? 8 | - Update the SWIG typemap syntax from a deprecated style to avoid warning during build. 9 | 10 | ---------- 11 | 1.6-pre (2008-07-20), not released 12 | 13 | - BUGFIX: deadkeys on international keyboards work correctly. 14 | - BUGFIX: pyHook can now be used in binaries built with py2exe (maybe this will 15 | work with PyInstaller too). 16 | 17 | ---------- 18 | 1.5.0 (1.5a) (2005-04-01) 19 | - Changed stateful key tracking to work better with SendKeys 20 | - Possibly fixed bug where weird chars were insert when Alt+Arrow key pressed 21 | 22 | ---------- 23 | 1.4.0 (2005-02-23) 24 | - Added documentation 25 | - Added KeyAll property to HookManager 26 | 27 | (2004-10-11) 28 | - Added support for translating virtual keycodes to ASCII characters when possible 29 | - Added support for stopping event propagation 30 | 31 | ---------- 32 | 1.3.0 (2004-09-13) 33 | - AA example was updated to work with the wx namespace 34 | - Added support for allowing/disallowing event propagation (see example.py) 35 | - Added a proper __init__.py to the package -------------------------------------------------------------------------------- /HookManager.py: -------------------------------------------------------------------------------- 1 | import cpyHook 2 | 3 | def GetKeyState(key_id): 4 | return cpyHook.cGetKeyState(key_id) 5 | 6 | class HookConstants: 7 | ''' 8 | Stores internal windows hook constants including hook types, mappings from virtual 9 | keycode name to value and value to name, and event type value to name. 10 | ''' 11 | WH_MIN = -1 12 | WH_MSGFILTER = -1 13 | WH_JOURNALRECORD = 0 14 | WH_JOURNALPLAYBACK = 1 15 | WH_KEYBOARD = 2 16 | WH_GETMESSAGE = 3 17 | WH_CALLWNDPROC = 4 18 | WH_CBT = 5 19 | WH_SYSMSGFILTER = 6 20 | WH_MOUSE = 7 21 | WH_HARDWARE = 8 22 | WH_DEBUG = 9 23 | WH_SHELL = 10 24 | WH_FOREGROUNDIDLE = 11 25 | WH_CALLWNDPROCRET = 12 26 | WH_KEYBOARD_LL = 13 27 | WH_MOUSE_LL = 14 28 | WH_MAX = 15 29 | 30 | WM_MOUSEFIRST = 0x0200 31 | WM_MOUSEMOVE = 0x0200 32 | WM_LBUTTONDOWN = 0x0201 33 | WM_LBUTTONUP = 0x0202 34 | WM_LBUTTONDBLCLK = 0x0203 35 | WM_RBUTTONDOWN =0x0204 36 | WM_RBUTTONUP = 0x0205 37 | WM_RBUTTONDBLCLK = 0x0206 38 | WM_MBUTTONDOWN = 0x0207 39 | WM_MBUTTONUP = 0x0208 40 | WM_MBUTTONDBLCLK = 0x0209 41 | WM_MOUSEWHEEL = 0x020A 42 | WM_MOUSELAST = 0x020A 43 | 44 | WM_KEYFIRST = 0x0100 45 | WM_KEYDOWN = 0x0100 46 | WM_KEYUP = 0x0101 47 | WM_CHAR = 0x0102 48 | WM_DEADCHAR = 0x0103 49 | WM_SYSKEYDOWN = 0x0104 50 | WM_SYSKEYUP = 0x0105 51 | WM_SYSCHAR = 0x0106 52 | WM_SYSDEADCHAR = 0x0107 53 | WM_KEYLAST = 0x0108 54 | 55 | 56 | #VK_0 thru VK_9 are the same as ASCII '0' thru '9' (0x30 -' : 0x39) 57 | #VK_A thru VK_Z are the same as ASCII 'A' thru 'Z' (0x41 -' : 0x5A) 58 | 59 | #virtual keycode constant names to virtual keycodes numerical id 60 | vk_to_id = {'VK_LBUTTON' : 0x01, 'VK_RBUTTON' : 0x02, 'VK_CANCEL' : 0x03, 'VK_MBUTTON' : 0x04, 61 | 'VK_BACK' : 0x08, 'VK_TAB' : 0x09, 'VK_CLEAR' : 0x0C, 'VK_RETURN' : 0x0D, 'VK_SHIFT' : 0x10, 62 | 'VK_CONTROL' : 0x11, 'VK_MENU' : 0x12, 'VK_PAUSE' : 0x13, 'VK_CAPITAL' : 0x14, 'VK_KANA' : 0x15, 63 | 'VK_HANGEUL' : 0x15, 'VK_HANGUL' : 0x15, 'VK_JUNJA' : 0x17, 'VK_FINAL' : 0x18, 'VK_HANJA' : 0x19, 64 | 'VK_KANJI' : 0x19, 'VK_ESCAPE' : 0x1B, 'VK_CONVERT' : 0x1C, 'VK_NONCONVERT' : 0x1D, 'VK_ACCEPT' : 0x1E, 65 | 'VK_MODECHANGE' : 0x1F, 'VK_SPACE' : 0x20, 'VK_PRIOR' : 0x21, 'VK_NEXT' : 0x22, 'VK_END' : 0x23, 66 | 'VK_HOME' : 0x24, 'VK_LEFT' : 0x25, 'VK_UP' : 0x26, 'VK_RIGHT' : 0x27, 'VK_DOWN' : 0x28, 67 | 'VK_SELECT' : 0x29, 'VK_PRINT' : 0x2A, 'VK_EXECUTE' : 0x2B, 'VK_SNAPSHOT' : 0x2C, 'VK_INSERT' : 0x2D, 68 | 'VK_DELETE' : 0x2E, 'VK_HELP' : 0x2F, 'VK_LWIN' : 0x5B, 'VK_RWIN' : 0x5C, 'VK_APPS' : 0x5D, 69 | 'VK_NUMPAD0' : 0x60, 'VK_NUMPAD1' : 0x61, 'VK_NUMPAD2' : 0x62, 'VK_NUMPAD3' : 0x63, 'VK_NUMPAD4' : 0x64, 70 | 'VK_NUMPAD5' : 0x65, 'VK_NUMPAD6' : 0x66, 'VK_NUMPAD7' : 0x67, 'VK_NUMPAD8' : 0x68, 'VK_NUMPAD9' : 0x69, 71 | 'VK_MULTIPLY' : 0x6A, 'VK_ADD' : 0x6B, 'VK_SEPARATOR' : 0x6C, 'VK_SUBTRACT' : 0x6D, 'VK_DECIMAL' : 0x6E, 72 | 'VK_DIVIDE' : 0x6F ,'VK_F1' : 0x70, 'VK_F2' : 0x71, 'VK_F3' : 0x72, 'VK_F4' : 0x73, 'VK_F5' : 0x74, 73 | 'VK_F6' : 0x75, 'VK_F7' : 0x76, 'VK_F8' : 0x77, 'VK_F9' : 0x78, 'VK_F10' : 0x79, 'VK_F11' : 0x7A, 74 | 'VK_F12' : 0x7B, 'VK_F13' : 0x7C, 'VK_F14' : 0x7D, 'VK_F15' : 0x7E, 'VK_F16' : 0x7F, 'VK_F17' : 0x80, 75 | 'VK_F18' : 0x81, 'VK_F19' : 0x82, 'VK_F20' : 0x83, 'VK_F21' : 0x84, 'VK_F22' : 0x85, 'VK_F23' : 0x86, 76 | 'VK_F24' : 0x87, 'VK_NUMLOCK' : 0x90, 'VK_SCROLL' : 0x91, 'VK_LSHIFT' : 0xA0, 'VK_RSHIFT' : 0xA1, 77 | 'VK_LCONTROL' : 0xA2, 'VK_RCONTROL' : 0xA3, 'VK_LMENU' : 0xA4, 'VK_RMENU' : 0xA5, 'VK_PROCESSKEY' : 0xE5, 78 | 'VK_ATTN' : 0xF6, 'VK_CRSEL' : 0xF7, 'VK_EXSEL' : 0xF8, 'VK_EREOF' : 0xF9, 'VK_PLAY' : 0xFA, 79 | 'VK_ZOOM' : 0xFB, 'VK_NONAME' : 0xFC, 'VK_PA1' : 0xFD, 'VK_OEM_CLEAR' : 0xFE, 'VK_BROWSER_BACK' : 0xA6, 80 | 'VK_BROWSER_FORWARD' : 0xA7, 'VK_BROWSER_REFRESH' : 0xA8, 'VK_BROWSER_STOP' : 0xA9, 'VK_BROWSER_SEARCH' : 0xAA, 81 | 'VK_BROWSER_FAVORITES' : 0xAB, 'VK_BROWSER_HOME' : 0xAC, 'VK_VOLUME_MUTE' : 0xAD, 'VK_VOLUME_DOWN' : 0xAE, 82 | 'VK_VOLUME_UP' : 0xAF, 'VK_MEDIA_NEXT_TRACK' : 0xB0, 'VK_MEDIA_PREV_TRACK' : 0xB1, 'VK_MEDIA_STOP' : 0xB2, 83 | 'VK_MEDIA_PLAY_PAUSE' : 0xB3, 'VK_LAUNCH_MAIL' : 0xB4, 'VK_LAUNCH_MEDIA_SELECT' : 0xB5, 'VK_LAUNCH_APP1' : 0xB6, 84 | 'VK_LAUNCH_APP2' : 0xB7, 'VK_OEM_1' : 0xBA, 'VK_OEM_PLUS' : 0xBB, 'VK_OEM_COMMA' : 0xBC, 'VK_OEM_MINUS' : 0xBD, 85 | 'VK_OEM_PERIOD' : 0xBE, 'VK_OEM_2' : 0xBF, 'VK_OEM_3' : 0xC0, 'VK_OEM_4' : 0xDB, 'VK_OEM_5' : 0xDC, 86 | 'VK_OEM_6' : 0xDD, 'VK_OEM_7' : 0xDE, 'VK_OEM_8' : 0xDF, 'VK_OEM_102' : 0xE2, 'VK_PROCESSKEY' : 0xE5, 87 | 'VK_PACKET' : 0xE7} 88 | 89 | #inverse mapping of keycodes 90 | id_to_vk = dict([(v,k) for k,v in vk_to_id.items()]) 91 | 92 | #message constants to message names 93 | msg_to_name = {WM_MOUSEMOVE : 'mouse move', WM_LBUTTONDOWN : 'mouse left down', 94 | WM_LBUTTONUP : 'mouse left up', WM_LBUTTONDBLCLK : 'mouse left double', 95 | WM_RBUTTONDOWN : 'mouse right down', WM_RBUTTONUP : 'mouse right up', 96 | WM_RBUTTONDBLCLK : 'mouse right double', WM_MBUTTONDOWN : 'mouse middle down', 97 | WM_MBUTTONUP : 'mouse middle up', WM_MBUTTONDBLCLK : 'mouse middle double', 98 | WM_MOUSEWHEEL : 'mouse wheel', WM_KEYDOWN : 'key down', 99 | WM_KEYUP : 'key up', WM_CHAR : 'key char', WM_DEADCHAR : 'key dead char', 100 | WM_SYSKEYDOWN : 'key sys down', WM_SYSKEYUP : 'key sys up', 101 | WM_SYSCHAR : 'key sys char', WM_SYSDEADCHAR : 'key sys dead char'} 102 | 103 | def MsgToName(cls, msg): 104 | ''' 105 | Class method. Converts a message value to message name. 106 | 107 | @param msg: Keyboard or mouse event message 108 | @type msg: integer 109 | @return: Name of the event 110 | @rtype: string 111 | ''' 112 | return HookConstants.msg_to_name.get(msg) 113 | 114 | def VKeyToID(cls, vkey): 115 | ''' 116 | Class method. Converts a virtual keycode name to its value. 117 | 118 | @param vkey: Virtual keycode name 119 | @type vkey: string 120 | @return: Virtual keycode value 121 | @rtype: integer 122 | ''' 123 | return HookConstants.vk_to_id.get(vkey) 124 | 125 | def IDToName(cls, code): 126 | ''' 127 | Class method. Gets the keycode name for the given value. 128 | 129 | @param code: Virtual keycode value 130 | @type code: integer 131 | @return: Virtual keycode name 132 | @rtype: string 133 | ''' 134 | if (code >= 0x30 and code <= 0x39) or (code >= 0x41 and code <= 0x5A): 135 | text = chr(code) 136 | else: 137 | text = HookConstants.id_to_vk.get(code) 138 | if text is not None: 139 | text = text[3:].title() 140 | return text 141 | 142 | MsgToName=classmethod(MsgToName) 143 | IDToName=classmethod(IDToName) 144 | VKeyToID=classmethod(VKeyToID) 145 | 146 | class HookEvent(object): 147 | ''' 148 | Holds information about a general hook event. 149 | 150 | @ivar Message: Keyboard or mouse event message 151 | @type Message: integer 152 | @ivar Time: Specifies the elapsed time, in milliseconds, from the time the system was started to the time the event was created (that is, placed in the thread's message queue). See the MSDN documentation for the GetMessageTime function for more detail. 153 | @type Time: integer 154 | @ivar Window: Window handle of the foreground window at the time of the event 155 | @type Window: integer 156 | @ivar WindowName: Name of the foreground window at the time of the event 157 | @type WindowName: string 158 | ''' 159 | def __init__(self, msg, time, hwnd, window_name): 160 | '''Initializes an event instance.''' 161 | self.Message = msg 162 | self.Time = time 163 | self.Window = hwnd 164 | self.WindowName = window_name 165 | 166 | def GetMessageName(self): 167 | ''' 168 | @return: Name of the event 169 | @rtype: string 170 | ''' 171 | return HookConstants.MsgToName(self.Message) 172 | MessageName = property(fget=GetMessageName) 173 | 174 | class MouseEvent(HookEvent): 175 | ''' 176 | Holds information about a mouse event. 177 | 178 | @ivar Position: Location of the mouse event on the screen 179 | @type Position: 2-tuple of integer 180 | @ivar Wheel: Positive if the wheel scrolls up, negative if down, zero otherwise 181 | @type Wheel: integer 182 | @ivar Injected: Was this event generated programmatically? 183 | @type Injected: boolean 184 | ''' 185 | def __init__(self, msg, x, y, data, flags, time, hwnd, window_name): 186 | '''Initializes an instance of the class.''' 187 | HookEvent.__init__(self, msg, time, hwnd, window_name) 188 | self.Position = (x,y) 189 | if data > 0: w = 1 190 | elif data < 0: w = -1 191 | else: w = 0 192 | self.Wheel = w 193 | self.Injected = flags & 0x01 194 | 195 | class KeyboardEvent(HookEvent): 196 | ''' 197 | Holds information about a keyboard event. 198 | 199 | @ivar KeyID: Virtual key code 200 | @type KeyID: integer 201 | @ivar ScanCode: Scan code 202 | @type ScanCode: integer 203 | @ivar Ascii: ASCII character code, if one exists 204 | @type Ascii: integer 205 | ''' 206 | def __init__(self, msg, vk_code, scan_code, ascii, flags, time, hwnd, window_name): 207 | '''Initializes an instance of the class.''' 208 | HookEvent.__init__(self, msg, time, hwnd, window_name) 209 | self.KeyID = vk_code 210 | self.ScanCode = scan_code 211 | self.Ascii = ascii 212 | self.flags = flags 213 | 214 | def GetKey(self): 215 | ''' 216 | @return: Name of the virtual keycode 217 | @rtype: string 218 | ''' 219 | return HookConstants.IDToName(self.KeyID) 220 | 221 | def IsExtended(self): 222 | ''' 223 | @return: Is this an extended key? 224 | @rtype: boolean 225 | ''' 226 | return self.flags & 0x01 227 | 228 | def IsInjected(self): 229 | ''' 230 | @return: Was this event generated programmatically? 231 | @rtype: boolean 232 | ''' 233 | return self.flags & 0x10 234 | 235 | def IsAlt(self): 236 | ''' 237 | @return: Was the alt key depressed? 238 | @rtype: boolean 239 | ''' 240 | return self.flags & 0x20 241 | 242 | def IsTransition(self): 243 | ''' 244 | @return: Is this a transition from up to down or vice versa? 245 | @rtype: boolean 246 | ''' 247 | return self.flags & 0x80 248 | 249 | Key = property(fget=GetKey) 250 | Extended = property(fget=IsExtended) 251 | Injected = property(fget=IsInjected) 252 | Alt = property(fget=IsAlt) 253 | Transition = property(fget=IsTransition) 254 | 255 | class HookManager(object): 256 | ''' 257 | Registers and manages callbacks for low level mouse and keyboard events. 258 | 259 | @ivar mouse_funcs: Callbacks for mouse events 260 | @type mouse_funcs: dictionary 261 | @ivar keyboard_funcs: Callbacks for keyboard events 262 | @type keyboard_funcs: dictionary 263 | @ivar mouse_hook: Is a mouse hook set? 264 | @type mouse_hook: boolean 265 | @ivar keyboard_hook: Is a keyboard hook set? 266 | @type keyboard_hook: boolean 267 | ''' 268 | def __init__(self): 269 | '''Initializes an instance by setting up an empty set of handlers.''' 270 | self.mouse_funcs = {} 271 | self.keyboard_funcs = {} 272 | 273 | self.mouse_hook = False 274 | self.keyboard_hook = False 275 | 276 | def __del__(self): 277 | '''Unhook all registered hooks.''' 278 | self.UnhookMouse() 279 | self.UnhookKeyboard() 280 | 281 | def HookMouse(self): 282 | '''Begins watching for mouse events.''' 283 | cpyHook.cSetHook(HookConstants.WH_MOUSE_LL, self.MouseSwitch) 284 | self.mouse_hook = True 285 | 286 | def HookKeyboard(self): 287 | '''Begins watching for keyboard events.''' 288 | cpyHook.cSetHook(HookConstants.WH_KEYBOARD_LL, self.KeyboardSwitch) 289 | self.keyboard_hook = True 290 | 291 | def UnhookMouse(self): 292 | '''Stops watching for mouse events.''' 293 | if self.mouse_hook: 294 | cpyHook.cUnhook(HookConstants.WH_MOUSE_LL) 295 | self.mouse_hook = False 296 | 297 | def UnhookKeyboard(self): 298 | '''Stops watching for keyboard events.''' 299 | if self.keyboard_hook: 300 | cpyHook.cUnhook(HookConstants.WH_KEYBOARD_LL) 301 | self.keyboard_hook = False 302 | 303 | def MouseSwitch(self, msg, x, y, data, flags, time, hwnd, window_name): 304 | ''' 305 | Passes a mouse event on to the appropriate handler if one is registered. 306 | 307 | @param msg: Message value 308 | @type msg: integer 309 | @param x: x-coordinate of the mouse event 310 | @type x: integer 311 | @param y: y-coordinate of the mouse event 312 | @type y: integer 313 | @param data: Data associated with the mouse event (scroll information) 314 | @type data: integer 315 | @param flags: Flags associated with the mouse event (injected or not) 316 | @type flags: integer 317 | @param time: Seconds since the epoch when the even current 318 | @type time: integer 319 | @param hwnd: Window handle of the foreground window at the time of the event 320 | @type hwnd: integer 321 | ''' 322 | event = MouseEvent(msg, x, y, data, flags, time, hwnd, window_name) 323 | func = self.mouse_funcs.get(msg) 324 | if func: 325 | return func(event) 326 | else: 327 | return True 328 | 329 | def KeyboardSwitch(self, msg, vk_code, scan_code, ascii, flags, time, hwnd, win_name): 330 | ''' 331 | Passes a keyboard event on to the appropriate handler if one is registered. 332 | 333 | @param msg: Message value 334 | @type msg: integer 335 | @param vk_code: The virtual keycode of the key 336 | @type vk_code: integer 337 | @param scan_code: The scan code of the key 338 | @type scan_code: integer 339 | @param ascii: ASCII numeric value for the key if available 340 | @type ascii: integer 341 | @param flags: Flags associated with the key event (injected or not, extended key, etc.) 342 | @type flags: integer 343 | @param time: Time since the epoch of the key event 344 | @type time: integer 345 | @param hwnd: Window handle of the foreground window at the time of the event 346 | @type hwnd: integer 347 | ''' 348 | event = KeyboardEvent(msg, vk_code, scan_code, ascii, flags, time, hwnd, win_name) 349 | func = self.keyboard_funcs.get(msg) 350 | if func: 351 | return func(event) 352 | else: 353 | return True 354 | 355 | def SubscribeMouseMove(self, func): 356 | ''' 357 | Registers the given function as the callback for this mouse event type. Use the 358 | MouseMove property as a shortcut. 359 | 360 | @param func: Callback function 361 | @type func: callable 362 | ''' 363 | if func is None: 364 | self.disconnect(self.mouse_funcs, HookConstants.WM_MOUSEMOVE) 365 | else: 366 | self.connect(self.mouse_funcs, HookConstants.WM_MOUSEMOVE, func) 367 | 368 | def SubscribeMouseLeftUp(self, func): 369 | ''' 370 | Registers the given function as the callback for this mouse event type. Use the 371 | MouseLeftUp property as a shortcut. 372 | 373 | @param func: Callback function 374 | @type func: callable 375 | ''' 376 | if func is None: 377 | self.disconnect(self.mouse_funcs, HookConstants.WM_LBUTTONUP) 378 | else: 379 | self.connect(self.mouse_funcs, HookConstants.WM_LBUTTONUP, func) 380 | 381 | def SubscribeMouseLeftDown(self, func): 382 | ''' 383 | Registers the given function as the callback for this mouse event type. Use the 384 | MouseLeftDown property as a shortcut. 385 | 386 | @param func: Callback function 387 | @type func: callable 388 | ''' 389 | if func is None: 390 | self.disconnect(self.mouse_funcs, HookConstants.WM_LBUTTONDOWN) 391 | else: 392 | self.connect(self.mouse_funcs, HookConstants.WM_LBUTTONDOWN, func) 393 | 394 | def SubscribeMouseLeftDbl(self, func): 395 | ''' 396 | Registers the given function as the callback for this mouse event type. Use the 397 | MouseLeftDbl property as a shortcut. 398 | 399 | @param func: Callback function 400 | @type func: callable 401 | ''' 402 | if func is None: 403 | self.disconnect(self.mouse_funcs, HookConstants.WM_LBUTTONDBLCLK) 404 | else: 405 | self.connect(self.mouse_funcs, HookConstants.WM_LBUTTONDBLCLK, func) 406 | 407 | def SubscribeMouseRightUp(self, func): 408 | ''' 409 | Registers the given function as the callback for this mouse event type. Use the 410 | MouseRightUp property as a shortcut. 411 | 412 | @param func: Callback function 413 | @type func: callable 414 | ''' 415 | if func is None: 416 | self.disconnect(self.mouse_funcs, HookConstants.WM_RBUTTONUP) 417 | else: 418 | self.connect(self.mouse_funcs, HookConstants.WM_RBUTTONUP, func) 419 | 420 | def SubscribeMouseRightDown(self, func): 421 | ''' 422 | Registers the given function as the callback for this mouse event type. Use the 423 | MouseRightDown property as a shortcut. 424 | 425 | @param func: Callback function 426 | @type func: callable 427 | ''' 428 | if func is None: 429 | self.disconnect(self.mouse_funcs, HookConstants.WM_RBUTTONDOWN) 430 | else: 431 | self.connect(self.mouse_funcs, HookConstants.WM_RBUTTONDOWN, func) 432 | 433 | def SubscribeMouseRightDbl(self, func): 434 | ''' 435 | Registers the given function as the callback for this mouse event type. Use the 436 | MouseRightDbl property as a shortcut. 437 | 438 | @param func: Callback function 439 | @type func: callable 440 | ''' 441 | if func is None: 442 | self.disconnect(self.mouse_funcs, HookConstants.WM_RBUTTONDBLCLK) 443 | else: 444 | self.connect(self.mouse_funcs, HookConstants.WM_RBUTTONDBLCLK, func) 445 | 446 | def SubscribeMouseMiddleUp(self, func): 447 | ''' 448 | Registers the given function as the callback for this mouse event type. Use the 449 | MouseMiddleUp property as a shortcut. 450 | 451 | @param func: Callback function 452 | @type func: callable 453 | ''' 454 | if func is None: 455 | self.disconnect(self.mouse_funcs, HookConstants.WM_MBUTTONUP) 456 | else: 457 | self.connect(self.mouse_funcs, HookConstants.WM_MBUTTONUP, func) 458 | 459 | def SubscribeMouseMiddleDown(self, func): 460 | ''' 461 | Registers the given function as the callback for this mouse event type. Use the 462 | MouseMiddleDown property as a shortcut. 463 | 464 | @param func: Callback function 465 | @type func: callable 466 | ''' 467 | if func is None: 468 | self.disconnect(self.mouse_funcs, HookConstants.WM_MBUTTONDOWN) 469 | else: 470 | self.connect(self.mouse_funcs, HookConstants.WM_MBUTTONDOWN, func) 471 | 472 | def SubscribeMouseMiddleDbl(self, func): 473 | ''' 474 | Registers the given function as the callback for this mouse event type. Use the 475 | MouseMiddleDbl property as a shortcut. 476 | 477 | @param func: Callback function 478 | @type func: callable 479 | ''' 480 | if func is None: 481 | self.disconnect(self.mouse_funcs, HookConstants.WM_MBUTTONDBLCLK) 482 | else: 483 | self.connect(self.mouse_funcs, HookConstants.WM_MBUTTONDBLCLK, func) 484 | 485 | def SubscribeMouseWheel(self, func): 486 | ''' 487 | Registers the given function as the callback for this mouse event type. Use the 488 | MouseWheel property as a shortcut. 489 | 490 | @param func: Callback function 491 | @type func: callable 492 | ''' 493 | if func is None: 494 | self.disconnect(self.mouse_funcs, HookConstants.WM_MOUSEWHEEL) 495 | else: 496 | self.connect(self.mouse_funcs, HookConstants.WM_MOUSEWHEEL, func) 497 | 498 | def SubscribeMouseAll(self, func): 499 | ''' 500 | Registers the given function as the callback for all mouse events. Use the 501 | MouseAll property as a shortcut. 502 | 503 | @param func: Callback function 504 | @type func: callable 505 | ''' 506 | self.SubscribeMouseMove(func) 507 | self.SubscribeMouseWheel(func) 508 | self.SubscribeMouseAllButtons(func) 509 | 510 | def SubscribeMouseAllButtons(self, func): 511 | ''' 512 | Registers the given function as the callback for all mouse button events. Use the 513 | MouseButtonAll property as a shortcut. 514 | 515 | @param func: Callback function 516 | @type func: callable 517 | ''' 518 | self.SubscribeMouseAllButtonsDown(func) 519 | self. SubscribeMouseAllButtonsUp(func) 520 | self.SubscribeMouseAllButtonsDbl(func) 521 | 522 | def SubscribeMouseAllButtonsDown(self, func): 523 | ''' 524 | Registers the given function as the callback for all mouse button down events. 525 | Use the MouseAllButtonsDown property as a shortcut. 526 | 527 | @param func: Callback function 528 | @type func: callable 529 | ''' 530 | self.SubscribeMouseLeftDown(func) 531 | self.SubscribeMouseRightDown(func) 532 | self.SubscribeMouseMiddleDown(func) 533 | 534 | def SubscribeMouseAllButtonsUp(self, func): 535 | ''' 536 | Registers the given function as the callback for all mouse button up events. 537 | Use the MouseAllButtonsUp property as a shortcut. 538 | 539 | @param func: Callback function 540 | @type func: callable 541 | ''' 542 | self.SubscribeMouseLeftUp(func) 543 | self.SubscribeMouseRightUp(func) 544 | self.SubscribeMouseMiddleUp(func) 545 | 546 | def SubscribeMouseAllButtonsDbl(self, func): 547 | ''' 548 | Registers the given function as the callback for all mouse button double click 549 | events. Use the MouseAllButtonsDbl property as a shortcut. 550 | 551 | @param func: Callback function 552 | @type func: callable 553 | ''' 554 | self.SubscribeMouseLeftDbl(func) 555 | self.SubscribeMouseRightDbl(func) 556 | self.SubscribeMouseMiddleDbl(func) 557 | 558 | def SubscribeKeyDown(self, func): 559 | ''' 560 | Registers the given function as the callback for this keyboard event type. 561 | Use the KeyDown property as a shortcut. 562 | 563 | @param func: Callback function 564 | @type func: callable 565 | ''' 566 | if func is None: 567 | self.disconnect(self.keyboard_funcs, HookConstants.WM_KEYDOWN) 568 | self.disconnect(self.keyboard_funcs, HookConstants.WM_SYSKEYDOWN) 569 | else: 570 | self.connect(self.keyboard_funcs, HookConstants.WM_KEYDOWN, func) 571 | self.connect(self.keyboard_funcs, HookConstants.WM_SYSKEYDOWN, func) 572 | 573 | def SubscribeKeyUp(self, func): 574 | ''' 575 | Registers the given function as the callback for this keyboard event type. 576 | Use the KeyUp property as a shortcut. 577 | 578 | @param func: Callback function 579 | @type func: callable 580 | ''' 581 | if func is None: 582 | self.disconnect(self.keyboard_funcs, HookConstants.WM_KEYUP) 583 | self.disconnect(self.keyboard_funcs, HookConstants.WM_SYSKEYUP) 584 | else: 585 | self.connect(self.keyboard_funcs, HookConstants.WM_KEYUP, func) 586 | self.connect(self.keyboard_funcs, HookConstants.WM_SYSKEYUP, func) 587 | 588 | def SubscribeKeyChar(self, func): 589 | ''' 590 | Registers the given function as the callback for this keyboard event type. 591 | Use the KeyChar property as a shortcut. 592 | 593 | B{Note}: this is currently non-functional, no WM_*CHAR messages are 594 | processed by the keyboard hook. 595 | 596 | @param func: Callback function 597 | @type func: callable 598 | ''' 599 | if func is None: 600 | self.disconnect(self.keyboard_funcs, HookConstants.WM_CHAR) 601 | self.disconnect(self.keyboard_funcs, HookConstants.WM_DEADCHAR) 602 | self.disconnect(self.keyboard_funcs, HookConstants.WM_SYSCHAR) 603 | self.disconnect(self.keyboard_funcs, HookConstants.WM_SYSDEADCHAR) 604 | else: 605 | self.connect(self.keyboard_funcs, HookConstants.WM_CHAR, func) 606 | self.connect(self.keyboard_funcs, HookConstants.WM_DEADCHAR, func) 607 | self.connect(self.keyboard_funcs, HookConstants.WM_SYSCHAR, func) 608 | self.connect(self.keyboard_funcs, HookConstants.WM_SYSDEADCHAR, func) 609 | 610 | def SubscribeKeyAll(self, func): 611 | ''' 612 | Registers the given function as the callback for all keyboard events. 613 | Use the KeyAll property as a shortcut. 614 | 615 | @param func: Callback function 616 | @type func: callable 617 | ''' 618 | self.SubscribeKeyDown(func) 619 | self.SubscribeKeyUp(func) 620 | self.SubscribeKeyChar(func) 621 | 622 | MouseAll = property(fset=SubscribeMouseAll) 623 | MouseAllButtons = property(fset=SubscribeMouseAllButtons) 624 | MouseAllButtonsUp = property(fset=SubscribeMouseAllButtonsUp) 625 | MouseAllButtonsDown = property(fset=SubscribeMouseAllButtonsDown) 626 | MouseAllButtonsDbl = property(fset=SubscribeMouseAllButtonsDbl) 627 | 628 | MouseWheel = property(fset=SubscribeMouseWheel) 629 | MouseMove = property(fset=SubscribeMouseMove) 630 | MouseLeftUp = property(fset=SubscribeMouseLeftUp) 631 | MouseLeftDown = property(fset=SubscribeMouseLeftDown) 632 | MouseLeftDbl = property(fset=SubscribeMouseLeftDbl) 633 | MouseRightUp = property(fset=SubscribeMouseRightUp) 634 | MouseRightDown = property(fset=SubscribeMouseRightDown) 635 | MouseRightDbl = property(fset=SubscribeMouseRightDbl) 636 | MouseMiddleUp = property(fset=SubscribeMouseMiddleUp) 637 | MouseMiddleDown = property(fset=SubscribeMouseMiddleDown) 638 | MouseMiddleDbl = property(fset=SubscribeMouseMiddleDbl) 639 | 640 | KeyUp = property(fset=SubscribeKeyUp) 641 | KeyDown = property(fset=SubscribeKeyDown) 642 | KeyChar = property(fset=SubscribeKeyChar) 643 | KeyAll = property(fset=SubscribeKeyAll) 644 | 645 | def connect(self, switch, id, func): 646 | ''' 647 | Registers a callback to the given function for the event with the given ID in the 648 | provided dictionary. Internal use only. 649 | 650 | @param switch: Collection of callbacks 651 | @type switch: dictionary 652 | @param id: Event type 653 | @type id: integer 654 | @param func: Callback function 655 | @type func: callable 656 | ''' 657 | switch[id] = func 658 | 659 | def disconnect(self, switch, id): 660 | ''' 661 | Unregisters a callback for the event with the given ID in the provided dictionary. 662 | Internal use only. 663 | 664 | @param switch: Collection of callbacks 665 | @type switch: dictionary 666 | @param id: Event type 667 | @type id: integer 668 | ''' 669 | try: 670 | del switch[id] 671 | except: 672 | pass -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2003 Peter Parente 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include HookManager.py 2 | include __init__.py 3 | include aa?hook.py 4 | include cpyHook.i 5 | include doc.py 6 | include example.py 7 | include setup.py 8 | include README.txt 9 | include CHANGELOG.txt 10 | include LICENSE.txt 11 | include MANIFEST.in -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Python3 fork of pyHook 2 | 3 | ## Introduction 4 | 5 | Copy from [official site](http://sourceforge.net/apps/mediawiki/pyhook/index.php?title=Main_Page): 6 | 7 | > pyHook is a python wrapper for global input hooks in Windows. Specifically it wraps the Windows SetWindowsHookEx API function using low-level keyboard (WH_KEYBOARD_LL) and mouse (WH_MOUSE_LL) hooks. 8 | > The pyHook package provides callbacks for global mouse and keyboard events in Windows. Python applications register can event handlers for user input events such as left mouse down, left mouse up, key down, etc. and set the keyboard and/or mouse hook. The underlying C library reports information like the time of the event, the name of the window in which the event occurred, the value of the event, any keyboard modifiers, etc. Events can be logged and/or filtered. 9 | 10 | ## Install 11 | 12 | ### Build with MSVC9 13 | 14 | Make sure you are under "Visual Studio 2008 Command Prompt". 15 | 16 | ``` 17 | python setup.py build_ext --swig=path-to-swig.exe 18 | pip install . 19 | ``` 20 | 21 | ## About this fork 22 | 23 | ### Unicode 24 | 25 | Fixed unicode decoding bug of window title. This bug may cause crashing on exit randomly. Usually with console output: 26 | 27 | > TypeError: MouseSwitch() takes exactly 9 arguments (1 given) 28 | 29 | or 30 | 31 | > TypeError: KeyboardSwitch() takes exactly 9 arguments (1 given) 32 | 33 | ### Freezing 34 | 35 | Original pyHook will cause "cannot find \_cpyHook module" error when using PyInstaller or cx-freeze to freeze python app. You need manually rename `pyHook._cpyHook` to `_cpyHook`. Now it's compatible with cx-freeze. 36 | -------------------------------------------------------------------------------- /README.txt: -------------------------------------------------------------------------------- 1 | See the website, http://pyhook.sourceforge.net/, for the latest updates to this information. 2 | 3 | Known bugs 4 | ---------- 5 | - PyInstaller can't build single-file executables using pyHook. This may be 6 | fixed in 1.5.1, but hasn't been tested. 7 | - pyHook is reported to break dead keys on non-US-english keyboards. 8 | - WM_CHAR messages are not intercepted by pyHook, even if SubscribeKeyChar() or 9 | SubscribeKeyAll() are used to set the callback function. 10 | 11 | Limitations 12 | ----------- 13 | - pyHook will not work on Win9x (no messages show up) as it uses hooks which 14 | are not present in Windows systems prior to NT 4.0 SP3. 15 | 16 | Website 17 | ------- 18 | Visit http://pyhook.sourceforge.net for binaries, documentation, and tutorials. 19 | 20 | Bug reports and feature requests should be reported via the Sourceforge page at 21 | https://sourceforge.net/projects/pyhook/ 22 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | from HookManager import * -------------------------------------------------------------------------------- /aa hook.py: -------------------------------------------------------------------------------- 1 | import wx 2 | import pyHook 3 | from pyAA import * 4 | 5 | class myFrame(wx.Frame): 6 | def __init__(self): 7 | wx.Frame.__init__(self, None, -1, 'My Frame') 8 | 9 | self.hm = pyHook.HookManager() 10 | self.hm.MouseAllButtonsDown = self.OnMouseEvent 11 | self.hm.KeyDown = self.OnKeyboardEvent 12 | 13 | self.hm.HookMouse() 14 | self.hm.HookKeyboard() 15 | 16 | wx.EVT_CLOSE(self, self.OnClose) 17 | 18 | def OnGetAO(self, event): 19 | if event.Type == 'keyboard': 20 | ao = AccessibleObjectFromWindow(event.Window, OBJID_CLIENT) 21 | elif event.Type == 'mouse': 22 | ao = AccessibleObjectFromPoint(event.Position) 23 | 24 | print 25 | print '---------------------------' 26 | print 'Event:' 27 | print ' ',event.MessageName 28 | print ' Window:', event.WindowName 29 | if event.Type == 'keyboard': 30 | print ' Key:',event.Key 31 | print 32 | print 'Object:' 33 | try: 34 | print ' Name:', ao.Name 35 | except: 36 | print 37 | 38 | try: 39 | print ' Value:', ao.Value 40 | except: 41 | print 42 | 43 | try: 44 | print ' Role:', ao.RoleText 45 | except: 46 | print 47 | 48 | try: 49 | print ' Description:', ao.Description 50 | except: 51 | print 52 | 53 | try: 54 | print ' State:', ao.StateText 55 | except: 56 | print 57 | 58 | try: 59 | print ' Shortcut:', ao.KeyboardShortcut 60 | except: 61 | print 62 | 63 | def OnMouseEvent(self, event): 64 | event.Type = 'mouse' 65 | wx.CallAfter(self.OnGetAO, event) 66 | 67 | def OnKeyboardEvent(self, event): 68 | event.Type = 'keyboard' 69 | wx.CallAfter(self.OnGetAO, event) 70 | 71 | def OnClose(self, event): 72 | del self.hm 73 | self.Destroy() 74 | 75 | if __name__ == '__main__': 76 | app = wx.PySimpleApp(0) 77 | frame = myFrame() 78 | app.SetTopWindow(frame) 79 | frame.Show() 80 | app.MainLoop() -------------------------------------------------------------------------------- /cpyHook.i: -------------------------------------------------------------------------------- 1 | %module cpyHook 2 | %include typemaps.i 3 | 4 | %{ 5 | #define _WIN32_WINNT 0x400 6 | #include "windows.h" 7 | 8 | #if PY_MAJOR_VERSION >= 3 9 | #define PY3K 10 | #endif 11 | 12 | PyObject* callback_funcs[WH_MAX]; 13 | HHOOK hHooks[WH_MAX]; 14 | BYTE key_state[256]; 15 | %} 16 | 17 | #ifdef SWIGPYTHON 18 | %typemap(in) PyObject *pyfunc { 19 | if (!PyCallable_Check($input)) { 20 | PyErr_SetString(PyExc_TypeError, "Need a callable object"); 21 | return NULL; 22 | } 23 | $1 = $input; 24 | } 25 | #endif 26 | 27 | %init %{ 28 | memset(key_state, 0, 256); 29 | memset(callback_funcs, 0, WH_MAX); 30 | memset(hHooks, 0, WH_MAX); 31 | PyEval_InitThreads(); 32 | 33 | // get initial key state 34 | Py_BEGIN_ALLOW_THREADS 35 | key_state[VK_NUMLOCK] = (GetKeyState(VK_NUMLOCK)&0x0001) ? 0x01 : 0x00; 36 | key_state[VK_CAPITAL] = (GetKeyState(VK_CAPITAL)&0x0001) ? 0x01 : 0x00; 37 | key_state[VK_SCROLL] = (GetKeyState(VK_SCROLL)&0x0001) ? 0x01 : 0x00; 38 | Py_END_ALLOW_THREADS 39 | %} 40 | 41 | %wrapper %{ 42 | unsigned short ConvertToASCII(unsigned int keycode, unsigned int scancode); 43 | void UpdateKeyState(unsigned int vkey, int msg); 44 | 45 | LRESULT CALLBACK cLLKeyboardCallback(int code, WPARAM wParam, LPARAM lParam) { 46 | PyObject *arglist, *r; 47 | PKBDLLHOOKSTRUCT kbd; 48 | HWND hwnd; 49 | PSTR win_name = NULL; 50 | unsigned short ascii = 0; 51 | static int win_len; 52 | static long result; 53 | long pass = 1; 54 | PyGILState_STATE gil; 55 | 56 | #ifdef PY3K 57 | PyObject *win_name_decoded = NULL; 58 | #endif 59 | 60 | // uncomment this next bit if you do not want to process events like "ctl-alt-del" 61 | // and other events that are not supposed to be processed 62 | // as per msdn documentation: 63 | // http://msdn.microsoft.com/en-us/library/ms644985(VS.85).aspx 64 | 65 | // if message code < 0, return immediately 66 | //if(code<0) 67 | // CallNextHookEx(hHooks[WH_KEYBOARD_LL], code, wParam, lParam); 68 | 69 | // get the GIL 70 | gil = PyGILState_Ensure(); 71 | 72 | // cast to a keyboard event struct 73 | kbd = (PKBDLLHOOKSTRUCT)lParam; 74 | // get the current foreground window (might not be the real window that received the event) 75 | hwnd = GetForegroundWindow(); 76 | 77 | // grab the window name if possible 78 | win_len = GetWindowTextLength(hwnd); 79 | if(win_len > 0) { 80 | win_name = (PSTR) malloc(sizeof(char) * win_len + 1); 81 | GetWindowText(hwnd, win_name, win_len + 1); 82 | } 83 | 84 | #ifdef PY3K 85 | win_name_decoded = PyUnicode_DecodeFSDefault(win_name); 86 | #endif 87 | 88 | // convert to an ASCII code if possible 89 | ascii = ConvertToASCII(kbd->vkCode, kbd->scanCode); 90 | 91 | // pass the message on to the Python function 92 | #ifdef PY3K 93 | arglist = Py_BuildValue("(iiiiiiiu)", wParam, kbd->vkCode, kbd->scanCode, ascii, 94 | kbd->flags, kbd->time, hwnd, win_name_decoded); 95 | #else 96 | arglist = Py_BuildValue("(iiiiiiiz)", wParam, kbd->vkCode, kbd->scanCode, ascii, 97 | kbd->flags, kbd->time, hwnd, win_name); 98 | #endif 99 | if(arglist == NULL) 100 | PyErr_Print(); 101 | 102 | r = PyObject_CallObject(callback_funcs[WH_KEYBOARD_LL], arglist); 103 | 104 | #ifdef PY3K 105 | // release unicode object 106 | Py_DECREF(win_name_decoded); 107 | #endif 108 | 109 | // check if we should pass the event on or not 110 | if(r == NULL) 111 | PyErr_Print(); 112 | else 113 | pass = PyInt_AsLong(r); 114 | 115 | Py_XDECREF(r); 116 | Py_DECREF(arglist); 117 | // release the GIL 118 | PyGILState_Release(gil); 119 | 120 | // free the memory for the window name 121 | if(win_name != NULL) 122 | free(win_name); 123 | 124 | // decide whether or not to call the next hook 125 | if(code < 0 || pass) { 126 | UpdateKeyState(kbd->vkCode, wParam); 127 | result = CallNextHookEx(hHooks[WH_KEYBOARD_LL], code, wParam, lParam); 128 | } else { 129 | // return a non-zero to prevent further processing 130 | result = 42; 131 | } 132 | return result; 133 | } 134 | 135 | LRESULT CALLBACK cLLMouseCallback(int code, WPARAM wParam, LPARAM lParam) { 136 | PyObject *arglist, *r; 137 | PMSLLHOOKSTRUCT ms; 138 | HWND hwnd; 139 | PSTR win_name = NULL; 140 | static int win_len; 141 | static long result; 142 | long pass = 1; 143 | PyGILState_STATE gil; 144 | 145 | #ifdef PY3K 146 | PyObject *win_name_decoded = NULL; 147 | #endif 148 | 149 | // get the GIL 150 | gil = PyGILState_Ensure(); 151 | 152 | //pass the message on to the Python function 153 | ms = (PMSLLHOOKSTRUCT)lParam; 154 | hwnd = WindowFromPoint(ms->pt); 155 | 156 | //grab the window name if possible 157 | win_len = GetWindowTextLength(hwnd); 158 | if(win_len > 0) { 159 | win_name = (PSTR) malloc(sizeof(char) * win_len + 1); 160 | GetWindowText(hwnd, win_name, win_len + 1); 161 | } 162 | 163 | #ifdef PY3K 164 | win_name_decoded = PyUnicode_DecodeFSDefault(win_name); 165 | #endif 166 | 167 | //build the argument list to the callback function 168 | #ifdef PY3K 169 | arglist = Py_BuildValue("(iiiiiiiu)", wParam, ms->pt.x, ms->pt.y, ms->mouseData, 170 | ms->flags, ms->time, hwnd, win_name_decoded); 171 | #else 172 | arglist = Py_BuildValue("(iiiiiiiz)", wParam, ms->pt.x, ms->pt.y, ms->mouseData, 173 | ms->flags, ms->time, hwnd, win_name); 174 | #endif 175 | if(arglist == NULL) 176 | PyErr_Print(); 177 | 178 | r = PyObject_CallObject(callback_funcs[WH_MOUSE_LL], arglist); 179 | 180 | #ifdef PY3K 181 | // release unicode object 182 | Py_DECREF(win_name_decoded); 183 | #endif 184 | 185 | // check if we should pass the event on or not 186 | if(r == NULL) 187 | PyErr_Print(); 188 | else 189 | pass = PyInt_AsLong(r); 190 | 191 | Py_XDECREF(r); 192 | Py_DECREF(arglist); 193 | // release the GIL 194 | PyGILState_Release(gil); 195 | 196 | //free the memory for the window name 197 | if(win_name != NULL) 198 | free(win_name); 199 | 200 | // decide whether or not to call the next hook 201 | if(code < 0 || pass) 202 | result = CallNextHookEx(hHooks[WH_MOUSE_LL], code, wParam, lParam); 203 | else { 204 | // return non-zero to prevent further processing 205 | result = 42; 206 | } 207 | return result; 208 | } 209 | 210 | int cSetHook(int idHook, PyObject *pyfunc) { 211 | HINSTANCE hMod; 212 | 213 | //make sure we have a valid hook number 214 | if(idHook > WH_MAX || idHook < WH_MIN) { 215 | PyErr_SetString(PyExc_ValueError, "Hooking error: invalid hook ID"); 216 | } 217 | 218 | //get the module handle 219 | Py_BEGIN_ALLOW_THREADS 220 | // try to get handle for current file - will succeed if called from a compiled .exe 221 | hMod = GetModuleHandle(NULL); 222 | if(NULL == hMod) // otherwise use name for DLL 223 | hMod = GetModuleHandle("_cpyHook.pyd"); 224 | Py_END_ALLOW_THREADS 225 | 226 | //switch on the type of hook so we point to the right C callback 227 | switch(idHook) { 228 | case WH_MOUSE_LL: 229 | if(callback_funcs[idHook] != NULL) 230 | break; 231 | 232 | callback_funcs[idHook] = pyfunc; 233 | Py_INCREF(callback_funcs[idHook]); 234 | 235 | Py_BEGIN_ALLOW_THREADS 236 | hHooks[idHook] = SetWindowsHookEx(WH_MOUSE_LL, cLLMouseCallback, (HINSTANCE) hMod, 0); 237 | Py_END_ALLOW_THREADS 238 | break; 239 | 240 | case WH_KEYBOARD_LL: 241 | if(callback_funcs[idHook] != NULL) 242 | break; 243 | 244 | callback_funcs[idHook] = pyfunc; 245 | Py_INCREF(callback_funcs[idHook]); 246 | 247 | Py_BEGIN_ALLOW_THREADS 248 | hHooks[idHook] = SetWindowsHookEx(WH_KEYBOARD_LL, cLLKeyboardCallback, (HINSTANCE) hMod, 0); 249 | Py_END_ALLOW_THREADS 250 | break; 251 | 252 | default: 253 | return 0; 254 | } 255 | 256 | if(!hHooks[idHook]) { 257 | PyErr_SetString(PyExc_TypeError, "Could not set hook"); 258 | } 259 | 260 | return 1; 261 | } 262 | 263 | int cUnhook(int idHook) { 264 | BOOL result; 265 | 266 | //make sure we have a valid hook number 267 | if(idHook > WH_MAX || idHook < WH_MIN) { 268 | PyErr_SetString(PyExc_ValueError, "Invalid hook ID"); 269 | } 270 | 271 | //unhook the callback 272 | Py_BEGIN_ALLOW_THREADS 273 | result = UnhookWindowsHookEx(hHooks[idHook]); 274 | Py_END_ALLOW_THREADS 275 | 276 | if(result) { 277 | //decrease the ref to the Python callback 278 | Py_DECREF(callback_funcs[idHook]); 279 | callback_funcs[idHook] = NULL; 280 | } 281 | 282 | return result; 283 | } 284 | 285 | void SetKeyState(unsigned int vkey, int down) { 286 | // (1 > 0) ? True : False 287 | if (vkey == VK_MENU || vkey == VK_LMENU || vkey == VK_RMENU) { 288 | key_state[vkey] = (down) ? 0x80 : 0x00; 289 | key_state[VK_MENU] = key_state[VK_LMENU] | key_state[VK_RMENU]; 290 | } else if (vkey == VK_SHIFT || vkey == VK_LSHIFT || vkey == VK_RSHIFT) { 291 | key_state[vkey] = (down) ? 0x80 : 0x00; 292 | key_state[VK_SHIFT] = key_state[VK_LSHIFT] | key_state[VK_RSHIFT]; 293 | } else if (vkey == VK_CONTROL || vkey == VK_LCONTROL || vkey == VK_RCONTROL) { 294 | key_state[vkey] = (down) ? 0x80 : 0x00; 295 | key_state[VK_CONTROL] = key_state[VK_LCONTROL] | key_state[VK_RCONTROL]; 296 | } else if (vkey == VK_NUMLOCK && !down) { 297 | key_state[VK_NUMLOCK] = !key_state[VK_NUMLOCK]; 298 | } else if (vkey == VK_CAPITAL && !down) { 299 | key_state[VK_CAPITAL] = !key_state[VK_CAPITAL]; 300 | } else if (vkey == VK_SCROLL && !down) { 301 | key_state[VK_SCROLL] = !key_state[VK_SCROLL]; 302 | } 303 | } 304 | 305 | void UpdateKeyState(unsigned int vkey, int msg) { 306 | if (msg == WM_KEYDOWN || msg == WM_SYSKEYDOWN) { 307 | SetKeyState(vkey, 1); 308 | } else if (msg == WM_KEYUP || msg == WM_SYSKEYUP) { 309 | SetKeyState(vkey, 0); 310 | } 311 | } 312 | 313 | unsigned int cGetKeyState(unsigned int vkey) { 314 | return key_state[vkey]; 315 | } 316 | 317 | unsigned short ConvertToASCII(unsigned int keycode, unsigned int scancode) { 318 | int r; 319 | unsigned short c = 0; 320 | 321 | Py_BEGIN_ALLOW_THREADS 322 | r = ToAscii(keycode, scancode, key_state, &c, 0); 323 | Py_END_ALLOW_THREADS 324 | if(r < 0) { 325 | //PyErr_SetString(PyExc_ValueError, "Could not convert to ASCII"); 326 | return 0; 327 | } 328 | return c; 329 | } 330 | %} 331 | 332 | unsigned int cGetKeyState(unsigned int vkey); 333 | int cSetHook(int idHook, PyObject *pyfunc); 334 | int cUnhook(int idHook); 335 | -------------------------------------------------------------------------------- /doc.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from epydoc.cli import cli 3 | 4 | # add default parameters to the command line 5 | options = ['--html', '-o', 'doc', '--inheritance', 'listed'] 6 | sys.argv.extend(options) 7 | # extend the command line to include the given files 8 | files = ['__init__.py', 'HookManager.py'] 9 | sys.argv.extend(files) 10 | cli() -------------------------------------------------------------------------------- /example.py: -------------------------------------------------------------------------------- 1 | import pyHook 2 | 3 | def OnMouseEvent(event): 4 | print 'MessageName:',event.MessageName 5 | print 'Message:',event.Message 6 | print 'Time:',event.Time 7 | print 'Window:',event.Window 8 | print 'WindowName:',event.WindowName 9 | print 'Position:',event.Position 10 | print 'Wheel:',event.Wheel 11 | print 'Injected:',event.Injected 12 | print '---' 13 | 14 | # return True to pass the event to other handlers 15 | # return False to stop the event from propagating 16 | return True 17 | 18 | def OnKeyboardEvent(event): 19 | print 'MessageName:',event.MessageName 20 | print 'Message:',event.Message 21 | print 'Time:',event.Time 22 | print 'Window:',event.Window 23 | print 'WindowName:',event.WindowName 24 | print 'Ascii:', event.Ascii, chr(event.Ascii) 25 | print 'Key:', event.Key 26 | print 'KeyID:', event.KeyID 27 | print 'ScanCode:', event.ScanCode 28 | print 'Extended:', event.Extended 29 | print 'Injected:', event.Injected 30 | print 'Alt', event.Alt 31 | print 'Transition', event.Transition 32 | print '---' 33 | 34 | # return True to pass the event to other handlers 35 | # return False to stop the event from propagating 36 | return True 37 | 38 | # create the hook mananger 39 | hm = pyHook.HookManager() 40 | # register two callbacks 41 | hm.MouseAllButtonsDown = OnMouseEvent 42 | hm.KeyDown = OnKeyboardEvent 43 | 44 | # hook into the mouse and keyboard events 45 | hm.HookMouse() 46 | hm.HookKeyboard() 47 | 48 | if __name__ == '__main__': 49 | import pythoncom 50 | pythoncom.PumpMessages() -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | '''pyHook: Python wrapper for out-of-context input hooks in Windows 2 | 3 | The pyHook package provides callbacks for global mouse and keyboard events in Windows. Python 4 | applications register event handlers for user input events such as left mouse down, left mouse up, 5 | key down, etc. and set the keyboard and/or mouse hook. The underlying C library reports information 6 | like the time of the event, the name of the window in which the event occurred, the value of the 7 | event, any keyboard modifiers, etc. 8 | ''' 9 | 10 | classifiers = """\ 11 | Development Status :: 5 - Production/Stable 12 | Intended Audience :: Developers 13 | License :: OSI Approved :: MIT License 14 | Programming Language :: Python 15 | Topic :: System :: Monitoring 16 | Topic :: Software Development :: Libraries :: Python Modules 17 | Operating System :: Microsoft :: Windows 18 | """ 19 | 20 | from setuptools import setup, Extension 21 | 22 | libs = ['user32'] 23 | doclines = __doc__.split('\n') 24 | 25 | setup(name='pyHook', 26 | version='1.5.1', 27 | maintainer='Daniel Folkinshteyn', 28 | maintainer_email='nanotube@users.sf.net', 29 | author='Peter Parente', 30 | author_email='parente@cs.unc.edu', 31 | url='http://pyhook.sourceforge.net', 32 | download_url='http://www.sourceforge.net/projects/pyhook', 33 | license='http://www.opensource.org/licenses/mit-license.php', 34 | platforms=['Win32'], 35 | description = doclines[0], 36 | classifiers = filter(None, classifiers.split('\n')), 37 | long_description = ' '.join(doclines[2:]), 38 | packages = ['pyHook'], 39 | package_dir = {'pyHook' : ""}, 40 | ext_modules = [Extension('pyHook._cpyHook', ['cpyHook.i'], libraries=libs)], 41 | data_files=[('Lib/site-packages/pyHook', ['LICENSE.txt', 'README.txt','CHANGELOG.txt'])], 42 | use_2to3=True 43 | ) 44 | --------------------------------------------------------------------------------