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

Source Code for Module appModuleHandler

  1  #appModuleHandler.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  """Manages appModules. 
  8  @var runningTable: a dictionary of the currently running appModules, using their application's main window handle as a key. 
  9  @type runningTable: dict 
 10  """ 
 11   
 12  import itertools 
 13  import ctypes 
 14  import os 
 15  import sys 
 16  import pkgutil 
 17  import baseObject 
 18  import globalVars 
 19  from logHandler import log 
 20  import NVDAHelper 
 21  import ui 
 22  import winUser 
 23  import winKernel 
 24  import config 
 25  import NVDAObjects #Catches errors before loading default appModule 
 26  import api 
 27  import appModules 
 28   
 29  #Dictionary of processID:appModule paires used to hold the currently running modules 
 30  runningTable={} 
 31  #: The process ID of NVDA itself. 
 32  NVDAProcessID=None 
 33  _importers=None 
 34   
35 -class processEntry32W(ctypes.Structure):
36 _fields_ = [ 37 ("dwSize",ctypes.wintypes.DWORD), 38 ("cntUsage", ctypes.wintypes.DWORD), 39 ("th32ProcessID", ctypes.wintypes.DWORD), 40 ("th32DefaultHeapID", ctypes.wintypes.DWORD), 41 ("th32ModuleID",ctypes.wintypes.DWORD), 42 ("cntThreads",ctypes.wintypes.DWORD), 43 ("th32ParentProcessID",ctypes.wintypes.DWORD), 44 ("pcPriClassBase",ctypes.c_long), 45 ("dwFlags",ctypes.wintypes.DWORD), 46 ("szExeFile", ctypes.c_wchar * 260) 47 ]
48
49 -def getAppNameFromProcessID(processID,includeExt=False):
50 """Finds out the application name of the given process. 51 @param processID: the ID of the process handle of the application you wish to get the name of. 52 @type processID: int 53 @param includeExt: C{True} to include the extension of the application's executable filename, C{False} to exclude it. 54 @type window: bool 55 @returns: application name 56 @rtype: unicode or str 57 """ 58 if processID==NVDAProcessID: 59 return "nvda.exe" if includeExt else "nvda" 60 FSnapshotHandle = winKernel.kernel32.CreateToolhelp32Snapshot (2,0) 61 FProcessEntry32 = processEntry32W() 62 FProcessEntry32.dwSize = ctypes.sizeof(processEntry32W) 63 ContinueLoop = winKernel.kernel32.Process32FirstW(FSnapshotHandle, ctypes.byref(FProcessEntry32)) 64 appName = unicode() 65 while ContinueLoop: 66 if FProcessEntry32.th32ProcessID == processID: 67 appName = FProcessEntry32.szExeFile 68 break 69 ContinueLoop = winKernel.kernel32.Process32NextW(FSnapshotHandle, ctypes.byref(FProcessEntry32)) 70 winKernel.kernel32.CloseHandle(FSnapshotHandle) 71 if not includeExt: 72 appName=os.path.splitext(appName)[0].lower() 73 return appName
74
75 -def getAppModuleForNVDAObject(obj):
76 if not isinstance(obj,NVDAObjects.NVDAObject): 77 return 78 return getAppModuleFromProcessID(obj.processID)
79
80 -def getAppModuleFromProcessID(processID):
81 """Finds the appModule that is for the given process ID. The module is also cached for later retreavals. 82 @param processID: The ID of the process for which you wish to find the appModule. 83 @type processID: int 84 @returns: the appModule, or None if there isn't one 85 @rtype: appModule 86 """ 87 mod=runningTable.get(processID) 88 if not mod: 89 appName=getAppNameFromProcessID(processID) 90 mod=fetchAppModule(processID,appName) 91 if not mod: 92 raise RuntimeError("error fetching default appModule") 93 runningTable[processID]=mod 94 return mod
95
96 -def update(processID):
97 """Removes any appModules from the cache whose process has died, and also tries to load a new appModule for the given process ID if need be. 98 @param processID: the ID of the process. 99 @type processID: int 100 """ 101 for deadMod in [mod for mod in runningTable.itervalues() if not mod.isAlive]: 102 log.debug("application %s closed"%deadMod.appName) 103 del runningTable[deadMod.processID] 104 if deadMod in set(o.appModule for o in api.getFocusAncestors()+[api.getFocusObject()] if o and o.appModule): 105 if hasattr(deadMod,'event_appLoseFocus'): 106 deadMod.event_appLoseFocus() 107 try: 108 deadMod.terminate() 109 except: 110 log.exception("Error terminating app module %r" % deadMod) 111 # This creates a new app module if necessary. 112 getAppModuleFromProcessID(processID)
113
114 -def doesAppModuleExist(name):
115 return any(importer.find_module("appModules.%s" % name) for importer in _importers)
116
117 -def fetchAppModule(processID,appName):
118 """Returns an appModule found in the appModules directory, for the given application name. 119 @param processID: process ID for it to be associated with 120 @type processID: integer 121 @param appName: the application name for which an appModule should be found. 122 @type appName: unicode or str 123 @returns: the appModule, or None if not found 124 @rtype: AppModule 125 """ 126 # First, check whether the module exists. 127 # We need to do this separately because even though an ImportError is raised when a module can't be found, it might also be raised for other reasons. 128 # Python 2.x can't properly handle unicode module names, so convert them. 129 modName = appName.encode("mbcs") 130 131 if doesAppModuleExist(modName): 132 try: 133 return __import__("appModules.%s" % modName, globals(), locals(), ("appModules",)).AppModule(processID, appName) 134 except: 135 log.error("error in appModule %r"%modName, exc_info=True) 136 # We can't present a message which isn't unicode, so use appName, not modName. 137 ui.message(_("Error in appModule %s")%appName) 138 139 # Use the base AppModule. 140 return AppModule(processID, appName)
141
142 -def reloadAppModules():
143 """Reloads running appModules. 144 especially, it clears the cache of running appModules and deletes them from sys.modules. 145 Each appModule will be reloaded immediately as a reaction on a first event coming from the process. 146 """ 147 global appModules 148 terminate() 149 del appModules 150 mods=[k for k,v in sys.modules.iteritems() if k.startswith("appModules") and v is not None] 151 for mod in mods: 152 del sys.modules[mod] 153 import appModules 154 initialize()
155
156 -def initialize():
157 """Initializes the appModule subsystem. 158 """ 159 global NVDAProcessID,_importers 160 NVDAProcessID=os.getpid() 161 config.addConfigDirsToPythonPackagePath(appModules) 162 _importers=list(pkgutil.iter_importers("appModules.__init__"))
163
164 -def terminate():
165 for processID, app in runningTable.iteritems(): 166 try: 167 app.terminate() 168 except: 169 log.exception("Error terminating app module %r" % app) 170 runningTable.clear()
171 172 #base class for appModules
173 -class AppModule(baseObject.ScriptableObject):
174 """Base app module. 175 App modules provide specific support for a single application. 176 Each app module should be a Python module in the appModules package named according to the executable it supports; 177 e.g. explorer.py for the explorer.exe application. 178 It should containa C{AppModule} class which inherits from this base class. 179 App modules can implement and bind gestures to scripts. 180 These bindings will only take effect while an object in the associated application has focus. 181 See L{ScriptableObject} for details. 182 App modules can also receive NVDAObject events for objects within the associated application. 183 This is done by implementing methods called C{event_eventName}, 184 where C{eventName} is the name of the event; e.g. C{event_gainFocus}. 185 These event methods take two arguments: the NVDAObject on which the event was fired 186 and a callable taking no arguments which calls the next event handler. 187 """ 188 189 #: Whether NVDA should sleep while in this application (e.g. the application is self-voicing). 190 #: If C{True}, all events and script requests inside this application are silently dropped. 191 #: @type: bool 192 sleepMode=False 193
194 - def __init__(self,processID,appName=None):
195 super(AppModule,self).__init__() 196 #: The ID of the process this appModule is for. 197 #: @type: int 198 self.processID=processID 199 self.helperLocalBindingHandle=NVDAHelper.localLib.createConnection(processID) 200 if appName is None: 201 appName=getAppNameFromProcessID(processID) 202 #: The application name. 203 #: @type: str 204 self.appName=appName 205 self.processHandle=winKernel.openProcess(winKernel.SYNCHRONIZE,False,processID)
206
207 - def __repr__(self):
208 return "<%r (appName %r, process ID %s) at address %x>"%(self.appModuleName,self.appName,self.processID,id(self))
209
210 - def _get_appModuleName(self):
211 return self.__class__.__module__.split('.')[-1]
212
213 - def _get_isAlive(self):
214 return bool(winKernel.waitForSingleObject(self.processHandle,0))
215
216 - def terminate(self):
217 """Terminate this app module. 218 This is called to perform any clean up when this app module is being destroyed. 219 Subclasses should call the superclass method first. 220 """ 221 winKernel.closeHandle(self.processHandle) 222 NVDAHelper.localLib.destroyConnection(self.helperLocalBindingHandle)
223
224 - def chooseNVDAObjectOverlayClasses(self, obj, clsList):
225 """Choose NVDAObject overlay classes for a given NVDAObject. 226 This is called when an NVDAObject is being instantiated after L{NVDAObjects.NVDAObject.findOverlayClasses} has been called on the API-level class. 227 This allows an AppModule to add or remove overlay classes. 228 See L{NVDAObjects.NVDAObject.findOverlayClasses} for details about overlay classes. 229 @param obj: The object being created. 230 @type obj: L{NVDAObjects.NVDAObject} 231 @param clsList: The list of classes, which will be modified by this method if appropriate. 232 @type clsList: list of L{NVDAObjects.NVDAObject} 233 """
234