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

Source Code for Module IAccessibleHandler

   1  #IAccessiblehandler.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  from __future__ import with_statement 
   8  import oleacc 
   9   
  10  MAX_WINEVENTS=500 
  11  MAX_WINEVENTS_PER_THREAD=10 
  12   
  13  #Constants 
  14  #OLE constants 
  15  REGCLS_SINGLEUSE = 0       # class object only generates one instance 
  16  REGCLS_MULTIPLEUSE = 1     # same class object genereates multiple inst. 
  17  REGCLS_MULTI_SEPARATE = 2  # multiple use, but separate control over each 
  18  REGCLS_SUSPENDED      = 4  # register it as suspended, will be activated 
  19  REGCLS_SURROGATE      = 8  # must be used when a surrogate process 
  20   
  21  CLSCTX_INPROC_SERVER=1 
  22  CLSCTX_LOCAL_SERVER=4 
  23   
  24  #Special Mozilla gecko MSAA constant additions 
  25  NAVRELATION_LABEL_FOR=0x1002 
  26  NAVRELATION_LABELLED_BY=0x1003 
  27  NAVRELATION_NODE_CHILD_OF=0x1005 
  28  NAVRELATION_EMBEDS=0x1009 
  29   
  30  # IAccessible2 relations (not included in the typelib) 
  31  IA2_RELATION_FLOWS_FROM = "flowsFrom" 
  32  IA2_RELATION_FLOWS_TO = "flowsTo" 
  33   
  34  import UIAHandler 
  35  import heapq 
  36  import itertools 
  37  import time 
  38  import struct 
  39  import weakref 
  40  from ctypes import * 
  41  from ctypes.wintypes import * 
  42  from comtypes.automation import * 
  43  from comtypes.server import * 
  44  from comtypes import GUID, IServiceProvider 
  45  import comtypes.client 
  46  import comtypes.client.lazybind 
  47  import Queue 
  48  from comInterfaces.Accessibility import * 
  49  from comInterfaces.IAccessible2Lib import * 
  50  import tones 
  51  import globalVars 
  52  from logHandler import log 
  53  import JABHandler 
  54  import eventHandler 
  55  import winKernel 
  56  import winUser 
  57  import speech 
  58  import sayAllHandler 
  59  import api 
  60  import queueHandler 
  61  import NVDAObjects.IAccessible 
  62  import NVDAObjects.window 
  63  import appModuleHandler 
  64  import config 
  65  import mouseHandler 
  66  import controlTypes 
  67   
  68  MENU_EVENTIDS=(winUser.EVENT_SYSTEM_MENUSTART,winUser.EVENT_SYSTEM_MENUEND,winUser.EVENT_SYSTEM_MENUPOPUPSTART,winUser.EVENT_SYSTEM_MENUPOPUPEND) 
  69   
70 -class OrderedWinEventLimiter(object):
71 """Collects and limits winEvents based on whether they are focus changes, or just generic (all other ones). 72 73 Only allow a max of L{maxFocusItems}, if more are added then the oldest focus event is removed to make room. 74 Only allow one event for one specific object at a time, though push it further forward in time if a duplicate tries to get added. This is true for both generic and focus events. 75 """ 76
77 - def __init__(self,maxFocusItems=3):
78 """ 79 @param maxFocusItems: the amount of focus changed events allowed to be queued. 80 @type maxFocusItems: integer 81 """ 82 self.maxFocusItems=maxFocusItems 83 self._focusEventCache={} 84 self._genericEventCache={} 85 self._eventHeap=[] 86 self._eventCounter=itertools.count() 87 self._lastMenuEvent=None
88
89 - def addEvent(self,eventID,window,objectID,childID,threadID):
90 """Adds a winEvent to the limiter. 91 @param eventID: the winEvent type 92 @type eventID: integer 93 @param window: the window handle of the winEvent 94 @type window: integer 95 @param objectID: the objectID of the winEvent 96 @type objectID: integer 97 @param childID: the childID of the winEvent 98 @type childID: integer 99 @param threadID: the threadID of the winEvent 100 @type threadID: integer 101 """ 102 if eventID==winUser.EVENT_OBJECT_FOCUS: 103 if objectID in (winUser.OBJID_SYSMENU,winUser.OBJID_MENU) and childID==0: 104 # This is a focus event on a menu bar itself, which is just silly. Ignore it. 105 return 106 #We do not need a focus event on an object if we already got a foreground event for it 107 if (winUser.EVENT_SYSTEM_FOREGROUND,window,objectID,childID,threadID) in self._focusEventCache: 108 return 109 self._focusEventCache[(eventID,window,objectID,childID,threadID)]=next(self._eventCounter) 110 return 111 elif eventID==winUser.EVENT_SYSTEM_FOREGROUND: 112 self._focusEventCache.pop((winUser.EVENT_OBJECT_FOCUS,window,objectID,childID,threadID),None) 113 self._focusEventCache[(eventID,window,objectID,childID,threadID)]=next(self._eventCounter) 114 elif eventID==winUser.EVENT_OBJECT_SHOW: 115 k=(winUser.EVENT_OBJECT_HIDE,window,objectID,childID,threadID) 116 if k in self._genericEventCache: 117 del self._genericEventCache[k] 118 return 119 elif eventID==winUser.EVENT_OBJECT_HIDE: 120 k=(winUser.EVENT_OBJECT_SHOW,window,objectID,childID,threadID) 121 if k in self._genericEventCache: 122 del self._genericEventCache[k] 123 return 124 elif eventID==winUser.EVENT_OBJECT_DESTROY: 125 k=(winUser.EVENT_OBJECT_CREATE,window,objectID,childID,threadID) 126 if k in self._genericEventCache: 127 del self._genericEventCache[k] 128 return 129 elif eventID in MENU_EVENTIDS: 130 self._lastMenuEvent=(next(self._eventCounter),eventID,window,objectID,childID,threadID) 131 return 132 self._genericEventCache[(eventID,window,objectID,childID,threadID)]=next(self._eventCounter)
133
134 - def flushEvents(self):
135 """Returns a list of winEvents (tuples of eventID,window,objectID,childID) that have been added, though due to limiting, it will not necessarily be all the winEvents that were originally added. They are definitely garenteed to be in the correct order though. 136 """ 137 if self._lastMenuEvent is not None: 138 heapq.heappush(self._eventHeap,self._lastMenuEvent) 139 self._lastMenuEvent=None 140 g=self._genericEventCache 141 self._genericEventCache={} 142 threadCounters={} 143 for k,v in sorted(g.iteritems(),key=lambda item: item[1],reverse=True): 144 threadCount=threadCounters.get(k[-1],0) 145 if threadCount>MAX_WINEVENTS_PER_THREAD: 146 continue 147 heapq.heappush(self._eventHeap,(v,)+k) 148 threadCounters[k[-1]]=threadCount+1 149 f=self._focusEventCache 150 self._focusEventCache={} 151 for k,v in sorted(f.iteritems(),key=lambda item: item[1])[0-self.maxFocusItems:]: 152 heapq.heappush(self._eventHeap,(v,)+k) 153 e=self._eventHeap 154 self._eventHeap=[] 155 r=[] 156 for count in xrange(len(e)): 157 event=heapq.heappop(e)[1:-1] 158 r.append(event) 159 return r
160 161 #The win event limiter for all winEvents 162 winEventLimiter=OrderedWinEventLimiter() 163 164 #A place to store live IAccessible NVDAObjects, that can be looked up by their window,objectID,childID event params. 165 liveNVDAObjectTable=weakref.WeakValueDictionary() 166 167 IAccessibleRolesToNVDARoles={ 168 oleacc.ROLE_SYSTEM_WINDOW:controlTypes.ROLE_WINDOW, 169 oleacc.ROLE_SYSTEM_CLIENT:controlTypes.ROLE_PANE, 170 oleacc.ROLE_SYSTEM_TITLEBAR:controlTypes.ROLE_TITLEBAR, 171 oleacc.ROLE_SYSTEM_DIALOG:controlTypes.ROLE_DIALOG, 172 oleacc.ROLE_SYSTEM_PANE:controlTypes.ROLE_PANE, 173 oleacc.ROLE_SYSTEM_CHECKBUTTON:controlTypes.ROLE_CHECKBOX, 174 oleacc.ROLE_SYSTEM_RADIOBUTTON:controlTypes.ROLE_RADIOBUTTON, 175 oleacc.ROLE_SYSTEM_STATICTEXT:controlTypes.ROLE_STATICTEXT, 176 oleacc.ROLE_SYSTEM_TEXT:controlTypes.ROLE_EDITABLETEXT, 177 oleacc.ROLE_SYSTEM_PUSHBUTTON:controlTypes.ROLE_BUTTON, 178 oleacc.ROLE_SYSTEM_MENUBAR:controlTypes.ROLE_MENUBAR, 179 oleacc.ROLE_SYSTEM_MENUITEM:controlTypes.ROLE_MENUITEM, 180 oleacc.ROLE_SYSTEM_MENUPOPUP:controlTypes.ROLE_POPUPMENU, 181 oleacc.ROLE_SYSTEM_COMBOBOX:controlTypes.ROLE_COMBOBOX, 182 oleacc.ROLE_SYSTEM_LIST:controlTypes.ROLE_LIST, 183 oleacc.ROLE_SYSTEM_LISTITEM:controlTypes.ROLE_LISTITEM, 184 oleacc.ROLE_SYSTEM_GRAPHIC:controlTypes.ROLE_GRAPHIC, 185 oleacc.ROLE_SYSTEM_HELPBALLOON:controlTypes.ROLE_HELPBALLOON, 186 oleacc.ROLE_SYSTEM_TOOLTIP:controlTypes.ROLE_TOOLTIP, 187 oleacc.ROLE_SYSTEM_LINK:controlTypes.ROLE_LINK, 188 oleacc.ROLE_SYSTEM_OUTLINE:controlTypes.ROLE_TREEVIEW, 189 oleacc.ROLE_SYSTEM_OUTLINEITEM:controlTypes.ROLE_TREEVIEWITEM, 190 oleacc.ROLE_SYSTEM_OUTLINEBUTTON:controlTypes.ROLE_TREEVIEWITEM, 191 oleacc.ROLE_SYSTEM_PAGETAB:controlTypes.ROLE_TAB, 192 oleacc.ROLE_SYSTEM_PAGETABLIST:controlTypes.ROLE_TABCONTROL, 193 oleacc.ROLE_SYSTEM_SLIDER:controlTypes.ROLE_SLIDER, 194 oleacc.ROLE_SYSTEM_PROGRESSBAR:controlTypes.ROLE_PROGRESSBAR, 195 oleacc.ROLE_SYSTEM_SCROLLBAR:controlTypes.ROLE_SCROLLBAR, 196 oleacc.ROLE_SYSTEM_STATUSBAR:controlTypes.ROLE_STATUSBAR, 197 oleacc.ROLE_SYSTEM_TABLE:controlTypes.ROLE_TABLE, 198 oleacc.ROLE_SYSTEM_CELL:controlTypes.ROLE_TABLECELL, 199 oleacc.ROLE_SYSTEM_COLUMN:controlTypes.ROLE_TABLECOLUMN, 200 oleacc.ROLE_SYSTEM_ROW:controlTypes.ROLE_TABLEROW, 201 oleacc.ROLE_SYSTEM_TOOLBAR:controlTypes.ROLE_TOOLBAR, 202 oleacc.ROLE_SYSTEM_COLUMNHEADER:controlTypes.ROLE_TABLECOLUMNHEADER, 203 oleacc.ROLE_SYSTEM_ROWHEADER:controlTypes.ROLE_TABLEROWHEADER, 204 oleacc.ROLE_SYSTEM_SPLITBUTTON:controlTypes.ROLE_SPLITBUTTON, 205 oleacc.ROLE_SYSTEM_BUTTONDROPDOWN:controlTypes.ROLE_DROPDOWNBUTTON, 206 oleacc.ROLE_SYSTEM_SEPARATOR:controlTypes.ROLE_SEPARATOR, 207 oleacc.ROLE_SYSTEM_DOCUMENT:controlTypes.ROLE_DOCUMENT, 208 oleacc.ROLE_SYSTEM_ANIMATION:controlTypes.ROLE_ANIMATION, 209 oleacc.ROLE_SYSTEM_APPLICATION:controlTypes.ROLE_APPLICATION, 210 oleacc.ROLE_SYSTEM_GROUPING:controlTypes.ROLE_GROUPING, 211 oleacc.ROLE_SYSTEM_PROPERTYPAGE:controlTypes.ROLE_PROPERTYPAGE, 212 oleacc.ROLE_SYSTEM_ALERT:controlTypes.ROLE_ALERT, 213 oleacc.ROLE_SYSTEM_BORDER:controlTypes.ROLE_BORDER, 214 oleacc.ROLE_SYSTEM_BUTTONDROPDOWNGRID:controlTypes.ROLE_DROPDOWNBUTTONGRID, 215 oleacc.ROLE_SYSTEM_CARET:controlTypes.ROLE_CARET, 216 oleacc.ROLE_SYSTEM_CHARACTER:controlTypes.ROLE_CHARACTER, 217 oleacc.ROLE_SYSTEM_CHART:controlTypes.ROLE_CHART, 218 oleacc.ROLE_SYSTEM_CURSOR:controlTypes.ROLE_CURSOR, 219 oleacc.ROLE_SYSTEM_DIAGRAM:controlTypes.ROLE_DIAGRAM, 220 oleacc.ROLE_SYSTEM_DIAL:controlTypes.ROLE_DIAL, 221 oleacc.ROLE_SYSTEM_DROPLIST:controlTypes.ROLE_DROPLIST, 222 oleacc.ROLE_SYSTEM_BUTTONMENU:controlTypes.ROLE_MENUBUTTON, 223 oleacc.ROLE_SYSTEM_EQUATION:controlTypes.ROLE_EQUATION, 224 oleacc.ROLE_SYSTEM_GRIP:controlTypes.ROLE_GRIP, 225 oleacc.ROLE_SYSTEM_HOTKEYFIELD:controlTypes.ROLE_HOTKEYFIELD, 226 oleacc.ROLE_SYSTEM_INDICATOR:controlTypes.ROLE_INDICATOR, 227 oleacc.ROLE_SYSTEM_SPINBUTTON:controlTypes.ROLE_SPINBUTTON, 228 oleacc.ROLE_SYSTEM_SOUND:controlTypes.ROLE_SOUND, 229 oleacc.ROLE_SYSTEM_WHITESPACE:controlTypes.ROLE_WHITESPACE, 230 oleacc.ROLE_SYSTEM_IPADDRESS:controlTypes.ROLE_IPADDRESS, 231 oleacc.ROLE_SYSTEM_OUTLINEBUTTON:controlTypes.ROLE_TREEVIEWBUTTON, 232 oleacc.ROLE_SYSTEM_CLOCK:controlTypes.ROLE_CLOCK, 233 #IAccessible2 roles 234 IA2_ROLE_UNKNOWN:controlTypes.ROLE_UNKNOWN, 235 IA2_ROLE_CANVAS:controlTypes.ROLE_CANVAS, 236 IA2_ROLE_CAPTION:controlTypes.ROLE_CAPTION, 237 IA2_ROLE_CHECK_MENU_ITEM:controlTypes.ROLE_CHECKMENUITEM, 238 IA2_ROLE_COLOR_CHOOSER:controlTypes.ROLE_COLORCHOOSER, 239 IA2_ROLE_DATE_EDITOR:controlTypes.ROLE_DATEEDITOR, 240 IA2_ROLE_DESKTOP_ICON:controlTypes.ROLE_DESKTOPICON, 241 IA2_ROLE_DESKTOP_PANE:controlTypes.ROLE_DESKTOPPANE, 242 IA2_ROLE_DIRECTORY_PANE:controlTypes.ROLE_DIRECTORYPANE, 243 IA2_ROLE_EDITBAR:controlTypes.ROLE_EDITBAR, 244 IA2_ROLE_EMBEDDED_OBJECT:controlTypes.ROLE_EMBEDDEDOBJECT, 245 IA2_ROLE_ENDNOTE:controlTypes.ROLE_ENDNOTE, 246 IA2_ROLE_FILE_CHOOSER:controlTypes.ROLE_FILECHOOSER, 247 IA2_ROLE_FONT_CHOOSER:controlTypes.ROLE_FONTCHOOSER, 248 IA2_ROLE_FOOTER:controlTypes.ROLE_FOOTER, 249 IA2_ROLE_FOOTNOTE:controlTypes.ROLE_FOOTNOTE, 250 IA2_ROLE_FORM:controlTypes.ROLE_FORM, 251 IA2_ROLE_FRAME:controlTypes.ROLE_FRAME, 252 IA2_ROLE_GLASS_PANE:controlTypes.ROLE_GLASSPANE, 253 IA2_ROLE_HEADER:controlTypes.ROLE_HEADER, 254 IA2_ROLE_HEADING:controlTypes.ROLE_HEADING, 255 IA2_ROLE_ICON:controlTypes.ROLE_ICON, 256 IA2_ROLE_IMAGE_MAP:controlTypes.ROLE_IMAGEMAP, 257 IA2_ROLE_INPUT_METHOD_WINDOW:controlTypes.ROLE_INPUTWINDOW, 258 IA2_ROLE_INTERNAL_FRAME:controlTypes.ROLE_INTERNALFRAME, 259 IA2_ROLE_LABEL:controlTypes.ROLE_LABEL, 260 IA2_ROLE_LAYERED_PANE:controlTypes.ROLE_LAYEREDPANE, 261 IA2_ROLE_NOTE:controlTypes.ROLE_NOTE, 262 IA2_ROLE_OPTION_PANE:controlTypes.ROLE_OPTIONPANE, 263 IA2_ROLE_PAGE:controlTypes.ROLE_PAGE, 264 IA2_ROLE_PARAGRAPH:controlTypes.ROLE_PARAGRAPH, 265 IA2_ROLE_RADIO_MENU_ITEM:controlTypes.ROLE_RADIOMENUITEM, 266 IA2_ROLE_REDUNDANT_OBJECT:controlTypes.ROLE_REDUNDANTOBJECT, 267 IA2_ROLE_ROOT_PANE:controlTypes.ROLE_ROOTPANE, 268 IA2_ROLE_RULER:controlTypes.ROLE_RULER, 269 IA2_ROLE_SCROLL_PANE:controlTypes.ROLE_SCROLLPANE, 270 IA2_ROLE_SECTION:controlTypes.ROLE_SECTION, 271 IA2_ROLE_SHAPE:controlTypes.ROLE_SHAPE, 272 IA2_ROLE_SPLIT_PANE:controlTypes.ROLE_SPLITPANE, 273 IA2_ROLE_TEAR_OFF_MENU:controlTypes.ROLE_TEAROFFMENU, 274 IA2_ROLE_TERMINAL:controlTypes.ROLE_TERMINAL, 275 IA2_ROLE_TEXT_FRAME:controlTypes.ROLE_TEXTFRAME, 276 IA2_ROLE_TOGGLE_BUTTON:controlTypes.ROLE_TOGGLEBUTTON, 277 IA2_ROLE_VIEW_PORT:controlTypes.ROLE_VIEWPORT, 278 #some common string roles 279 "frame":controlTypes.ROLE_FRAME, 280 "iframe":controlTypes.ROLE_INTERNALFRAME, 281 "page":controlTypes.ROLE_PAGE, 282 "form":controlTypes.ROLE_FORM, 283 "div":controlTypes.ROLE_SECTION, 284 "li":controlTypes.ROLE_LISTITEM, 285 "ul":controlTypes.ROLE_LIST, 286 "tbody":controlTypes.ROLE_TABLEBODY, 287 "browser":controlTypes.ROLE_WINDOW, 288 "h1":controlTypes.ROLE_HEADING1, 289 "h2":controlTypes.ROLE_HEADING2, 290 "h3":controlTypes.ROLE_HEADING3, 291 "h4":controlTypes.ROLE_HEADING4, 292 "h5":controlTypes.ROLE_HEADING5, 293 "h6":controlTypes.ROLE_HEADING6, 294 "p":controlTypes.ROLE_PARAGRAPH, 295 "hbox":controlTypes.ROLE_BOX, 296 "embed":controlTypes.ROLE_EMBEDDEDOBJECT, 297 "object":controlTypes.ROLE_EMBEDDEDOBJECT, 298 "applet":controlTypes.ROLE_EMBEDDEDOBJECT, 299 } 300 301 IAccessibleStatesToNVDAStates={ 302 oleacc.STATE_SYSTEM_TRAVERSED:controlTypes.STATE_VISITED, 303 oleacc.STATE_SYSTEM_UNAVAILABLE:controlTypes.STATE_UNAVAILABLE, 304 oleacc.STATE_SYSTEM_FOCUSED:controlTypes.STATE_FOCUSED, 305 oleacc.STATE_SYSTEM_SELECTED:controlTypes.STATE_SELECTED, 306 oleacc.STATE_SYSTEM_BUSY:controlTypes.STATE_BUSY, 307 oleacc.STATE_SYSTEM_PRESSED:controlTypes.STATE_PRESSED, 308 oleacc.STATE_SYSTEM_CHECKED:controlTypes.STATE_CHECKED, 309 oleacc.STATE_SYSTEM_MIXED:controlTypes.STATE_HALFCHECKED, 310 oleacc.STATE_SYSTEM_READONLY:controlTypes.STATE_READONLY, 311 oleacc.STATE_SYSTEM_EXPANDED:controlTypes.STATE_EXPANDED, 312 oleacc.STATE_SYSTEM_COLLAPSED:controlTypes.STATE_COLLAPSED, 313 oleacc.STATE_SYSTEM_OFFSCREEN:controlTypes.STATE_OFFSCREEN, 314 oleacc.STATE_SYSTEM_INVISIBLE:controlTypes.STATE_INVISIBLE, 315 oleacc.STATE_SYSTEM_TRAVERSED:controlTypes.STATE_VISITED, 316 oleacc.STATE_SYSTEM_LINKED:controlTypes.STATE_LINKED, 317 oleacc.STATE_SYSTEM_HASPOPUP:controlTypes.STATE_HASPOPUP, 318 oleacc.STATE_SYSTEM_PROTECTED:controlTypes.STATE_PROTECTED, 319 oleacc.STATE_SYSTEM_SELECTABLE:controlTypes.STATE_SELECTABLE, 320 oleacc.STATE_SYSTEM_FOCUSABLE:controlTypes.STATE_FOCUSABLE, 321 } 322 323 IAccessible2StatesToNVDAStates={ 324 IA2_STATE_REQUIRED:controlTypes.STATE_REQUIRED, 325 IA2_STATE_DEFUNCT:controlTypes.STATE_DEFUNCT, 326 #IA2_STATE_STALE:controlTypes.STATE_DEFUNCT, 327 IA2_STATE_INVALID_ENTRY:controlTypes.STATE_INVALID_ENTRY, 328 IA2_STATE_MODAL:controlTypes.STATE_MODAL, 329 IA2_STATE_SUPPORTS_AUTOCOMPLETION:controlTypes.STATE_AUTOCOMPLETE, 330 IA2_STATE_MULTI_LINE:controlTypes.STATE_MULTILINE, 331 IA2_STATE_ICONIFIED:controlTypes.STATE_ICONIFIED, 332 IA2_STATE_EDITABLE:controlTypes.STATE_EDITABLE, 333 } 334 335 #A list to store handles received from setWinEventHook, for use with unHookWinEvent 336 winEventHookIDs=[] 337
338 -def normalizeIAccessible(pacc):
339 if not isinstance(pacc,IAccessible): 340 try: 341 pacc=pacc.QueryInterface(IAccessible) 342 except COMError: 343 raise RuntimeError("%s Not an IAccessible"%pacc) 344 if not isinstance(pacc,IAccessible2): 345 try: 346 s=pacc.QueryInterface(IServiceProvider) 347 pacc2=s.QueryService(IAccessible._iid_,IAccessible2) 348 if not pacc2: 349 # QueryService should fail if IA2 is not supported, but some applications such as AIM 7 misbehave and return a null COM pointer. 350 # Treat this as if QueryService failed. 351 raise ValueError 352 pacc=pacc2 353 except: 354 pass 355 return pacc
356
357 -def accessibleObjectFromEvent(window,objectID,childID):
358 try: 359 pacc,childID=oleacc.AccessibleObjectFromEvent(window,objectID,childID) 360 except Exception as e: 361 log.debugWarning("oleacc.AccessibleObjectFromEvent with window %s, objectID %s and childID %s: %s"%(window,objectID,childID,e)) 362 return None 363 return (normalizeIAccessible(pacc),childID)
364
365 -def accessibleObjectFromPoint(x,y):
366 try: 367 pacc, child = oleacc.AccessibleObjectFromPoint(x, y) 368 except: 369 return None 370 return (normalizeIAccessible(pacc),child)
371
372 -def windowFromAccessibleObject(ia):
373 try: 374 return oleacc.WindowFromAccessibleObject(ia) 375 except: 376 return 0
377
378 -def accessibleChildren(ia,startIndex,numChildren):
379 children=oleacc.AccessibleChildren(ia,startIndex,numChildren) 380 for childNum in xrange(len(children)): 381 if isinstance(children[childNum],comtypes.client.lazybind.Dispatch) or isinstance(children[childNum],comtypes.client.dynamic._Dispatch) or isinstance(children[childNum],IUnknown): 382 children[childNum]=(normalizeIAccessible(children[childNum]),0) 383 elif isinstance(children[childNum],int): 384 children[childNum]=(ia,children[childNum]) 385 return children
386
387 -def accFocus(ia):
388 try: 389 res=ia.accFocus 390 if isinstance(res,comtypes.client.lazybind.Dispatch) or isinstance(res,comtypes.client.dynamic._Dispatch) or isinstance(res,IUnknown): 391 new_ia=normalizeIAccessible(res) 392 new_child=0 393 elif isinstance(res,int): 394 try: 395 new_ia=ia.accChild(res) 396 except: 397 new_ia=None 398 if new_ia: 399 new_ia=normalizeIAccessible(new_ia) 400 new_child=0 401 else: 402 new_ia=ia 403 new_child=res 404 else: 405 return None 406 return (new_ia,new_child) 407 except: 408 return None
409
410 -def accHitTest(ia,x,y):
411 try: 412 res=ia.accHitTest(x,y) 413 except COMError: 414 return None 415 if isinstance(res,comtypes.client.lazybind.Dispatch) or isinstance(res,comtypes.client.dynamic._Dispatch) or isinstance(res,IUnknown): 416 return accHitTest(normalizeIAccessible(res),x,y),0 417 elif isinstance(res,int): 418 return ia,res 419 return None
420
421 -def accChild(ia,child):
422 try: 423 res=ia.accChild(child) 424 if isinstance(res,comtypes.client.lazybind.Dispatch) or isinstance(res,comtypes.client.dynamic._Dispatch) or isinstance(res,IUnknown): 425 new_ia=normalizeIAccessible(res) 426 new_child=0 427 elif isinstance(res,int): 428 new_ia=ia 429 new_child=res 430 return (new_ia,new_child) 431 except: 432 return None
433
434 -def accParent(ia,child):
435 try: 436 if not child: 437 res=ia.accParent 438 if isinstance(res,comtypes.client.lazybind.Dispatch) or isinstance(res,comtypes.client.dynamic._Dispatch) or isinstance(res,IUnknown): 439 new_ia=normalizeIAccessible(res) 440 new_child=0 441 else: 442 raise ValueError("no IAccessible interface") 443 else: 444 new_ia=ia 445 new_child=0 446 return (new_ia,new_child) 447 except: 448 return None
449
450 -def accNavigate(ia,child,direction):
451 res=None 452 try: 453 res=ia.accNavigate(direction,child) 454 if isinstance(res,int): 455 new_ia=ia 456 new_child=res 457 elif isinstance(res,comtypes.client.lazybind.Dispatch) or isinstance(res,comtypes.client.dynamic._Dispatch) or isinstance(res,IUnknown): 458 new_ia=normalizeIAccessible(res) 459 new_child=0 460 else: 461 raise RuntimeError 462 return (new_ia,new_child) 463 except: 464 pass
465 466 467 winEventIDsToNVDAEventNames={ 468 winUser.EVENT_SYSTEM_DESKTOPSWITCH:"desktopSwitch", 469 winUser.EVENT_SYSTEM_FOREGROUND:"gainFocus", 470 winUser.EVENT_SYSTEM_ALERT:"alert", 471 winUser.EVENT_SYSTEM_MENUSTART:"menuStart", 472 winUser.EVENT_SYSTEM_MENUEND:"menuEnd", 473 winUser.EVENT_SYSTEM_MENUPOPUPSTART:"menuStart", 474 winUser.EVENT_SYSTEM_MENUPOPUPEND:"menuEnd", 475 winUser.EVENT_SYSTEM_SCROLLINGSTART:"scrollingStart", 476 # We don't need switchStart. 477 winUser.EVENT_SYSTEM_SWITCHEND:"switchEnd", 478 winUser.EVENT_OBJECT_FOCUS:"gainFocus", 479 winUser.EVENT_OBJECT_SHOW:"show", 480 winUser.EVENT_OBJECT_DESTROY:"destroy", 481 winUser.EVENT_OBJECT_DESCRIPTIONCHANGE:"descriptionChange", 482 winUser.EVENT_OBJECT_LOCATIONCHANGE:"locationChange", 483 winUser.EVENT_OBJECT_NAMECHANGE:"nameChange", 484 #winUser.EVENT_OBJECT_REORDER:"reorder", 485 winUser.EVENT_OBJECT_SELECTION:"selection", 486 winUser.EVENT_OBJECT_SELECTIONADD:"selectionAdd", 487 winUser.EVENT_OBJECT_SELECTIONREMOVE:"selectionRemove", 488 winUser.EVENT_OBJECT_SELECTIONWITHIN:"selectionWithIn", 489 winUser.EVENT_OBJECT_STATECHANGE:"stateChange", 490 winUser.EVENT_OBJECT_VALUECHANGE:"valueChange", 491 IA2_EVENT_TEXT_CARET_MOVED:"caret", 492 IA2_EVENT_DOCUMENT_LOAD_COMPLETE:"documentLoadComplete", 493 IA2_EVENT_OBJECT_ATTRIBUTE_CHANGED:"IA2AttributeChange", 494 } 495
496 -def winEventToNVDAEvent(eventID,window,objectID,childID,useCache=True):
497 """Tries to convert a win event ID to an NVDA event name, and instanciate or fetch an NVDAObject for the win event parameters. 498 @param eventID: the win event ID (type) 499 @type eventID: integer 500 @param window: the win event's window handle 501 @type window: integer 502 @param objectID: the win event's object ID 503 @type objectID: integer 504 @param childID: the win event's childID 505 @type childID: the win event's childID 506 @param useCache: C{True} to use the L{liveNVDAObjectTable} cache when retrieving an NVDAObject, C{False} if the cache should not be used. 507 @type useCache: boolean 508 @returns: the NVDA event name and the NVDAObject the event is for 509 @rtype: tuple of string and L{NVDAObjects.IAccessible.IAccessible} 510 """ 511 #We can't handle MSAA create events. (Destroys are handled elsewhere.) 512 if eventID == winUser.EVENT_OBJECT_CREATE: 513 return None 514 #Handle the special MSAA caret object's locationChange and show events as 'caret' events for the focus object 515 NVDAEventName=winEventIDsToNVDAEventNames.get(eventID,None) 516 if not NVDAEventName: 517 return None 518 #Ignore any events with invalid window handles 519 if not window or not winUser.isWindow(window): 520 return None 521 #Make sure this window does not have a ghost window if possible 522 if NVDAObjects.window.GhostWindowFromHungWindow and NVDAObjects.window.GhostWindowFromHungWindow(window): 523 return None 524 #We do not support MSAA object proxied from native UIA 525 if UIAHandler.handler and UIAHandler.handler.isUIAWindow(window): 526 return None 527 obj=None 528 if useCache: 529 #See if we already know an object by this win event info 530 obj=liveNVDAObjectTable.get((window,objectID,childID),None) 531 #If we don't yet have the object, then actually instanciate it. 532 if not obj: 533 obj=NVDAObjects.IAccessible.getNVDAObjectFromEvent(window,objectID,childID) 534 #At this point if we don't have an object then we can't do any more 535 if not obj: 536 return None 537 #SDM MSAA objects sometimes don't contain enough information to be useful 538 #Sometimes there is a real window that does, so try to get the SDMChild property on the NVDAObject, and if successull use that as obj instead. 539 if 'bosa_sdm' in obj.windowClassName: 540 SDMChild=getattr(obj,'SDMChild',None) 541 if SDMChild: obj=SDMChild 542 return (NVDAEventName,obj)
543
544 -def winEventCallback(handle,eventID,window,objectID,childID,threadID,timestamp):
545 try: 546 #Ignore all object IDs from alert onwards (sound, nativeom etc) as we don't support them 547 if objectID<=winUser.OBJID_ALERT: 548 return 549 #Ignore all locationChange events except ones for the caret 550 if eventID==winUser.EVENT_OBJECT_LOCATIONCHANGE and objectID!=winUser.OBJID_CARET: 551 return 552 #Change window objIDs to client objIDs for better reporting of objects 553 if (objectID==0) and (childID==0): 554 objectID=winUser.OBJID_CLIENT 555 #Ignore events with invalid window handles 556 isWindow = winUser.isWindow(window) if window else 0 557 if window==0 or (not isWindow and eventID in (winUser.EVENT_SYSTEM_SWITCHSTART,winUser.EVENT_SYSTEM_SWITCHEND,winUser.EVENT_SYSTEM_MENUEND,winUser.EVENT_SYSTEM_MENUPOPUPEND)): 558 window=winUser.getDesktopWindow() 559 elif not isWindow: 560 return 561 562 if childID<0: 563 tempWindow=window 564 while tempWindow and not winUser.getWindowStyle(tempWindow)&winUser.WS_POPUP and winUser.getClassName(tempWindow)=="MozillaWindowClass": 565 tempWindow=winUser.getAncestor(tempWindow,winUser.GA_PARENT) 566 if tempWindow and winUser.getClassName(tempWindow).startswith('Mozilla'): 567 window=tempWindow 568 569 windowClassName=winUser.getClassName(window) 570 #At the moment we can't handle show, hide or reorder events on Mozilla Firefox Location bar,as there are just too many of them 571 #Ignore show, hide and reorder on MozillaDropShadowWindowClass windows. 572 if windowClassName.startswith('Mozilla') and eventID in (winUser.EVENT_OBJECT_SHOW,winUser.EVENT_OBJECT_HIDE,winUser.EVENT_OBJECT_REORDER) and childID<0: 573 #Mozilla Gecko can sometimes fire win events on a catch-all window which isn't really the real window 574 #Move up the ancestry to find the real mozilla Window and use that 575 if winUser.getClassName(window)=='MozillaDropShadowWindowClass': 576 return 577 #We never want to see foreground events for the Program Manager or Shell (task bar) 578 if eventID==winUser.EVENT_SYSTEM_FOREGROUND and windowClassName in ("Progman","Shell_TrayWnd"): 579 return 580 if windowClassName=="MSNHiddenWindowClass": 581 # HACK: Events get fired by this window in Windows Live Messenger 2009 when it starts. 582 # If we send a WM_NULL to this window at this point (which happens in accessibleObjectFromEvent), Messenger will silently exit (#677). 583 # Therefore, completely ignore these events, which is useless to us anyway. 584 return 585 winEventLimiter.addEvent(eventID,window,objectID,childID,threadID) 586 except: 587 log.error("winEventCallback", exc_info=True)
588
589 -def processGenericWinEvent(eventID,window,objectID,childID):
590 """Converts the win event to an NVDA event, 591 Checks to see if this NVDAObject equals the current focus. 592 If all goes well, then the event is queued and we return True 593 @param eventID: a win event ID (type) 594 @type eventID: integer 595 @param window: a win event's window handle 596 @type window: integer 597 @param objectID: a win event's object ID 598 @type objectID: integer 599 @param childID: a win event's child ID 600 @type childID: integer 601 @returns: True if the event was processed, False otherwise. 602 @rtype: boolean 603 """ 604 #Notify appModuleHandler of this new window 605 appModuleHandler.update(winUser.getWindowThreadProcessID(window)[0]) 606 #Handle particular events for the special MSAA caret object just as if they were for the focus object 607 focus=eventHandler.lastQueuedFocusObject 608 if focus and objectID==winUser.OBJID_CARET and eventID in (winUser.EVENT_OBJECT_LOCATIONCHANGE,winUser.EVENT_OBJECT_SHOW): 609 NVDAEvent=("caret",focus) 610 else: 611 NVDAEvent=winEventToNVDAEvent(eventID,window,objectID,childID) 612 if not NVDAEvent: 613 return False 614 if NVDAEvent[0]=="nameChange" and objectID==winUser.OBJID_CURSOR: 615 mouseHandler.updateMouseShape(NVDAEvent[1].name) 616 return 617 if NVDAEvent[1]==focus: 618 NVDAEvent=(NVDAEvent[0],focus) 619 eventHandler.queueEvent(*NVDAEvent) 620 return True
621
622 -def processFocusWinEvent(window,objectID,childID,force=False):
623 """checks to see if the focus win event is not the same as the existing focus, 624 then converts the win event to an NVDA event (instanciating an NVDA Object) then calls processFocusNVDAEvent. If all is ok it returns True. 625 @type window: integer 626 @param objectID: a win event's object ID 627 @type objectID: integer 628 @param childID: a win event's child ID 629 @type childID: integer 630 @param force: If True, the shouldAllowIAccessibleFocusEvent property of the object is ignored. 631 @type force: boolean 632 @returns: True if the focus is valid and was handled, False otherwise. 633 @rtype: boolean 634 """ 635 windowClassName=winUser.getClassName(window) 636 # Generally, we must ignore focus on child windows of SDM windows as we only want the SDM MSAA events. 637 # However, we don't want to ignore focus if the child ID isn't 0, 638 # as this is a child control and the SDM MSAA events don't handle child controls. 639 if childID==0 and not windowClassName.startswith('bosa_sdm') and winUser.getClassName(winUser.getAncestor(window,winUser.GA_PARENT)).startswith('bosa_sdm'): 640 return False 641 rootWindow=winUser.getAncestor(window,winUser.GA_ROOT) 642 # If this window is not within the foreground window and this window or its root window is not a popup window, and this window's root window is not the highest in the z-order 643 if not winUser.isDescendantWindow(winUser.getForegroundWindow(),window) and not (winUser.getWindowStyle(window) & winUser.WS_POPUP or winUser.getWindowStyle(rootWindow)&winUser.WS_POPUP) and winUser.getPreviousWindow(rootWindow)!=0: 644 # This is a focus event from a background window, so ignore it. 645 return False 646 #Notify appModuleHandler of this new foreground window 647 appModuleHandler.update(winUser.getWindowThreadProcessID(window)[0]) 648 #If Java access bridge is running, and this is a java window, then pass it to java and forget about it 649 if JABHandler.isRunning and JABHandler.isJavaWindow(window): 650 JABHandler.event_enterJavaWindow(window) 651 return True 652 #Convert the win event to an NVDA event 653 NVDAEvent=winEventToNVDAEvent(winUser.EVENT_OBJECT_FOCUS,window,objectID,childID,useCache=False) 654 if not NVDAEvent: 655 return False 656 eventName,obj=NVDAEvent 657 if (childID==0 and obj.IAccessibleRole==oleacc.ROLE_SYSTEM_LIST) or (objectID==winUser.OBJID_CLIENT and "SysListView32" in obj.windowClassName): 658 # Some controls incorrectly fire focus on child ID 0, even when there is a child with focus. 659 try: 660 realChildID=obj.IAccessibleObject.accFocus 661 except: 662 realChildID=None 663 if isinstance(realChildID,int) and realChildID>0 and realChildID!=childID: 664 realObj=NVDAObjects.IAccessible.IAccessible(IAccessibleObject=obj.IAccessibleObject,IAccessibleChildID=realChildID,event_windowHandle=window,event_objectID=objectID,event_childID=realChildID) 665 if realObj: 666 obj=realObj 667 return processFocusNVDAEvent(obj,force=force)
668
669 -def processFocusNVDAEvent(obj,force=False):
670 """Processes a focus NVDA event. 671 If the focus event is valid, it is queued. 672 @param obj: the NVDAObject the focus event is for 673 @type obj: L{NVDAObjects.NVDAObject} 674 @param force: If True, the shouldAllowIAccessibleFocusEvent property of the object is ignored. 675 @type force: boolean 676 @return: C{True} if the focus event is valid and was queued, C{False} otherwise. 677 @rtype: boolean 678 """ 679 if not force and isinstance(obj,NVDAObjects.IAccessible.IAccessible): 680 focus=eventHandler.lastQueuedFocusObject 681 if isinstance(focus,NVDAObjects.IAccessible.IAccessible) and focus.isDuplicateIAccessibleEvent(obj): 682 return True 683 if not obj.shouldAllowIAccessibleFocusEvent: 684 return False 685 eventHandler.queueEvent('gainFocus',obj) 686 return True
687
688 -class SecureDesktopNVDAObject(NVDAObjects.window.Desktop):
689
690 - def findOverlayClasses(self,clsList):
691 clsList.append(SecureDesktopNVDAObject) 692 return clsList
693
694 - def _get_name(self):
695 return _("Secure Desktop")
696
697 - def _get_role(self):
698 return controlTypes.ROLE_PANE
699
700 -def processDesktopSwitchWinEvent(window,objectID,childID):
701 hDesk=ctypes.windll.user32.OpenInputDesktop(0, False, 0) 702 #name = ctypes.create_string_buffer(256) 703 #res=ctypes.windll.user32.GetUserObjectInformationA(desktop, 2, ctypes.byref(name), ctypes.sizeof(name), None) 704 #speech.speakMessage(name.value) 705 if hDesk!=0: 706 ctypes.windll.user32.CloseDesktop(hDesk) 707 import wx 708 wx.CallLater(200, _correctFocus) 709 else: 710 obj=SecureDesktopNVDAObject(windowHandle=window) 711 eventHandler.executeEvent("gainFocus",obj)
712
713 -def _correctFocus():
714 eventHandler.executeEvent("gainFocus",api.getDesktopObject().objectWithFocus())
715
716 -def processForegroundWinEvent(window,objectID,childID):
717 """checks to see if the foreground win event is not the same as the existing focus or any of its parents, 718 then converts the win event to an NVDA event (instanciating an NVDA Object) and then checks the NVDAObject against the existing focus object. 719 If all is ok it queues the foreground event to NVDA and returns True. 720 @param window: a win event's window handle 721 @type window: integer 722 @param objectID: a win event's object ID 723 @type objectID: integer 724 @param childID: a win event's child ID 725 @type childID: integer 726 @returns: True if the foreground was processed, False otherwise. 727 @rtype: boolean 728 """ 729 #Ignore foreground events on windows that aren't the current foreground window 730 if window!=winUser.getForegroundWindow(): 731 return False 732 # If there is a pending gainFocus, it will handle the foreground object. 733 oldFocus=eventHandler.lastQueuedFocusObject 734 #If this foreground win event's window is an ancestor of the existing focus's window, then ignore it 735 if isinstance(oldFocus,NVDAObjects.window.Window) and winUser.isDescendantWindow(window,oldFocus.windowHandle): 736 return False 737 #If the existing focus has the same win event params as these, then ignore this event 738 if isinstance(oldFocus,NVDAObjects.IAccessible.IAccessible) and window==oldFocus.event_windowHandle and objectID==oldFocus.event_objectID and childID==oldFocus.event_childID: 739 return False 740 #Notify appModuleHandler of this new foreground window 741 appModuleHandler.update(winUser.getWindowThreadProcessID(window)[0]) 742 #If Java access bridge is running, and this is a java window, then pass it to java and forget about it 743 if JABHandler.isRunning and JABHandler.isJavaWindow(window): 744 JABHandler.event_enterJavaWindow(window) 745 return True 746 #Convert the win event to an NVDA event 747 NVDAEvent=winEventToNVDAEvent(winUser.EVENT_SYSTEM_FOREGROUND,window,objectID,childID,useCache=False) 748 if not NVDAEvent: 749 return False 750 eventHandler.queueEvent(*NVDAEvent) 751 return True
752
753 -def processShowWinEvent(window,objectID,childID):
754 className=winUser.getClassName(window) 755 #For now we only support 'show' event for tooltips as otherwize we get flooded 756 if className=="tooltips_class32" and objectID==winUser.OBJID_CLIENT: 757 NVDAEvent=winEventToNVDAEvent(winUser.EVENT_OBJECT_SHOW,window,objectID,childID) 758 if NVDAEvent: 759 eventHandler.queueEvent(*NVDAEvent)
760
761 -def processDestroyWinEvent(window,objectID,childID):
762 """Process a destroy win event. 763 This removes the object associated with the event parameters from L{liveNVDAObjectTable} if such an object exists. 764 """ 765 try: 766 del liveNVDAObjectTable[(window,objectID,childID)] 767 except KeyError: 768 pass
769
770 -def processMenuStartWinEvent(eventID, window, objectID, childID, validFocus):
771 """Process a menuStart win event. 772 @postcondition: Focus will be directed to the menu if appropriate. 773 """ 774 if validFocus: 775 lastFocus=eventHandler.lastQueuedFocusObject 776 if isinstance(lastFocus,NVDAObjects.IAccessible.IAccessible) and lastFocus.IAccessibleRole in (oleacc.ROLE_SYSTEM_MENUPOPUP, oleacc.ROLE_SYSTEM_MENUITEM): 777 # Focus has already been set to a menu or menu item, so we don't need to handle the menuStart. 778 return 779 NVDAEvent = winEventToNVDAEvent(eventID, window, objectID, childID) 780 if not NVDAEvent: 781 return 782 eventName, obj = NVDAEvent 783 if obj.IAccessibleRole != oleacc.ROLE_SYSTEM_MENUPOPUP: 784 # menuStart on anything other than a menu is silly. 785 return 786 processFocusNVDAEvent(obj, force=True)
787
788 -def processFakeFocusWinEvent(eventID, window, objectID, childID):
789 """Process a fake focus win event. 790 @postcondition: The focus will be found and an event generated for it if appropriate. 791 """ 792 # A suitable event for faking the focus has been received with no focus event, so we probably need to find the focus and fake it. 793 # However, it is possible that the focus event has simply been delayed, so wait a bit and only do it if the focus hasn't changed yet. 794 import wx 795 wx.CallLater(50, _fakeFocus, api.getFocusObject())
796
797 -def _fakeFocus(oldFocus):
798 if oldFocus is not api.getFocusObject(): 799 # The focus has changed - no need to fake it. 800 return 801 focus = api.getDesktopObject().objectWithFocus() 802 if not focus: 803 return 804 processFocusNVDAEvent(focus)
805 806 #Register internal object event with IAccessible 807 cWinEventCallback=WINFUNCTYPE(None,c_int,c_int,c_int,c_int,c_int,c_int,c_int)(winEventCallback) 808 809 accPropServices=None 810
811 -def initialize():
812 global accPropServices 813 try: 814 accPropServices=comtypes.client.CreateObject(CAccPropServices) 815 except (WindowsError,COMError) as e: 816 log.debugWarning("AccPropServices is not available: %s"%e) 817 for eventType in winEventIDsToNVDAEventNames.keys(): 818 hookID=winUser.setWinEventHook(eventType,eventType,0,cWinEventCallback,0,0,0) 819 if hookID: 820 winEventHookIDs.append(hookID) 821 else: 822 log.error("initialize: could not register callback for event %s (%s)"%(eventType,winEventIDsToNVDAEventNames[eventType]))
823
824 -def pumpAll():
825 #Receive all the winEvents from the limiter for this cycle 826 winEvents=winEventLimiter.flushEvents() 827 focusWinEvents=[] 828 validFocus=False 829 fakeFocusEvent=None 830 for winEvent in winEvents[0-MAX_WINEVENTS:]: 831 #We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one 832 if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS,winUser.EVENT_SYSTEM_FOREGROUND): 833 focusWinEvents.append(winEvent) 834 continue 835 else: 836 for focusWinEvent in reversed(focusWinEvents): 837 procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent 838 if procFunc(*(focusWinEvent[1:])): 839 validFocus=True 840 break 841 focusWinEvents=[] 842 if winEvent[0]==winUser.EVENT_SYSTEM_DESKTOPSWITCH: 843 processDesktopSwitchWinEvent(*winEvent[1:]) 844 elif winEvent[0]==winUser.EVENT_OBJECT_DESTROY: 845 processDestroyWinEvent(*winEvent[1:]) 846 elif winEvent[0]==winUser.EVENT_OBJECT_SHOW: 847 processShowWinEvent(*winEvent[1:]) 848 elif winEvent[0] in MENU_EVENTIDS+(winUser.EVENT_SYSTEM_SWITCHEND,): 849 # If there is no valid focus event, we may need to use this to fake the focus later. 850 fakeFocusEvent=winEvent 851 else: 852 processGenericWinEvent(*winEvent) 853 for focusWinEvent in reversed(focusWinEvents): 854 procFunc=processForegroundWinEvent if focusWinEvent[0]==winUser.EVENT_SYSTEM_FOREGROUND else processFocusWinEvent 855 if procFunc(*(focusWinEvent[1:])): 856 validFocus=True 857 break 858 if fakeFocusEvent: 859 # Try this as a last resort. 860 if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART): 861 # menuStart needs to be handled specially and might act even if there was a valid focus event. 862 processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus) 863 elif not validFocus: 864 # Other fake focus events only need to be handled if there was no valid focus event. 865 processFakeFocusWinEvent(*fakeFocusEvent)
866
867 -def terminate():
868 for handle in winEventHookIDs: 869 winUser.unhookWinEvent(handle)
870
871 -def getIAccIdentity(pacc,childID):
872 IAccIdentityObject=pacc.QueryInterface(IAccIdentity) 873 stringPtr,stringSize=IAccIdentityObject.getIdentityString(childID) 874 try: 875 if accPropServices: 876 hwnd,objectID,childID=accPropServices.DecomposeHwndIdentityString(stringPtr,stringSize) 877 return dict(windowHandle=hwnd,objectID=c_int(objectID).value,childID=childID) 878 stringPtr=cast(stringPtr,POINTER(c_char*stringSize)) 879 fields=struct.unpack('IIiI',stringPtr.contents.raw) 880 d={} 881 d['childID']=fields[3] 882 if fields[0]&2: 883 d['menuHandle']=fields[2] 884 else: 885 d['objectID']=fields[2] 886 d['windowHandle']=fields[1] 887 return d 888 finally: 889 windll.ole32.CoTaskMemFree(stringPtr)
890 891
892 -def findGroupboxObject(obj):
893 prevWindow=winUser.getPreviousWindow(obj.windowHandle) 894 while prevWindow: 895 if winUser.getClassName(prevWindow)=="Button" and winUser.getWindowStyle(prevWindow)&winUser.BS_GROUPBOX: 896 groupObj=NVDAObjects.IAccessible.getNVDAObjectFromEvent(prevWindow,winUser.OBJID_CLIENT,0) 897 try: 898 (left,top,width,height)=obj.location 899 (groupLeft,groupTop,groupWidth,groupHeight)=groupObj.location 900 except: 901 return 902 if groupObj.IAccessibleRole==oleacc.ROLE_SYSTEM_GROUPING and left>=groupLeft and (left+width)<=(groupLeft+groupWidth) and top>=groupTop and (top+height)<=(groupTop+groupHeight): 903 return groupObj 904 prevWindow=winUser.getPreviousWindow(prevWindow)
905
906 -def getRecursiveTextFromIAccessibleTextObject(obj,startOffset=0,endOffset=-1):
907 if not isinstance(obj,IAccessibleText): 908 try: 909 textObject=obj.QueryInterface(IAccessibleText) 910 except: 911 textObject=None 912 else: 913 textObject=obj 914 if not isinstance(obj,IAccessible): 915 try: 916 accObject=obj.QueryInterface(IAccessible) 917 except: 918 return "" 919 else: 920 accObject=obj 921 try: 922 text=textObject.text(startOffset,endOffset) 923 except: 924 text=None 925 if not text or text.isspace(): 926 try: 927 name=accObject.accName(0) 928 except: 929 name=None 930 try: 931 value=accObject.accValue(0) 932 except: 933 value=None 934 try: 935 description=accObject.accDescription(0) 936 except: 937 description=None 938 return " ".join([x for x in [name,value,description] if x and not x.isspace()]) 939 try: 940 hypertextObject=accObject.QueryInterface(IAccessibleHypertext) 941 except: 942 return text 943 textList=[] 944 for i in range(len(text)): 945 t=text[i] 946 if ord(t)==0xFFFC: 947 try: 948 childTextObject=hypertextObject.hyperlink(hypertextObject.hyperlinkIndex(i+startOffset)).QueryInterface(IAccessible) 949 t=" %s "%getRecursiveTextFromIAccessibleTextObject(childTextObject) 950 except: 951 pass 952 textList.append(t) 953 return "".join(textList).replace(' ',' ')
954
955 -def splitIA2Attribs(attribsString):
956 """Split an IAccessible2 attributes string into a dict of attribute keys and values. 957 An invalid attributes string does not cause an error, but strange results may be returned. 958 Subattributes are handled. Subattribute keys and values are placed into a dict which becomes the value of the attribute. 959 @param attribsString: The IAccessible2 attributes string to convert. 960 @type attribsString: str 961 @return: A dict of the attribute keys and values, where values are strings or dicts. 962 @rtype: {str: str or {str: str}} 963 """ 964 attribsDict = {} 965 tmp = "" 966 key = "" 967 subkey = "" 968 subattr = {} 969 inEscape = False 970 for char in attribsString: 971 if inEscape: 972 tmp += char 973 inEscape = False 974 elif char == "\\": 975 inEscape = True 976 elif char == ":": 977 # We're about to move on to the value, so save the key and clear tmp. 978 key = tmp 979 tmp = "" 980 elif char == "=": 981 # This is a subattribute. 982 # Save the subattribute key and clear tmp, ready for the value. 983 subkey = tmp 984 tmp = "" 985 elif char == ",": 986 # We're about to move on to a new subattribute. 987 # Add this subattribute key/value pair to the dict. 988 if subkey: 989 subattr[subkey] = tmp 990 subkey = "" 991 tmp = "" 992 elif char == ";": 993 # We're about to move on to a new attribute. 994 if subkey: 995 # Add the last subattribute key/value pair to the dict. 996 subattr[subkey] = tmp 997 subkey = "" 998 if subattr: 999 # This attribute had subattributes. 1000 # Add the key/subattribute pair to the dict. 1001 attribsDict[key] = subattr 1002 subattr = {} 1003 elif key: 1004 # Add this key/value pair to the dict. 1005 attribsDict[key] = tmp 1006 key = "" 1007 tmp = "" 1008 else: 1009 tmp += char 1010 # If there was no trailing semi-colon, we need to handle the last attribute. 1011 if subkey: 1012 # Add the last subattribute key/value pair to the dict. 1013 subattr[subkey] = tmp 1014 if subattr: 1015 # This attribute had subattributes. 1016 # Add the key/subattribute pair to the dict. 1017 attribsDict[key] = subattr 1018 elif key: 1019 # Add this key/value pair to the dict. 1020 attribsDict[key] = tmp 1021 return attribsDict
1022
1023 -def isMarshalledIAccessible(IAccessibleObject):
1024 """Looks at the location of the first function in the IAccessible object's vtable (IUnknown::AddRef) to see if it was implemented in oleacc.dll (its local) or ole32.dll (its marshalled).""" 1025 if not isinstance(IAccessibleObject,IAccessible): 1026 raise TypeError("object should be of type IAccessible, not %s"%IAccessibleObject) 1027 buf=create_unicode_buffer(1024) 1028 from comtypes import _compointer_base 1029 addr=POINTER(c_void_p).from_address(super(_compointer_base,IAccessibleObject).value).contents.value 1030 handle=HANDLE() 1031 windll.kernel32.GetModuleHandleExW(6,addr,byref(handle)) 1032 windll.kernel32.GetModuleFileNameW(handle,buf,1024) 1033 return not buf.value.lower().endswith('oleacc.dll')
1034