Module NVDAHelper
[hide private]
[frames] | no frames]

Source Code for Module NVDAHelper

  1  import os 
  2  import _winreg 
  3  import msvcrt 
  4  import winKernel 
  5   
  6  from ctypes import * 
  7  from comtypes import BSTR 
  8  import winUser 
  9  import speech 
 10  import eventHandler 
 11  import queueHandler 
 12  import api 
 13  import globalVars 
 14  from logHandler import log 
 15  import time 
 16  import globalVars 
 17   
 18  _remoteLib=None 
 19  _remoteLoader64=None 
 20  localLib=None 
 21  generateBeep=None 
 22  VBuf_getTextInRange=None 
 23  lastInputLangChangeTime=0 
24 25 #utility function to point an exported function pointer in a dll to a ctypes wrapped python function 26 -def _setDllFuncPointer(dll,name,cfunc):
27 cast(getattr(dll,name),POINTER(c_void_p)).contents.value=cast(cfunc,c_void_p).value
28
29 #Implementation of nvdaController methods 30 @WINFUNCTYPE(c_long,c_wchar_p) 31 -def nvdaController_speakText(text):
32 import queueHandler 33 import speech 34 queueHandler.queueFunction(queueHandler.eventQueue,speech.speakText,text) 35 return 0
36
37 @WINFUNCTYPE(c_long) 38 -def nvdaController_cancelSpeech():
39 import queueHandler 40 import speech 41 queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech) 42 return 0
43
44 @WINFUNCTYPE(c_long,c_wchar_p) 45 -def nvdaController_brailleMessage(text):
46 import queueHandler 47 import braille 48 queueHandler.queueFunction(queueHandler.eventQueue,braille.handler.message,text) 49 return 0
50
51 -def _lookupKeyboardLayoutNameWithHexString(layoutString):
52 try: 53 key = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"+ layoutString) 54 except WindowsError: 55 log.debugWarning("Could not find reg key %s"%layoutString) 56 return None 57 try: 58 s = _winreg.QueryValueEx(key, "Layout Display Name")[0] 59 except: 60 log.debugWarning("Could not find reg value 'Layout Display Name' for reg key %s"%layoutString) 61 s=None 62 if s: 63 buf=create_unicode_buffer(256) 64 windll.shlwapi.SHLoadIndirectString(s,buf,256,None) 65 return buf.value 66 try: 67 return _winreg.QueryValueEx(key, "Layout Text")[0] 68 except: 69 log.debugWarning("Could not find reg value 'Layout Text' for reg key %s"%layoutString) 70 return None
71
72 @WINFUNCTYPE(c_long,c_long,c_long,c_long,c_long,c_long) 73 -def nvdaControllerInternal_displayModelTextChangeNotify(hwnd, left, top, right, bottom):
74 import displayModel 75 displayModel.textChangeNotify(hwnd, left, top, right, bottom) 76 return 0
77
78 @WINFUNCTYPE(c_long,c_long,c_long,c_wchar_p) 79 -def nvdaControllerInternal_logMessage(level,pid,message):
80 if not log.isEnabledFor(level): 81 return 0 82 from appModuleHandler import getAppNameFromProcessID 83 codepath="RPC process %s (%s)"%(pid,getAppNameFromProcessID(pid,includeExt=True)) 84 log._log(level,message,[],codepath=codepath) 85 return 0
86
87 @WINFUNCTYPE(c_long,c_long,c_ulong,c_wchar_p) 88 -def nvdaControllerInternal_inputLangChangeNotify(threadID,hkl,layoutString):
89 global lastInputLangChangeTime 90 import queueHandler 91 import ui 92 curTime=time.time() 93 if (curTime-lastInputLangChangeTime)<0.2: 94 return 0 95 lastInputLangChangeTime=curTime 96 #layoutString can sometimes be None, yet a registry entry still exists for a string representation of hkl 97 if not layoutString: 98 layoutString=hex(hkl)[2:].rstrip('L').upper().rjust(8,'0') 99 log.debugWarning("layoutString was None, generated new one from hkl as %s"%layoutString) 100 layoutName=_lookupKeyboardLayoutNameWithHexString(layoutString) 101 if not layoutName and hkl<0xd0000000: 102 #Try using the high word of hkl as the lang ID for a default layout for that language 103 simpleLayoutString=layoutString[0:4].rjust(8,'0') 104 log.debugWarning("trying simple version: %s"%simpleLayoutString) 105 layoutName=_lookupKeyboardLayoutNameWithHexString(simpleLayoutString) 106 if not layoutName: 107 #Try using the low word of hkl as the lang ID for a default layout for that language 108 simpleLayoutString=layoutString[4:].rjust(8,'0') 109 log.debugWarning("trying simple version: %s"%simpleLayoutString) 110 layoutName=_lookupKeyboardLayoutNameWithHexString(simpleLayoutString) 111 if not layoutName: 112 log.debugWarning("Could not find layout name for keyboard layout, reporting as unknown") 113 layoutName=_("unknown layout") 114 queueHandler.queueFunction(queueHandler.eventQueue,ui.message,_("layout %s")%layoutName) 115 return 0
116
117 @WINFUNCTYPE(c_long,c_long,c_wchar) 118 -def nvdaControllerInternal_typedCharacterNotify(threadID,ch):
119 focus=api.getFocusObject() 120 if focus.windowClassName!="ConsoleWindowClass": 121 eventHandler.queueEvent("typedCharacter",focus,ch=ch) 122 return 0
123
124 -class RemoteLoader64(object):
125
126 - def __init__(self):
127 # Create a pipe so we can write to stdin of the loader process. 128 pipeReadOrig, self._pipeWrite = winKernel.CreatePipe(None, 0) 129 # Make the read end of the pipe inheritable. 130 pipeRead = self._duplicateAsInheritable(pipeReadOrig) 131 winKernel.closeHandle(pipeReadOrig) 132 # stdout/stderr of the loader process should go to nul. 133 with file("nul", "w") as nul: 134 nulHandle = self._duplicateAsInheritable(msvcrt.get_osfhandle(nul.fileno())) 135 # Set the process to start with the appropriate std* handles. 136 si = winKernel.STARTUPINFO(dwFlags=winKernel.STARTF_USESTDHANDLES, hSTDInput=pipeRead, hSTDOutput=nulHandle, hSTDError=nulHandle) 137 pi = winKernel.PROCESS_INFORMATION() 138 # Even if we have uiAccess privileges, they will not be inherited by default. 139 # Therefore, explicitly specify our own process token, which causes them to be inherited. 140 token = winKernel.OpenProcessToken(winKernel.GetCurrentProcess(), winKernel.MAXIMUM_ALLOWED) 141 try: 142 winKernel.CreateProcessAsUser(token, None, u"lib64/nvdaHelperRemoteLoader.exe", None, None, True, None, None, None, si, pi) 143 # We don't need the thread handle. 144 winKernel.closeHandle(pi.hThread) 145 self._process = pi.hProcess 146 except: 147 winKernel.closeHandle(self._pipeWrite) 148 raise 149 finally: 150 winKernel.closeHandle(pipeRead) 151 winKernel.closeHandle(token)
152
153 - def _duplicateAsInheritable(self, handle):
154 curProc = winKernel.GetCurrentProcess() 155 return winKernel.DuplicateHandle(curProc, handle, curProc, 0, True, winKernel.DUPLICATE_SAME_ACCESS)
156
157 - def terminate(self):
158 # Closing the write end of the pipe will cause EOF for the waiting loader process, which will then exit gracefully. 159 winKernel.closeHandle(self._pipeWrite) 160 # Wait until it's dead. 161 winKernel.waitForSingleObject(self._process, winKernel.INFINITE) 162 winKernel.closeHandle(self._process)
163
164 -def initialize():
165 global _remoteLib, _remoteLoader64, localLib, generateBeep,VBuf_getTextInRange 166 localLib=cdll.LoadLibrary('lib/nvdaHelperLocal.dll') 167 for name,func in [ 168 ("nvdaController_speakText",nvdaController_speakText), 169 ("nvdaController_cancelSpeech",nvdaController_cancelSpeech), 170 ("nvdaController_brailleMessage",nvdaController_brailleMessage), 171 ("nvdaControllerInternal_inputLangChangeNotify",nvdaControllerInternal_inputLangChangeNotify), 172 ("nvdaControllerInternal_typedCharacterNotify",nvdaControllerInternal_typedCharacterNotify), 173 ("nvdaControllerInternal_displayModelTextChangeNotify",nvdaControllerInternal_displayModelTextChangeNotify), 174 ("nvdaControllerInternal_logMessage",nvdaControllerInternal_logMessage), 175 ]: 176 try: 177 _setDllFuncPointer(localLib,"_%s"%name,func) 178 except AttributeError: 179 log.error("nvdaHelperLocal function pointer for %s could not be found, possibly old nvdaHelperLocal dll"%name) 180 localLib.nvdaHelperLocal_initialize() 181 generateBeep=localLib.generateBeep 182 generateBeep.argtypes=[c_char_p,c_float,c_uint,c_ubyte,c_ubyte] 183 generateBeep.restype=c_uint 184 # Handle VBuf_getTextInRange's BSTR out parameter so that the BSTR will be freed automatically. 185 VBuf_getTextInRange = CFUNCTYPE(c_int, c_int, c_int, c_int, POINTER(BSTR), c_int)( 186 ("VBuf_getTextInRange", localLib), 187 ((1,), (1,), (1,), (2,), (1,))) 188 #Load nvdaHelperRemote.dll but with an altered search path so it can pick up other dlls in lib 189 h=windll.kernel32.LoadLibraryExW(os.path.abspath(ur"lib\nvdaHelperRemote.dll"),0,0x8) 190 if not h: 191 log.critical("Error loading nvdaHelperRemote.dll: %s" % WinError()) 192 return 193 _remoteLib=CDLL("nvdaHelperRemote",handle=h) 194 if _remoteLib.injection_initialize(globalVars.appArgs.secure) == 0: 195 raise RuntimeError("Error initializing NVDAHelperRemote") 196 if not _remoteLib.installIA2Support(): 197 log.error("Error installing IA2 support") 198 #Manually start the in-process manager thread for this NVDA main thread now, as a slow system can cause this action to confuse WX 199 _remoteLib.initInprocManagerThreadIfNeeded() 200 if os.environ.get('PROCESSOR_ARCHITEW6432')=='AMD64': 201 _remoteLoader64=RemoteLoader64()
202
203 -def terminate():
204 global _remoteLib, _remoteLoader64, localLib, generateBeep, VBuf_getTextInRange 205 if not _remoteLib.uninstallIA2Support(): 206 log.debugWarning("Error uninstalling IA2 support") 207 if _remoteLib.injection_terminate() == 0: 208 raise RuntimeError("Error terminating NVDAHelperRemote") 209 _remoteLib=None 210 if _remoteLoader64: 211 _remoteLoader64.terminate() 212 _remoteLoader64=None 213 generateBeep=None 214 VBuf_getTextInRange=None 215 localLib.nvdaHelperLocal_terminate() 216 localLib=None
217