-
-
Notifications
You must be signed in to change notification settings - Fork 628
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Changes to how NVDA hooks the keyboard and mouse, to fix problems suc…
…h as: *The keyboard or mouse hook dying in NVDA on Windows 7 leaving the user unable to give NVDA commands (including shutting up speech), or getting feedback when moving the mouse. *Sometimes no ability to control the Operating System with the mouse or keyboard if NVDA had frozen. *the insert or other modifier keys seeming to get stuck down, if pressed while NVDA was extremely busy. Specifically the changes are: *Merge keyHook and mouseHook together and call it winInputHook. *winInputHook: hook the keyboard and mouse in a separate thread to NVDA's main thread, so that if NVDA's main thread blocks, the hooks can still run. winInputHook.initialize and winInputHook.terminate are ref counted, so that it can be initialized more than once, and terminated more than once, quite safely. initialize and terminate do not take any parameters. use winInputHook.setCallbacks to set callback functions for the hooks. *keyboardHandler and mouseHandler: Use winInputHook rather than keyHook and mouseHook.
- Loading branch information
1 parent
2717d33
commit 68e7a5c
Showing
5 changed files
with
109 additions
and
102 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,101 @@ | ||
#winInputHook.py | ||
#A part of NonVisual Desktop Access (NVDA) | ||
#Copyright (C) 2006-2008 NVDA Contributors <http://www.nvda-project.org/> | ||
#This file is covered by the GNU General Public License. | ||
#See the file COPYING for more details. | ||
|
||
import threading | ||
import comtypes.client | ||
import time | ||
from ctypes import * | ||
from ctypes.wintypes import * | ||
from win32con import WM_QUIT, HC_ACTION, WH_KEYBOARD_LL, LLKHF_UP, LLKHF_EXTENDED, LLKHF_INJECTED, WH_MOUSE_LL, LLMHF_INJECTED | ||
|
||
class KBDLLHOOKSTRUCT(Structure): | ||
_fields_=[ | ||
('vkCode',DWORD), | ||
('scanCode',DWORD), | ||
('flags',DWORD), | ||
('time',DWORD), | ||
('dwExtraInfo',DWORD), | ||
] | ||
|
||
class MSLLHOOKSTRUCT(Structure): | ||
_fields_=[ | ||
('pt',POINT), | ||
('mouseData',DWORD), | ||
('flags',DWORD), | ||
('time',DWORD), | ||
('dwExtraInfo',DWORD), | ||
] | ||
|
||
keyDownCallback=None | ||
keyUpCallback=None | ||
mouseCallback=None | ||
|
||
@WINFUNCTYPE(c_long,c_int,WPARAM,LPARAM) | ||
def keyboardHook(code,wParam,lParam): | ||
if code!=HC_ACTION: | ||
return windll.user32.CallNextHookEx(0,code,wParam,lParam) | ||
kbd=KBDLLHOOKSTRUCT.from_address(lParam) | ||
if keyUpCallback and kbd.flags&LLKHF_UP: | ||
if not keyUpCallback(kbd.vkCode,kbd.scanCode,bool(kbd.flags&LLKHF_EXTENDED),bool(kbd.flags&LLKHF_INJECTED)): | ||
return 1 | ||
elif keyDownCallback: | ||
if not keyDownCallback(kbd.vkCode,kbd.scanCode,bool(kbd.flags&LLKHF_EXTENDED),bool(kbd.flags&LLKHF_INJECTED)): | ||
return 1 | ||
return windll.user32.CallNextHookEx(0,code,wParam,lParam) | ||
|
||
@WINFUNCTYPE(c_long,c_int,WPARAM,LPARAM) | ||
def mouseHook(code,wParam,lParam): | ||
if code!=HC_ACTION: | ||
return windll.user32.CallNextHookEx(0,code,wParam,lParam) | ||
msll=MSLLHOOKSTRUCT.from_address(lParam) | ||
if mouseCallback: | ||
if not mouseCallback(wParam,msll.pt.x,msll.pt.y,msll.flags&LLMHF_INJECTED): | ||
return 1 | ||
return windll.user32.CallNextHookEx(0,code,wParam,lParam) | ||
|
||
hookThread=None | ||
hookThreadRefCount=0 | ||
|
||
def hookThreadFunc(): | ||
keyHookID=windll.user32.SetWindowsHookExW(WH_KEYBOARD_LL,keyboardHook,windll.kernel32.GetModuleHandleW(None),0) | ||
if keyHookID==0: | ||
raise OSError("Could not register keyboard hook") | ||
mouseHookID=windll.user32.SetWindowsHookExW(WH_MOUSE_LL,mouseHook,windll.kernel32.GetModuleHandleW(None),0) | ||
if mouseHookID==0: | ||
raise OSError("Could not register mouse hook") | ||
msg=MSG() | ||
while windll.user32.GetMessageW(byref(msg),None,0,0): | ||
pass | ||
if windll.user32.UnhookWindowsHookEx(keyHookID)==0: | ||
raise OSError("could not unregister key hook %s"%keyHookID) | ||
if windll.user32.UnhookWindowsHookEx(mouseHookID)==0: | ||
raise OSError("could not unregister mouse hook %s"%mouseHookID) | ||
|
||
def initialize(): | ||
global hookThread, hookThreadRefCount | ||
hookThreadRefCount+=1 | ||
if hookThreadRefCount==1: | ||
hookThread=threading.Thread(target=hookThreadFunc) | ||
hookThread.start() | ||
|
||
def setCallbacks(keyUp=None,keyDown=None,mouse=None): | ||
global keyUpCallback, keyDownCallback, mouseCallback | ||
if keyUp: | ||
keyUpCallback=keyUp | ||
if keyDown: | ||
keyDownCallback=keyDown | ||
if mouse: | ||
mouseCallback=mouse | ||
|
||
def terminate(): | ||
global hookThread, hookThreadRefCount | ||
if not hookThread: | ||
raise RuntimeError("winInputHook not running") | ||
hookThreadRefCount-=1 | ||
if hookThreadRefCount==0: | ||
windll.user32.PostThreadMessageW(hookThread.ident,WM_QUIT,0,0) | ||
hookThread.join() | ||
hookThread=None |