logonui.py
4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
#appModules/logonui.py
#A part of NonVisual Desktop Access (NVDA)
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.
#Copyright (C) 2009-2016 NV Access Limited, Joseph Lee
import speech
import api
import braille
import controlTypes
from NVDAObjects.IAccessible import IAccessible
from NVDAObjects.behaviors import Dialog
import appModuleHandler
import eventHandler
import UIAHandler
if UIAHandler.isUIAAvailable:
from NVDAObjects.UIA import UIA
"""App module for the Windows Logon screen
"""
class LogonDialog(Dialog):
role = controlTypes.ROLE_DIALOG
isPresentableFocusAncestor = True
def event_gainFocus(self):
child = self.firstChild
if child and controlTypes.STATE_FOCUSED in child.states and not eventHandler.isPendingEvents("gainFocus"):
# UIA reports that focus is on the top level pane, even when it's actually on the frame below.
# This causes us to incorrectly use UIA for the top level pane, which causes this pane to be spoken again when the focus moves.
# Therefore, bounce the focus to the correct object.
eventHandler.queueEvent("gainFocus", child)
return
return super(LogonDialog, self).event_gainFocus()
if UIAHandler.isUIAAvailable:
class Win8PasswordField(UIA):
#This UIA object has no invoke pattern, at least set focus.
# #6024: Affects both Windows 8.x and 10.
def doAction(self,index=None):
if not index:
self.setFocus()
else:
super(Win8PasswordField,self).doAction(index)
class XPPasswordField(IAccessible):
def initOverlayClass(self):
for gesture in ("kb:upArrow", "kb:downArrow"):
self.bindGesture(gesture, "changeUser")
def _get_name(self):
# Focus automatically jumps to the password field when a user is selected. This field has no name.
# This means that the new selected user is not reported.
# However, the selected user name is the name of the window object for this field.
try:
self.parent.invalidateCache()
return self.parent.name
except:
return super(XPPasswordField, self).name
def script_changeUser(self, gesture):
# The up and down arrow keys change the selected user, but there's no reliable NVDA event for detecting this.
oldName = self.name
gesture.send()
self.invalidateCache()
if oldName == self.name or controlTypes.STATE_FOCUSED not in self.states:
return
self.event_gainFocus()
class AppModule(appModuleHandler.AppModule):
def event_NVDAObject_init(self, obj):
if obj.windowClassName == "NativeHWNDHost" and obj.parent and not obj.parent.parent:
# This is the top level pane of the XP logon screen.
# Make sure it is always presented.
obj.isPresentableFocusAncestor = True
def chooseNVDAObjectOverlayClasses(self, obj, clsList):
windowClass = obj.windowClassName
if UIAHandler.isUIAAvailable:
if isinstance(obj,UIA) and obj.UIAElement.cachedClassName in ("TouchEditInner", "PasswordBox") and obj.role==controlTypes.ROLE_EDITABLETEXT:
clsList.insert(0,Win8PasswordField)
# #6010: Allow Windows 10 version to be recognized as well.
if ((windowClass == "AUTHUI.DLL: LogonUI Logon Window" and obj.parent and obj.parent.parent and not obj.parent.parent.parent)
or (windowClass == "LogonUI Logon Window" and obj.UIAIsWindowElement)):
clsList.insert(0, LogonDialog)
return
if windowClass == "Edit" and not obj.name:
parent = obj.parent
if parent and parent.role == controlTypes.ROLE_WINDOW:
clsList.insert(0, XPPasswordField)
return
def event_gainFocus(self,obj,nextHandler):
# #6010: Windows 10 version uses a different window class name.
if obj.windowClassName in ("DirectUIHWND", "LogonUI Logon Window") and obj.role==controlTypes.ROLE_BUTTON and not obj.next:
prev=obj.previous
if prev and prev.role in (controlTypes.ROLE_STATICTEXT, controlTypes.ROLE_PANE):
# This is for a popup message in the logon dialog.
# Present the dialog again so the message will be reported.
speech.speakObjectProperties(api.getForegroundObject(),name=True,role=True,description=True)
braille.invalidateCachedFocusAncestors(1)
nextHandler()