Skip to content

Commit 0e77fe6

Browse files
authored
Merge fc80111 into 1af8775
2 parents 1af8775 + fc80111 commit 0e77fe6

9 files changed

Lines changed: 137 additions & 60 deletions

File tree

source/NVDAObjects/UIA/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -871,7 +871,7 @@ def findOverlayClasses(self,clsList):
871871
# Support Windows Console's UIA interface
872872
if (
873873
self.windowClassName == "ConsoleWindowClass"
874-
and config.conf['UIA']['winConsoleImplementation'] == "UIA"
874+
and shouldUseUIAConsole()
875875
):
876876
from . import winConsoleUIA
877877
winConsoleUIA.findExtraOverlayClasses(self, clsList)

source/NVDAObjects/UIA/winConsoleUIA.py

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111

1212
from comtypes import COMError
1313
from UIAUtils import isTextRangeOffscreen
14-
from winVersion import isWin10
1514
from . import UIATextInfo
1615
from ..behaviors import KeyboardHandlerBasedTypedCharSupport
1716
from ..window import Window
@@ -24,30 +23,28 @@ class consoleUIATextInfo(UIATextInfo):
2423
#: to do much good either.
2524
_expandCollapseBeforeReview = False
2625

27-
def __init__(self,obj,position,_rangeObj=None):
26+
def __init__(self, obj, position, _rangeObj=None):
2827
super(consoleUIATextInfo, self).__init__(obj, position, _rangeObj)
2928
# Re-implement POSITION_FIRST and POSITION_LAST in terms of
3029
# visible ranges to fix review top/bottom scripts.
31-
if position==textInfos.POSITION_FIRST:
30+
if position == textInfos.POSITION_FIRST:
3231
visiRanges = self.obj.UIATextPattern.GetVisibleRanges()
3332
firstVisiRange = visiRanges.GetElement(0)
3433
self._rangeObj = firstVisiRange
3534
self.collapse()
36-
elif position==textInfos.POSITION_LAST:
35+
elif position == textInfos.POSITION_LAST:
3736
visiRanges = self.obj.UIATextPattern.GetVisibleRanges()
3837
lastVisiRange = visiRanges.GetElement(visiRanges.length - 1)
3938
self._rangeObj = lastVisiRange
4039
self.collapse(True)
4140

42-
def collapse(self,end=False):
43-
"""Works around a UIA bug on Windows 10 1903 and later."""
44-
if not isWin10(1903):
45-
return super(consoleUIATextInfo, self).collapse(end=end)
41+
def collapse(self, end=False):
42+
"""Works around a UIA bug on Windows 10 1803 and later."""
4643
# When collapsing, consoles seem to incorrectly push the start of the
4744
# textRange back one character.
4845
# Correct this by bringing the start back up to where the end is.
49-
oldInfo=self.copy()
50-
super(consoleUIATextInfo,self).collapse(end=end)
46+
oldInfo = self.copy()
47+
super(consoleUIATextInfo, self).collapse(end=end)
5148
if not end:
5249
self._rangeObj.MoveEndpointByRange(
5350
UIAHandler.TextPatternRangeEndpoint_Start,
@@ -157,9 +154,7 @@ def expand(self, unit):
157154
return super(consoleUIATextInfo, self).expand(unit)
158155

159156
def _get_isCollapsed(self):
160-
"""Works around a UIA bug on Windows 10 1903 and later."""
161-
if not isWin10(1903):
162-
return super(consoleUIATextInfo, self)._get_isCollapsed()
157+
"""Works around a UIA bug on Windows 10 1803 and later."""
163158
# Even when a console textRange's start and end have been moved to the
164159
# same position, the console incorrectly reports the end as being
165160
# past the start.
@@ -215,9 +210,9 @@ def _getWordOffsetsInThisLine(self, offset, lineInfo):
215210
min(end.value, max(1, len(lineText) - 2))
216211
)
217212

218-
def __ne__(self,other):
213+
def __ne__(self, other):
219214
"""Support more accurate caret move detection."""
220-
return not self==other
215+
return not self == other
221216

222217

223218
class consoleUIAWindow(Window):

source/NVDAObjects/behaviors.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,7 @@ def event_typedCharacter(self, ch):
413413
config.conf['keyboard']['speakTypedCharacters']
414414
or config.conf['keyboard']['speakTypedWords']
415415
)
416-
and not config.conf['UIA']['winConsoleSpeakPasswords']
416+
and not config.conf['terminals']['speakPasswords']
417417
and self._supportsTextChange
418418
):
419419
self._queuedChars.append(ch)

source/UIAUtils.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
1-
#A part of NonVisual Desktop Access (NVDA)
2-
#Copyright (C) 2015-2016 NV Access Limited
3-
#This file is covered by the GNU General Public License.
4-
#See the file COPYING for more details.
1+
# A part of NonVisual Desktop Access (NVDA)
2+
# Copyright (C) 2015-2019 NV Access Limited, Bill Dengler
3+
# This file is covered by the GNU General Public License.
4+
# See the file COPYING for more details.
55

66
import operator
77
from comtypes import COMError
8+
import config
89
import ctypes
910
import UIAHandler
11+
from winVersion import isWin10
1012

1113
def createUIAMultiPropertyCondition(*dicts):
1214
"""
@@ -220,3 +222,21 @@ def getValue(self,ID,ignoreMixedValues=False):
220222
raise UIAMixedAttributeError
221223
return val
222224

225+
226+
def shouldUseUIAConsole(setting=None):
227+
"""Determines whether to use UIA in the Windows Console.
228+
@param setting: the config value to base this check on (if not provided,
229+
it is retrieved from config).
230+
"""
231+
if not setting:
232+
setting = config.conf['UIA']['winConsoleImplementation']
233+
if setting == "legacy":
234+
return False
235+
elif setting == "UIA":
236+
return True
237+
# #7497: Windows 10 Fall Creators Update has an incomplete UIA
238+
# implementation for console windows, therefore for now we should
239+
# ignore it.
240+
# It does not implement caret/selection, and probably has no
241+
# new text events.
242+
return isWin10(1803)

source/_UIAHandler.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,9 +356,10 @@ def IUIAutomationNotificationEventHandler_HandleNotificationEvent(self,sender,No
356356

357357
def _isBadUIAWindowClassName(self, windowClass):
358358
"Given a windowClassName, returns True if this is a known problematic UIA implementation."
359-
# #7497: Windows 10 Fall Creators Update has an incomplete UIA implementation for console windows, therefore for now we should ignore it.
360-
# It does not implement caret/selection, and probably has no new text events.
361-
if windowClass == "ConsoleWindowClass" and config.conf['UIA']['winConsoleImplementation'] != "UIA":
359+
if (
360+
windowClass == "ConsoleWindowClass"
361+
and not UIAUtils.shouldUseUIAConsole()
362+
):
362363
return True
363364
return windowClass in badUIAWindowClassNames
364365

source/config/configSpec.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@
1111
#: provide an upgrade step (@see profileUpgradeSteps.py). An upgrade step does not need to be added when
1212
#: just adding a new element to (or removing from) the schema, only when old versions of the config
1313
#: (conforming to old schema versions) will not work correctly with the new schema.
14-
latestSchemaVersion = 2
14+
latestSchemaVersion = 3
1515

1616
#: The configuration specification string
1717
#: @type: String
18-
configSpecString = ("""# NVDA Configuration File
18+
configSpecString = (f"""# NVDA Configuration File
1919
schemaVersion = integer(min=0, default={latestSchemaVersion})
2020
[general]
2121
language = string(default="Windows")
@@ -188,9 +188,9 @@
188188
enabled = boolean(default=true)
189189
useInMSWordWhenAvailable = boolean(default=false)
190190
winConsoleImplementation= option("auto", "legacy", "UIA", default="auto")
191-
winConsoleSpeakPasswords = boolean(default=false)
192191
193192
[terminals]
193+
speakPasswords = boolean(default=false)
194194
keyboardSupportInLegacy = boolean(default=True)
195195
196196
[update]
@@ -224,7 +224,7 @@
224224
225225
[development]
226226
enableScratchpadDir = boolean(default=false)
227-
""").format(latestSchemaVersion=latestSchemaVersion)
227+
""")
228228

229229
#: The configuration specification
230230
#: @type: ConfigObj

source/config/profileUpgradeSteps.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,16 @@ def upgradeConfigFrom_1_to_2(profile):
4545
else:
4646
del profile["braille"]["cursorShape"]
4747
profile["braille"]["cursorShapeFocus"] = cursorShape
48+
49+
50+
def upgradeConfigFrom_2_to_3(profile):
51+
# The winConsoleSpeakPasswords option has been moved to the terminals section of the config.
52+
try:
53+
speakPasswords = profile["UIA"]["winConsoleSpeakPasswords"]
54+
except KeyError:
55+
# Setting does not exist, no need for upgrade of this setting
56+
log.debug("winConsoleSpeakPasswords not present, no action taken.")
57+
pass
58+
else:
59+
del profile["UIA"]["winConsoleSpeakPasswords"]
60+
profile["terminals"]["speakPasswords"] = speakPasswords

source/gui/settingsDialogs.py

Lines changed: 66 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
# -*- coding: UTF-8 -*-
2-
#settingsDialogs.py
3-
#A part of NonVisual Desktop Access (NVDA)
4-
#Copyright (C) 2006-2019 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee, Heiko Folkerts, Zahari Yurukov, Leonard de Ruijter, Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger
5-
#This file is covered by the GNU General Public License.
6-
#See the file COPYING for more details.
2+
# settingsDialogs.py
3+
# A part of NonVisual Desktop Access (NVDA)
4+
# Copyright (C) 2006-2019 NV Access Limited, Peter Vágner, Aleksey Sadovoy, Rui Batista, Joseph Lee, Heiko Folkerts, Zahari Yurukov, Leonard de Ruijter, Derek Riemer, Babbage B.V., Davy Kager, Ethan Holliger, Bill Dengler
5+
# This file is covered by the GNU General Public License.
6+
# See the file COPYING for more details.
77

88
import logging
99
from abc import abstractmethod
@@ -42,6 +42,7 @@
4242
import inputCore
4343
from . import nvdaControls
4444
from driverHandler import *
45+
from UIAUtils import shouldUseUIAConsole
4546
import touchHandler
4647
import winVersion
4748
import weakref
@@ -2053,20 +2054,43 @@ def __init__(self, parent):
20532054
self.UIAInMSWordCheckBox.SetValue(config.conf["UIA"]["useInMSWordWhenAvailable"])
20542055
self.UIAInMSWordCheckBox.defaultValue = self._getDefaultValue(["UIA", "useInMSWordWhenAvailable"])
20552056

2056-
# Translators: This is the label for a checkbox in the
2057-
# Advanced settings panel.
2058-
label = _("Use UI Automation to access the Windows Console when available")
2059-
consoleUIADevMap = True if config.conf['UIA']['winConsoleImplementation'] == 'UIA' else False
2060-
self.ConsoleUIACheckBox=UIAGroup.addItem(wx.CheckBox(self, label=label))
2061-
self.ConsoleUIACheckBox.SetValue(consoleUIADevMap)
2062-
self.ConsoleUIACheckBox.defaultValue = self._getDefaultValue(["UIA", "winConsoleImplementation"])
2063-
2064-
# Translators: This is the label for a checkbox in the
2065-
# Advanced settings panel.
2066-
label = _("Speak &passwords in UIA consoles (may improve performance)")
2067-
self.winConsoleSpeakPasswordsCheckBox=UIAGroup.addItem(wx.CheckBox(self, label=label))
2068-
self.winConsoleSpeakPasswordsCheckBox.SetValue(config.conf["UIA"]["winConsoleSpeakPasswords"])
2069-
self.winConsoleSpeakPasswordsCheckBox.defaultValue = self._getDefaultValue(["UIA", "winConsoleSpeakPasswords"])
2057+
# Translators: This is the label for a combo box for selecting the
2058+
# active console implementation in the advanced settings panel.
2059+
# Choices are automatic, prefer UIA, and legacy.
2060+
consoleComboText = _("Windows C&onsole support:")
2061+
consoleChoices = [
2062+
# Translators: A choice in a combo box in the advanced settings
2063+
# panel to have NVDA determine its Windows Console implementation
2064+
# automatically.
2065+
_("Automatic"),
2066+
# Translators: A choice in a combo box in the advanced settings
2067+
# panel to have NVDA use UIA in the Windows Console when available.
2068+
_("Prefer UIA"),
2069+
# Translators: A choice in a combo box in the advanced settings
2070+
# panel to have NVDA use its legacy Windoes Console support
2071+
# in all cases.
2072+
_("Legacy")
2073+
]
2074+
#: The possible console config values, in the order they appear
2075+
#: in the combo box.
2076+
self.consoleVals = (
2077+
"auto",
2078+
"UIA",
2079+
"legacy"
2080+
)
2081+
self.consoleCombo = UIAGroup.addLabeledControl(consoleComboText, wx.Choice, choices=consoleChoices)
2082+
self.consoleCombo.Bind(
2083+
wx.EVT_CHOICE,
2084+
self.enableConsolePasswordsCheckBox,
2085+
self.consoleCombo
2086+
)
2087+
curChoice = self.consoleVals.index(
2088+
config.conf['UIA']['winConsoleImplementation']
2089+
)
2090+
self.consoleCombo.SetSelection(curChoice)
2091+
self.consoleCombo.defaultValue = self.consoleVals.index(
2092+
self._getDefaultValue(["UIA", "winConsoleImplementation"])
2093+
)
20702094

20712095
# Translators: This is the label for a group of advanced options in the
20722096
# Advanced settings panel
@@ -2078,6 +2102,13 @@ def __init__(self, parent):
20782102
sHelper.addItem(terminalsGroup)
20792103
# Translators: This is the label for a checkbox in the
20802104
# Advanced settings panel.
2105+
label = _("Speak &passwords in Windows Console (may improve performance)")
2106+
self.speakPasswordsCheckBox = terminalsGroup.addItem(wx.CheckBox(self, label=label))
2107+
self.speakPasswordsCheckBox.SetValue(config.conf["terminals"]["speakPasswords"])
2108+
self.speakPasswordsCheckBox.defaultValue = self._getDefaultValue(["terminals", "speakPasswords"])
2109+
self.enableConsolePasswordsCheckBox()
2110+
# Translators: This is the label for a checkbox in the
2111+
# Advanced settings panel.
20812112
label = _("Use the new t&yped character support in legacy Windows consoles when available")
20822113
self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label))
20832114
self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"])
@@ -2156,6 +2187,13 @@ def __init__(self, parent):
21562187
]
21572188
self.Layout()
21582189

2190+
def enableConsolePasswordsCheckBox(self, evt=None):
2191+
return self.speakPasswordsCheckBox.Enable(
2192+
shouldUseUIAConsole(self.consoleVals[
2193+
self.consoleCombo.GetSelection()
2194+
])
2195+
)
2196+
21592197
def onOpenScratchpadDir(self,evt):
21602198
path=config.getScratchpadDir(ensureExists=True)
21612199
os.startfile(path)
@@ -2168,8 +2206,8 @@ def haveConfigDefaultsBeenRestored(self):
21682206
self._defaultsRestored and
21692207
self.scratchpadCheckBox.IsChecked() == self.scratchpadCheckBox.defaultValue and
21702208
self.UIAInMSWordCheckBox.IsChecked() == self.UIAInMSWordCheckBox.defaultValue and
2171-
self.ConsoleUIACheckBox.IsChecked() == (self.ConsoleUIACheckBox.defaultValue=='UIA') and
2172-
self.winConsoleSpeakPasswordsCheckBox.IsChecked() == self.winConsoleSpeakPasswordsCheckBox.defaultValue and
2209+
self.consoleCombo.GetSelection() == self.consoleCombo.defaultValue and
2210+
self.speakPasswordsCheckBox.IsChecked() == self.speakPasswordsCheckBox.defaultValue and
21732211
self.keyboardSupportInLegacyCheckBox.IsChecked() == self.keyboardSupportInLegacyCheckBox.defaultValue and
21742212
self.autoFocusFocusableElementsCheckBox.IsChecked() == self.autoFocusFocusableElementsCheckBox.defaultValue and
21752213
self.caretMoveTimeoutSpinControl.GetValue() == self.caretMoveTimeoutSpinControl.defaultValue and
@@ -2180,8 +2218,8 @@ def haveConfigDefaultsBeenRestored(self):
21802218
def restoreToDefaults(self):
21812219
self.scratchpadCheckBox.SetValue(self.scratchpadCheckBox.defaultValue)
21822220
self.UIAInMSWordCheckBox.SetValue(self.UIAInMSWordCheckBox.defaultValue)
2183-
self.ConsoleUIACheckBox.SetValue(self.ConsoleUIACheckBox.defaultValue=='UIA')
2184-
self.winConsoleSpeakPasswordsCheckBox.SetValue(self.winConsoleSpeakPasswordsCheckBox.defaultValue)
2221+
self.consoleCombo.SetSelection(self.consoleCombo.defaultValue == 'UIA')
2222+
self.speakPasswordsCheckBox.SetValue(self.speakPasswordsCheckBox.defaultValue)
21852223
self.keyboardSupportInLegacyCheckBox.SetValue(self.keyboardSupportInLegacyCheckBox.defaultValue)
21862224
self.autoFocusFocusableElementsCheckBox.SetValue(self.autoFocusFocusableElementsCheckBox.defaultValue)
21872225
self.caretMoveTimeoutSpinControl.SetValue(self.caretMoveTimeoutSpinControl.defaultValue)
@@ -2192,11 +2230,11 @@ def onSave(self):
21922230
log.debug("Saving advanced config")
21932231
config.conf["development"]["enableScratchpadDir"]=self.scratchpadCheckBox.IsChecked()
21942232
config.conf["UIA"]["useInMSWordWhenAvailable"]=self.UIAInMSWordCheckBox.IsChecked()
2195-
if self.ConsoleUIACheckBox.IsChecked():
2196-
config.conf['UIA']['winConsoleImplementation'] = "UIA"
2197-
else:
2198-
config.conf['UIA']['winConsoleImplementation'] = "auto"
2199-
config.conf["UIA"]["winConsoleSpeakPasswords"]=self.winConsoleSpeakPasswordsCheckBox.IsChecked()
2233+
consoleChoice = self.consoleCombo.GetSelection()
2234+
config.conf['UIA']['winConsoleImplementation'] = (
2235+
self.consoleVals[consoleChoice]
2236+
)
2237+
config.conf["terminals"]["speakPasswords"] = self.speakPasswordsCheckBox.IsChecked()
22002238
config.conf["terminals"]["keyboardSupportInLegacy"]=self.keyboardSupportInLegacyCheckBox.IsChecked()
22012239
config.conf["virtualBuffers"]["autoFocusFocusableElements"] = self.autoFocusFocusableElementsCheckBox.IsChecked()
22022240
config.conf["editableText"]["caretMoveTimeoutMs"]=self.caretMoveTimeoutSpinControl.GetValue()

user_docs/en/userGuide.t2t

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1686,11 +1686,21 @@ This includes in Microsoft Word itself, and also the Microsoft Outlook message v
16861686
However, There may be some information which is either not exposed, or exposed incorrectly in some versions of Microsoft Office, which means this UI automation support cannot always be relied upon.
16871687
We still do not recommend that the majority of users turn this on by default, though we do welcome users of Office 2016/365 to test this feature and provide feedback.
16881688

1689-
==== Use UI Automation to access the Windows Console when available ====[AdvancedSettingsConsoleUIA]
1690-
When this option is enabled, NVDA will use a new, work in progress version of its support for Windows Console which takes advantage of [accessibility improvements made by Microsoft https://devblogs.microsoft.com/commandline/whats-new-in-windows-console-in-windows-10-fall-creators-update/]. This feature is highly experimental and is still incomplete, so its use is not yet recommended. However, once completed, it is anticipated that this new support will become the default, improving NVDA's performance and stability in Windows command consoles.
1689+
==== Windows Console support ====[AdvancedSettingsConsoleUIA]
1690+
This option selects how NVDA interacts with the Windows Console used by command prompt, PowerShell, and the Windows Subsystem for Linux.
1691+
It does not affect the modern Windows Terminal.
1692+
In Windows 10 version 1709, Microsoft [added support for its UI Automation API to the console https://devblogs.microsoft.com/commandline/whats-new-in-windows-console-in-windows-10-fall-creators-update/], bringing vastly improved performance and stability for screen readers that support it.
1693+
In situations where UI Automation is unavailable or known to result in an inferior user experience, NVDA's legacy console support is available as a fallback.
1694+
The Windows Console support combo box has three options:
1695+
- Automatic: Uses UI Automation in consoles on Windows 10 version 1803 and later. This option is recommended and set by default.
1696+
- Prefer UIA: Uses UI Automation in consoles if available, even on Windows versions with incomplete or buggy implementations. While this limited functionality may be useful (and even sufficient for your usage), use of this option is entirely at your own risk and no support for it will be provided.
1697+
- Legacy: UI Automation in the Windows Console will be completely disabled, so the legacy fallback will always be used.
1698+
-
16911699

1692-
==== Speak passwords in UIA consoles ====[AdvancedSettingsWinConsoleSpeakPasswords]
1693-
This setting controls whether characters are spoken by [speak typed characters #KeyboardSettingsSpeakTypedCharacters] or [speak typed words #KeyboardSettingsSpeakTypedWords] in situations where the screen does not update (such as password entry) in the Windows Console with UI automation support enabled. For security purposes, this setting should be left disabled. However, you may wish to enable it if you experience performance issues or instability with typed character and/or word reporting while using NVDA's new experimental console support.
1700+
==== Speak passwords in Windows Console ====[AdvancedSettingsWinConsoleSpeakPasswords]
1701+
This setting controls whether characters are spoken by [speak typed characters #KeyboardSettingsSpeakTypedCharacters] or [speak typed words #KeyboardSettingsSpeakTypedWords] in situations where the screen does not update (such as password entry) in some terminal programs, such as the Windows Console with UI automation support enabled and Mintty.
1702+
For security purposes, this setting should be left disabled.
1703+
However, you may wish to enable it if you experience performance issues or instability with typed character and/or word reporting in consoles, or work in trusted environments and prefer password announcement.
16941704

16951705
==== Use the new typed character support in legacy Windows consoles when available ====[AdvancedSettingsKeyboardSupportInLegacy]
16961706
This option enables an alternative method for detecting typed characters in legacy Windows consoles.

0 commit comments

Comments
 (0)