From f7e7e68e1253ce1bd500fd008a70668a1f3a7324 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 10 Jul 2019 15:50:24 +0800 Subject: [PATCH 01/23] Factor out the UIA keyboard support into a mixin and add it to PuTTY/MinTTY --- source/NVDAObjects/UIA/winConsoleUIA.py | 78 +------------------------ source/NVDAObjects/behaviors.py | 77 ++++++++++++++++++++++++ source/appModules/putty.py | 6 +- source/keyboardHandler.py | 4 +- 4 files changed, 84 insertions(+), 81 deletions(-) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index 7a293af5ccc..e76f40edb6e 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -4,18 +4,14 @@ # See the file COPYING for more details. # Copyright (C) 2019 Bill Dengler -import config import ctypes import NVDAHelper -import speech -import time import textInfos import UIAHandler -from scriptHandler import script from winVersion import isWin10 from . import UIATextInfo -from ..behaviors import Terminal +from ..behaviors import Terminal, TerminalKeyboardSupport from ..window import Window @@ -223,92 +219,22 @@ def _get_focusRedirect(self): return None -class WinConsoleUIA(Terminal): +class WinConsoleUIA(TerminalKeyboardSupport, Terminal): #: Disable the name as it won't be localized name = "" #: Only process text changes every 30 ms, in case the console is getting #: a lot of text. STABILIZE_DELAY = 0.03 _TextInfo = consoleUIATextInfo - #: A queue of typed characters, to be dispatched on C{textChange}. - #: This queue allows NVDA to suppress typed passwords when needed. - _queuedChars = [] - #: Whether the console got new text lines in its last update. - #: Used to determine if typed character/word buffers should be flushed. - _hasNewLines = False #: the caret in consoles can take a while to move on Windows 10 1903 and later. _caretMovementTimeoutMultiplier = 1.5 - def _reportNewText(self, line): - # Additional typed character filtering beyond that in LiveText - if len(line.strip()) < max(len(speech.curWordChars) + 1, 3): - return - if self._hasNewLines: - # Clear the typed word buffer for new text lines. - # This will need to be changed once #8110 is merged. - speech.curWordChars = [] - self._queuedChars = [] - super(WinConsoleUIA, self)._reportNewText(line) - - def event_typedCharacter(self, ch): - if ch == '\t': - # Clear the typed word buffer for tab completion. - # This will need to be changed once #8110 is merged. - speech.curWordChars = [] - if ( - ( - config.conf['keyboard']['speakTypedCharacters'] - or config.conf['keyboard']['speakTypedWords'] - ) - and not config.conf['UIA']['winConsoleSpeakPasswords'] - ): - self._queuedChars.append(ch) - else: - super(WinConsoleUIA, self).event_typedCharacter(ch) - - def event_textChange(self): - while self._queuedChars: - ch = self._queuedChars.pop(0) - super(WinConsoleUIA, self).event_typedCharacter(ch) - super(WinConsoleUIA, self).event_textChange() - - @script(gestures=[ - "kb:enter", - "kb:numpadEnter", - "kb:tab", - "kb:control+c", - "kb:control+d", - "kb:control+pause" - ]) - def script_flush_queuedChars(self, gesture): - """ - Flushes the typed word buffer and queue of typedCharacter events if present. - Since these gestures clear the current word/line, we should flush the - queue to avoid erroneously reporting these chars. - """ - gesture.send() - self._queuedChars = [] - speech.curWordChars = [] - def _getTextLines(self): # Filter out extraneous empty lines from UIA ptr = self.UIATextPattern.GetVisibleRanges() res = [ptr.GetElement(i).GetText(-1) for i in range(ptr.length)] return res - def _calculateNewText(self, newLines, oldLines): - self._hasNewLines = ( - self._findNonBlankIndices(newLines) - != self._findNonBlankIndices(oldLines) - ) - return super(WinConsoleUIA, self)._calculateNewText(newLines, oldLines) - - def _findNonBlankIndices(self, lines): - """ - Given a list of strings, returns a list of indices where the strings - are not empty. - """ - return [index for index, line in enumerate(lines) if line] def findExtraOverlayClasses(obj, clsList): if obj.UIAElement.cachedAutomationId == "Text Area": diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index ddfc57eab26..d1d239fa3d5 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -24,6 +24,7 @@ import textInfos import editableText from logHandler import log +from scriptHandler import script import api import ui import braille @@ -365,6 +366,82 @@ def event_gainFocus(self): def event_loseFocus(self): self.stopMonitoring() + +class TerminalKeyboardSupport(Terminal): + """Provides typed character support for console applications on Windows 10 1703 and later.""" + #: A queue of typed characters, to be dispatched on C{textChange}. + #: This queue allows NVDA to suppress typed passwords when needed. + _queuedChars = [] + #: Whether the console got new text lines in its last update. + #: Used to determine if typed character/word buffers should be flushed. + _hasNewLines = False + + def _reportNewText(self, line): + # Additional typed character filtering beyond that in LiveText + if len(line.strip()) < max(len(speech.curWordChars) + 1, 3): + return + if self._hasNewLines: + # Clear the typed word buffer for new text lines. + # This will need to be changed once #8110 is merged. + speech.curWordChars = [] + self._queuedChars = [] + super(TerminalKeyboardSupport, self)._reportNewText(line) + + def event_typedCharacter(self, ch): + if ch == '\t': + # Clear the typed word buffer for tab completion. + # This will need to be changed once #8110 is merged. + speech.curWordChars = [] + if ( + ( + config.conf['keyboard']['speakTypedCharacters'] + or config.conf['keyboard']['speakTypedWords'] + ) + and not config.conf['UIA']['winConsoleSpeakPasswords'] + ): + self._queuedChars.append(ch) + else: + super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + + def event_textChange(self): + while self._queuedChars: + ch = self._queuedChars.pop(0) + super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + super(TerminalKeyboardSupport, self).event_textChange() + + @script(gestures=[ + "kb:enter", + "kb:numpadEnter", + "kb:tab", + "kb:control+c", + "kb:control+d", + "kb:control+pause" + ]) + def script_flush_queuedChars(self, gesture): + """ + Flushes the typed word buffer and queue of typedCharacter events if present. + Since these gestures clear the current word/line, we should flush the + queue to avoid erroneously reporting these chars. + """ + gesture.send() + self._queuedChars = [] + speech.curWordChars = [] + + def _calculateNewText(self, newLines, oldLines): + self._hasNewLines = ( + self._findNonBlankIndices(newLines) + != self._findNonBlankIndices(oldLines) + ) + return super(TerminalKeyboardSupport, self)._calculateNewText(newLines, oldLines) + + def _findNonBlankIndices(self, lines): + """ + Given a list of strings, returns a list of indices where the strings + are not empty. + """ + return [index for index, line in enumerate(lines) if line] + + class CandidateItem(NVDAObject): def getFormattedCandidateName(self,number,candidate): diff --git a/source/appModules/putty.py b/source/appModules/putty.py index b84ccee57e5..3b51d6afdbb 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -2,13 +2,13 @@ #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) 2010-2014 NV Access Limited +#Copyright (C) 2010-2019 NV Access Limited, Bill Dengler """App module for PuTTY """ import oleacc -from NVDAObjects.behaviors import Terminal +from NVDAObjects.behaviors import Terminal, TerminalKeyboardSupport from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible @@ -23,4 +23,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (Terminal, DisplayModelLiveText) + clsList[0:0] = (TerminalKeyboardSupport, Terminal, DisplayModelLiveText) diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index d85b359acf1..a6e45598be9 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -197,7 +197,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # #6017: handle typed characters in Win10 RS2 and above where we can't detect typed characters in-process # This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code. focus=api.getFocusObject() - from NVDAObjects.UIA.winConsoleUIA import WinConsoleUIA + from NVDAObjects.behaviors import TerminalKeyboardSupport if ( # This is only possible in Windows 10 RS2 and above winVersion.isWin10(1703) @@ -212,7 +212,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') #Or this is a UIA console window, where WM_CHAR messages are doubled - or isinstance(focus, WinConsoleUIA) + or isinstance(focus, TerminalKeyboardSupport) ) ): keyStates=(ctypes.c_byte*256)() From db72da0a48807d2e1d09fb2c5467e6aa0e734537 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Thu, 11 Jul 2019 15:57:07 +0800 Subject: [PATCH 02/23] Enable TerminalKeyboardSupport for legacy consoles on Windows 10 1703 and later. --- source/NVDAObjects/IAccessible/__init__.py | 7 ++++++- source/NVDAObjects/IAccessible/winConsole.py | 10 +++++++++- source/NVDAObjects/behaviors.py | 13 ++++++++++--- source/NVDAObjects/window/winConsole.py | 10 ++++++++-- source/winConsoleHandler.py | 3 ++- 5 files changed, 35 insertions(+), 8 deletions(-) diff --git a/source/NVDAObjects/IAccessible/__init__.py b/source/NVDAObjects/IAccessible/__init__.py index 6da88c0cdb8..a0f43f7ef63 100644 --- a/source/NVDAObjects/IAccessible/__init__.py +++ b/source/NVDAObjects/IAccessible/__init__.py @@ -531,6 +531,12 @@ def findOverlayClasses(self,clsList): elif windowClassName.startswith("Chrome_"): from . import chromium chromium.findExtraOverlayClasses(self, clsList) + if ( + windowClassName == "ConsoleWindowClass" + and role == oleacc.ROLE_SYSTEM_CLIENT + ): + from . import winConsole + winConsole.findExtraOverlayClasses(self,clsList) #Support for Windowless richEdit @@ -2012,5 +2018,4 @@ def event_alert(self): ("NUIDialog",oleacc.ROLE_SYSTEM_CLIENT):"NUIDialogClient", ("_WwB",oleacc.ROLE_SYSTEM_CLIENT):"winword.ProtectedDocumentPane", ("MsoCommandBar",oleacc.ROLE_SYSTEM_LISTITEM):"msOffice.CommandBarListItem", - ("ConsoleWindowClass",oleacc.ROLE_SYSTEM_CLIENT):"winConsole.WinConsole", } diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 59b33186edd..72c5f3e12cd 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -4,9 +4,17 @@ #See the file COPYING for more details. #Copyright (C) 2007-2019 NV Access Limited, Bill Dengler +from winVersion import isWin10 + from . import IAccessible from ..window.winConsole import WinConsole class WinConsole(WinConsole, IAccessible): "The legacy console implementation for situations where UIA isn't supported." - pass \ No newline at end of file + pass + +def findExtraOverlayClasses(obj, clsList): + if isWin10(1703): + from NVDAObjects.behaviors import TerminalKeyboardSupport + clsList.append(TerminalKeyboardSupport) + clsList.append(WinConsole) \ No newline at end of file diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index d1d239fa3d5..555aa7762ad 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -369,6 +369,8 @@ def event_loseFocus(self): class TerminalKeyboardSupport(Terminal): """Provides typed character support for console applications on Windows 10 1703 and later.""" + #: Whether this object reliably sends textChange events. + _supportsTextChange = True #: A queue of typed characters, to be dispatched on C{textChange}. #: This queue allows NVDA to suppress typed passwords when needed. _queuedChars = [] @@ -398,15 +400,14 @@ def event_typedCharacter(self, ch): or config.conf['keyboard']['speakTypedWords'] ) and not config.conf['UIA']['winConsoleSpeakPasswords'] + and self._supportsTextChange ): self._queuedChars.append(ch) else: super(TerminalKeyboardSupport, self).event_typedCharacter(ch) def event_textChange(self): - while self._queuedChars: - ch = self._queuedChars.pop(0) - super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + self._dispatchQueue() super(TerminalKeyboardSupport, self).event_textChange() @script(gestures=[ @@ -434,6 +435,12 @@ def _calculateNewText(self, newLines, oldLines): ) return super(TerminalKeyboardSupport, self)._calculateNewText(newLines, oldLines) + def _dispatchQueue(self): + """Sends queued typedCharacter events through to NVDA.""" + while self._queuedChars: + ch = self._queuedChars.pop(0) + super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + def _findNonBlankIndices(self, lines): """ Given a list of strings, returns a list of indices where the strings diff --git a/source/NVDAObjects/window/winConsole.py b/source/NVDAObjects/window/winConsole.py index ec3923e5fcf..30d4066b6b1 100644 --- a/source/NVDAObjects/window/winConsole.py +++ b/source/NVDAObjects/window/winConsole.py @@ -2,11 +2,11 @@ #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) 2007-2012 NV Access Limited +#Copyright (C) 2007-2019 NV Access Limited, Bill Dengler import winConsoleHandler from . import Window -from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection +from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalKeyboardSupport import api import core @@ -18,6 +18,12 @@ class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window): """ STABILIZE_DELAY = 0.03 + def initOverlayClass(self): + # Legacy consoles take quite a while to send textChange events. + # This significantly impacts typing performance, so don't queue chars. + if isinstance(self, TerminalKeyboardSupport): + self._supportsTextChange = False + def _get_TextInfo(self): consoleObject=winConsoleHandler.consoleObject if consoleObject and self.windowHandle == consoleObject.windowHandle: diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index 2a583b64199..3dd38cd03dd 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -133,6 +133,7 @@ def getConsoleVisibleLines(): @winUser.WINEVENTPROC def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp): + from NVDAObjects.behaviors import TerminalKeyboardSupport #We don't want to do anything with the event if the event is not for the window this console is in if window!=consoleObject.windowHandle: return @@ -145,7 +146,7 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam x=winUser.GET_X_LPARAM(objectID) y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) - if x Date: Thu, 11 Jul 2019 16:30:15 +0800 Subject: [PATCH 03/23] Make TerminalKeyboardSupport configurable for legacy consoles. --- source/NVDAObjects/IAccessible/winConsole.py | 4 +++- source/config/configSpec.py | 3 +++ source/gui/settingsDialogs.py | 18 ++++++++++++++++++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 72c5f3e12cd..83b06e188d5 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -4,6 +4,8 @@ #See the file COPYING for more details. #Copyright (C) 2007-2019 NV Access Limited, Bill Dengler +import config + from winVersion import isWin10 from . import IAccessible @@ -14,7 +16,7 @@ class WinConsole(WinConsole, IAccessible): pass def findExtraOverlayClasses(obj, clsList): - if isWin10(1703): + if isWin10(1703) and config.conf['terminals']['keyboardSupportInLegacy']: from NVDAObjects.behaviors import TerminalKeyboardSupport clsList.append(TerminalKeyboardSupport) clsList.append(WinConsole) \ No newline at end of file diff --git a/source/config/configSpec.py b/source/config/configSpec.py index fdc4e0114b5..4622178ddef 100644 --- a/source/config/configSpec.py +++ b/source/config/configSpec.py @@ -190,6 +190,9 @@ winConsoleImplementation= option("auto", "legacy", "UIA", default="auto") winConsoleSpeakPasswords = boolean(default=false) +[terminals] + keyboardSupportInLegacy = boolean(default=True) + [update] autoCheck = boolean(default=true) startupNotification = boolean(default=true) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 0b44104f1c7..f02282e781d 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -2066,6 +2066,21 @@ def __init__(self, parent): self.winConsoleSpeakPasswordsCheckBox.SetValue(config.conf["UIA"]["winConsoleSpeakPasswords"]) self.winConsoleSpeakPasswordsCheckBox.defaultValue = self._getDefaultValue(["UIA", "winConsoleSpeakPasswords"]) + # Translators: This is the label for a group of advanced options in the + # Advanced settings panel + label = _("Terminal programs") + terminalsGroup = guiHelper.BoxSizerHelper( + parent=self, + sizer=wx.StaticBoxSizer(parent=self, label=label, orient=wx.VERTICAL) + ) + sHelper.addItem(terminalsGroup) + # Translators: This is the label for a checkbox in the + # Advanced settings panel. + label = _("Use the new &keyboard support in legacy Windows consoles") + self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label)) + self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"]) + self.keyboardSupportInLegacyCheckBox.defaultValue = self._getDefaultValue(["terminals", "keyboardSupportInLegacy"]) + # Translators: This is the label for a group of advanced options in the # Advanced settings panel label = _("Browse mode") @@ -2152,6 +2167,7 @@ def haveConfigDefaultsBeenRestored(self): self.UIAInMSWordCheckBox.IsChecked() == self.UIAInMSWordCheckBox.defaultValue and self.ConsoleUIACheckBox.IsChecked() == (self.ConsoleUIACheckBox.defaultValue=='UIA') and self.winConsoleSpeakPasswordsCheckBox.IsChecked() == self.winConsoleSpeakPasswordsCheckBox.defaultValue and + self.keyboardSupportInLegacyCheckBox.IsChecked() == self.keyboardSupportInLegacyCheckBox.defaultValue and self.autoFocusFocusableElementsCheckBox.IsChecked() == self.autoFocusFocusableElementsCheckBox.defaultValue and self.caretMoveTimeoutSpinControl.GetValue() == self.caretMoveTimeoutSpinControl.defaultValue and set(self.logCategoriesList.CheckedItems) == set(self.logCategoriesList.defaultCheckedItems) and @@ -2163,6 +2179,7 @@ def restoreToDefaults(self): self.UIAInMSWordCheckBox.SetValue(self.UIAInMSWordCheckBox.defaultValue) self.ConsoleUIACheckBox.SetValue(self.ConsoleUIACheckBox.defaultValue=='UIA') self.winConsoleSpeakPasswordsCheckBox.SetValue(self.winConsoleSpeakPasswordsCheckBox.defaultValue) + self.keyboardSupportInLegacyCheckBox.SetValue(self.keyboardSupportInLegacyCheckBox.defaultValue) self.autoFocusFocusableElementsCheckBox.SetValue(self.autoFocusFocusableElementsCheckBox.defaultValue) self.caretMoveTimeoutSpinControl.SetValue(self.caretMoveTimeoutSpinControl.defaultValue) self.logCategoriesList.CheckedItems = self.logCategoriesList.defaultCheckedItems @@ -2177,6 +2194,7 @@ def onSave(self): else: config.conf['UIA']['winConsoleImplementation'] = "auto" config.conf["UIA"]["winConsoleSpeakPasswords"]=self.winConsoleSpeakPasswordsCheckBox.IsChecked() + config.conf["terminals"]["keyboardSupportInLegacy"]=self.keyboardSupportInLegacyCheckBox.IsChecked() config.conf["virtualBuffers"]["autoFocusFocusableElements"] = self.autoFocusFocusableElementsCheckBox.IsChecked() config.conf["editableText"]["caretMoveTimeoutMs"]=self.caretMoveTimeoutSpinControl.GetValue() for index,key in enumerate(self.logCategories): From 187c4525f4a779850a9c8741b201b7219507433c Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 13 Jul 2019 13:16:01 +0800 Subject: [PATCH 04/23] Make the TerminalKeyboardSupport mixin inherit from object, so that Terminal doesn't appear in the MRO twice. --- source/NVDAObjects/behaviors.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 555aa7762ad..bb0b02f959f 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -367,8 +367,9 @@ def event_loseFocus(self): self.stopMonitoring() -class TerminalKeyboardSupport(Terminal): - """Provides typed character support for console applications on Windows 10 1703 and later.""" +class TerminalKeyboardSupport(object): + """Provides typed character support for console applications on Windows 10 1703 and later. + @note: objects must also inherit from the Terminal class.""" #: Whether this object reliably sends textChange events. _supportsTextChange = True #: A queue of typed characters, to be dispatched on C{textChange}. From 4698f941271b2d787852c445d9cf07f13ebdd298 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sun, 14 Jul 2019 14:01:06 +0800 Subject: [PATCH 05/23] TerminalKeyboardSupport -> TerminalWithKeyboardSupport. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- source/NVDAObjects/UIA/winConsoleUIA.py | 4 ++-- source/NVDAObjects/behaviors.py | 16 ++++++++-------- source/NVDAObjects/window/winConsole.py | 4 ++-- source/appModules/putty.py | 4 ++-- source/keyboardHandler.py | 4 ++-- source/winConsoleHandler.py | 4 ++-- 7 files changed, 20 insertions(+), 20 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 83b06e188d5..b9b0a6fbb6e 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -17,6 +17,6 @@ class WinConsole(WinConsole, IAccessible): def findExtraOverlayClasses(obj, clsList): if isWin10(1703) and config.conf['terminals']['keyboardSupportInLegacy']: - from NVDAObjects.behaviors import TerminalKeyboardSupport - clsList.append(TerminalKeyboardSupport) + from NVDAObjects.behaviors import TerminalWithKeyboardSupport + clsList.append(TerminalWithKeyboardSupport) clsList.append(WinConsole) \ No newline at end of file diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index e76f40edb6e..d93230212c1 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -11,7 +11,7 @@ from winVersion import isWin10 from . import UIATextInfo -from ..behaviors import Terminal, TerminalKeyboardSupport +from ..behaviors import TerminalWithKeyboardSupport from ..window import Window @@ -219,7 +219,7 @@ def _get_focusRedirect(self): return None -class WinConsoleUIA(TerminalKeyboardSupport, Terminal): +class WinConsoleUIA(TerminalWithKeyboardSupport): #: Disable the name as it won't be localized name = "" #: Only process text changes every 30 ms, in case the console is getting diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index bb0b02f959f..e87fafd7e0b 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -367,9 +367,9 @@ def event_loseFocus(self): self.stopMonitoring() -class TerminalKeyboardSupport(object): - """Provides typed character support for console applications on Windows 10 1703 and later. - @note: objects must also inherit from the Terminal class.""" +class TerminalWithKeyboardSupport(Terminal): + """A Terminal object that also provides typed character support for + console applications on Windows 10 1703 and later.""" #: Whether this object reliably sends textChange events. _supportsTextChange = True #: A queue of typed characters, to be dispatched on C{textChange}. @@ -388,7 +388,7 @@ def _reportNewText(self, line): # This will need to be changed once #8110 is merged. speech.curWordChars = [] self._queuedChars = [] - super(TerminalKeyboardSupport, self)._reportNewText(line) + super(TerminalWithKeyboardSupport, self)._reportNewText(line) def event_typedCharacter(self, ch): if ch == '\t': @@ -405,11 +405,11 @@ def event_typedCharacter(self, ch): ): self._queuedChars.append(ch) else: - super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) def event_textChange(self): self._dispatchQueue() - super(TerminalKeyboardSupport, self).event_textChange() + super(TerminalWithKeyboardSupport, self).event_textChange() @script(gestures=[ "kb:enter", @@ -434,13 +434,13 @@ def _calculateNewText(self, newLines, oldLines): self._findNonBlankIndices(newLines) != self._findNonBlankIndices(oldLines) ) - return super(TerminalKeyboardSupport, self)._calculateNewText(newLines, oldLines) + return super(TerminalWithKeyboardSupport, self)._calculateNewText(newLines, oldLines) def _dispatchQueue(self): """Sends queued typedCharacter events through to NVDA.""" while self._queuedChars: ch = self._queuedChars.pop(0) - super(TerminalKeyboardSupport, self).event_typedCharacter(ch) + super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) def _findNonBlankIndices(self, lines): """ diff --git a/source/NVDAObjects/window/winConsole.py b/source/NVDAObjects/window/winConsole.py index 30d4066b6b1..04fd0d546b3 100644 --- a/source/NVDAObjects/window/winConsole.py +++ b/source/NVDAObjects/window/winConsole.py @@ -6,7 +6,7 @@ import winConsoleHandler from . import Window -from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalKeyboardSupport +from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithKeyboardSupport import api import core @@ -21,7 +21,7 @@ class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window): def initOverlayClass(self): # Legacy consoles take quite a while to send textChange events. # This significantly impacts typing performance, so don't queue chars. - if isinstance(self, TerminalKeyboardSupport): + if isinstance(self, TerminalWithKeyboardSupport): self._supportsTextChange = False def _get_TextInfo(self): diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 3b51d6afdbb..047e8a1f1b4 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -8,7 +8,7 @@ """ import oleacc -from NVDAObjects.behaviors import Terminal, TerminalKeyboardSupport +from NVDAObjects.behaviors import TerminalWithKeyboardSupport from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible @@ -23,4 +23,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (TerminalKeyboardSupport, Terminal, DisplayModelLiveText) + clsList[0:0] = (TerminalWithKeyboardSupport, DisplayModelLiveText) diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index a6e45598be9..8cb7b3adda1 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -197,7 +197,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # #6017: handle typed characters in Win10 RS2 and above where we can't detect typed characters in-process # This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code. focus=api.getFocusObject() - from NVDAObjects.behaviors import TerminalKeyboardSupport + from NVDAObjects.behaviors import TerminalWithKeyboardSupport if ( # This is only possible in Windows 10 RS2 and above winVersion.isWin10(1703) @@ -212,7 +212,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') #Or this is a UIA console window, where WM_CHAR messages are doubled - or isinstance(focus, TerminalKeyboardSupport) + or isinstance(focus, TerminalWithKeyboardSupport) ) ): keyStates=(ctypes.c_byte*256)() diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index 3dd38cd03dd..fe8d57cb150 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -133,7 +133,7 @@ def getConsoleVisibleLines(): @winUser.WINEVENTPROC def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp): - from NVDAObjects.behaviors import TerminalKeyboardSupport + from NVDAObjects.behaviors import TerminalWithKeyboardSupport #We don't want to do anything with the event if the event is not for the window this console is in if window!=consoleObject.windowHandle: return @@ -146,7 +146,7 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam x=winUser.GET_X_LPARAM(objectID) y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) - if not isinstance(consoleObject, TerminalKeyboardSupport) and x Date: Sun, 14 Jul 2019 14:19:00 +0800 Subject: [PATCH 06/23] Fix ambiguous imports. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index b9b0a6fbb6e..48639ed5a8b 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -9,9 +9,9 @@ from winVersion import isWin10 from . import IAccessible -from ..window.winConsole import WinConsole +from ..window.winConsole import WinConsole as LegacyWinConsole -class WinConsole(WinConsole, IAccessible): +class WinConsole(LegacyWinConsole, IAccessible): "The legacy console implementation for situations where UIA isn't supported." pass From 530460ff6bd6d22c0948aee865c984f35cec7e96 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sun, 14 Jul 2019 18:28:07 +0800 Subject: [PATCH 07/23] Enable new keyboard handling and TerminalWithKeyboardSupport on Windows 10 1607. --- source/NVDAObjects/IAccessible/winConsole.py | 2 +- source/NVDAObjects/behaviors.py | 2 +- source/keyboardHandler.py | 8 ++++---- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 48639ed5a8b..7f551dee64f 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -16,7 +16,7 @@ class WinConsole(LegacyWinConsole, IAccessible): pass def findExtraOverlayClasses(obj, clsList): - if isWin10(1703) and config.conf['terminals']['keyboardSupportInLegacy']: + if isWin10(1607) and config.conf['terminals']['keyboardSupportInLegacy']: from NVDAObjects.behaviors import TerminalWithKeyboardSupport clsList.append(TerminalWithKeyboardSupport) clsList.append(WinConsole) \ No newline at end of file diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index e87fafd7e0b..9354994aa32 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -369,7 +369,7 @@ def event_loseFocus(self): class TerminalWithKeyboardSupport(Terminal): """A Terminal object that also provides typed character support for - console applications on Windows 10 1703 and later.""" + console applications on Windows 10 1607 and later.""" #: Whether this object reliably sends textChange events. _supportsTextChange = True #: A queue of typed characters, to be dispatched on C{textChange}. diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index 8cb7b3adda1..c4cf8ed024e 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -199,19 +199,19 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): focus=api.getFocusObject() from NVDAObjects.behaviors import TerminalWithKeyboardSupport if ( - # This is only possible in Windows 10 RS2 and above - winVersion.isWin10(1703) + # This is only possible in Windows 10 1607 and above + winVersion.isWin10(1607) # And we only want to do this if the gesture did not result in an executed action and not gestureExecuted # and not if this gesture is a modifier key and not isNVDAModifierKey(vkCode,extended) and not vkCode in KeyboardInputGesture.NORMAL_MODIFIER_KEYS and ( # Either of - # We couldn't inject in-process, and its not a legacy console window. + # We couldn't inject in-process, and its not a legacy console window without keyboard support. # console windows have their own specific typed character support. (not focus.appModule.helperLocalBindingHandle and focus.windowClassName!='ConsoleWindowClass') # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') - #Or this is a UIA console window, where WM_CHAR messages are doubled + #Or this is a console with keyboard support, where WM_CHAR messages are doubled or isinstance(focus, TerminalWithKeyboardSupport) ) ): From a94d38467dfaeedf51249aea059cd8e2ffc0290f Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sun, 14 Jul 2019 18:30:23 +0800 Subject: [PATCH 08/23] Clarify GUI option text. --- source/gui/settingsDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index f02282e781d..e821da52004 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -2076,7 +2076,7 @@ def __init__(self, parent): sHelper.addItem(terminalsGroup) # Translators: This is the label for a checkbox in the # Advanced settings panel. - label = _("Use the new &keyboard support in legacy Windows consoles") + label = _("Use the new &keyboard support in legacy Windows consoles when available") self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label)) self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"]) self.keyboardSupportInLegacyCheckBox.defaultValue = self._getDefaultValue(["terminals", "keyboardSupportInLegacy"]) From 98b6931fbd32f0df38998f8a049b5321f7f5b92a Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 17 Jul 2019 01:09:04 +0800 Subject: [PATCH 09/23] Introduce changes from #9936. --- source/NVDAObjects/behaviors.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 9354994aa32..23a2cf1f411 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -378,10 +378,17 @@ class TerminalWithKeyboardSupport(Terminal): #: Whether the console got new text lines in its last update. #: Used to determine if typed character/word buffers should be flushed. _hasNewLines = False + #: Whether the last typed character is a tab. + #: If so, we should temporarily disable filtering as completions may + #: be short. + _hasTab = False def _reportNewText(self, line): # Additional typed character filtering beyond that in LiveText - if len(line.strip()) < max(len(speech.curWordChars) + 1, 3): + if ( + not self._hasTab + and len(line.strip()) < max(len(speech.curWordChars) + 1, 3) + ): return if self._hasNewLines: # Clear the typed word buffer for new text lines. @@ -392,9 +399,12 @@ def _reportNewText(self, line): def event_typedCharacter(self, ch): if ch == '\t': + self._hasTab = True # Clear the typed word buffer for tab completion. # This will need to be changed once #8110 is merged. speech.curWordChars = [] + else: + self._hasTab = False if ( ( config.conf['keyboard']['speakTypedCharacters'] From f59dd6ae41feb1ef833bab07e55cda6d4dbedc3b Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 17 Jul 2019 01:19:55 +0800 Subject: [PATCH 10/23] TerminalWithKeyboardSupport -> TerminalWithoutTypedCharDetection. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- source/NVDAObjects/UIA/winConsoleUIA.py | 4 ++-- source/NVDAObjects/behaviors.py | 12 ++++++------ source/NVDAObjects/window/winConsole.py | 4 ++-- source/appModules/putty.py | 4 ++-- source/keyboardHandler.py | 4 ++-- source/winConsoleHandler.py | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 7f551dee64f..4b81e5b18d1 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -17,6 +17,6 @@ class WinConsole(LegacyWinConsole, IAccessible): def findExtraOverlayClasses(obj, clsList): if isWin10(1607) and config.conf['terminals']['keyboardSupportInLegacy']: - from NVDAObjects.behaviors import TerminalWithKeyboardSupport - clsList.append(TerminalWithKeyboardSupport) + from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection + clsList.append(TerminalWithoutTypedCharDetection) clsList.append(WinConsole) \ No newline at end of file diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index c9b1ee0219b..e66139692ea 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -11,7 +11,7 @@ from winVersion import isWin10 from . import UIATextInfo -from ..behaviors import TerminalWithKeyboardSupport +from ..behaviors import TerminalWithoutTypedCharDetection from ..window import Window @@ -219,7 +219,7 @@ def _get_focusRedirect(self): return None -class WinConsoleUIA(TerminalWithKeyboardSupport): +class WinConsoleUIA(TerminalWithoutTypedCharDetection): #: Disable the name as it won't be localized name = "" #: Only process text changes every 30 ms, in case the console is getting diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 23a2cf1f411..e825ac20be3 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -367,7 +367,7 @@ def event_loseFocus(self): self.stopMonitoring() -class TerminalWithKeyboardSupport(Terminal): +class TerminalWithoutTypedCharDetection(Terminal): """A Terminal object that also provides typed character support for console applications on Windows 10 1607 and later.""" #: Whether this object reliably sends textChange events. @@ -395,7 +395,7 @@ def _reportNewText(self, line): # This will need to be changed once #8110 is merged. speech.curWordChars = [] self._queuedChars = [] - super(TerminalWithKeyboardSupport, self)._reportNewText(line) + super(TerminalWithoutTypedCharDetection, self)._reportNewText(line) def event_typedCharacter(self, ch): if ch == '\t': @@ -415,11 +415,11 @@ def event_typedCharacter(self, ch): ): self._queuedChars.append(ch) else: - super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) + super(TerminalWithoutTypedCharDetection, self).event_typedCharacter(ch) def event_textChange(self): self._dispatchQueue() - super(TerminalWithKeyboardSupport, self).event_textChange() + super(TerminalWithoutTypedCharDetection, self).event_textChange() @script(gestures=[ "kb:enter", @@ -444,13 +444,13 @@ def _calculateNewText(self, newLines, oldLines): self._findNonBlankIndices(newLines) != self._findNonBlankIndices(oldLines) ) - return super(TerminalWithKeyboardSupport, self)._calculateNewText(newLines, oldLines) + return super(TerminalWithoutTypedCharDetection, self)._calculateNewText(newLines, oldLines) def _dispatchQueue(self): """Sends queued typedCharacter events through to NVDA.""" while self._queuedChars: ch = self._queuedChars.pop(0) - super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) + super(TerminalWithoutTypedCharDetection, self).event_typedCharacter(ch) def _findNonBlankIndices(self, lines): """ diff --git a/source/NVDAObjects/window/winConsole.py b/source/NVDAObjects/window/winConsole.py index 04fd0d546b3..3e81f4cdd58 100644 --- a/source/NVDAObjects/window/winConsole.py +++ b/source/NVDAObjects/window/winConsole.py @@ -6,7 +6,7 @@ import winConsoleHandler from . import Window -from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithKeyboardSupport +from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithoutTypedCharDetection import api import core @@ -21,7 +21,7 @@ class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window): def initOverlayClass(self): # Legacy consoles take quite a while to send textChange events. # This significantly impacts typing performance, so don't queue chars. - if isinstance(self, TerminalWithKeyboardSupport): + if isinstance(self, TerminalWithoutTypedCharDetection): self._supportsTextChange = False def _get_TextInfo(self): diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 047e8a1f1b4..86edbe3aa33 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -8,7 +8,7 @@ """ import oleacc -from NVDAObjects.behaviors import TerminalWithKeyboardSupport +from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible @@ -23,4 +23,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (TerminalWithKeyboardSupport, DisplayModelLiveText) + clsList[0:0] = (TerminalWithoutTypedCharDetection, DisplayModelLiveText) diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index c4cf8ed024e..909f7bb7fc9 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -197,7 +197,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # #6017: handle typed characters in Win10 RS2 and above where we can't detect typed characters in-process # This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code. focus=api.getFocusObject() - from NVDAObjects.behaviors import TerminalWithKeyboardSupport + from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection if ( # This is only possible in Windows 10 1607 and above winVersion.isWin10(1607) @@ -212,7 +212,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') #Or this is a console with keyboard support, where WM_CHAR messages are doubled - or isinstance(focus, TerminalWithKeyboardSupport) + or isinstance(focus, TerminalWithoutTypedCharDetection) ) ): keyStates=(ctypes.c_byte*256)() diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index fe8d57cb150..f4b0a81c6f6 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -133,7 +133,7 @@ def getConsoleVisibleLines(): @winUser.WINEVENTPROC def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp): - from NVDAObjects.behaviors import TerminalWithKeyboardSupport + from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection #We don't want to do anything with the event if the event is not for the window this console is in if window!=consoleObject.windowHandle: return @@ -146,7 +146,7 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam x=winUser.GET_X_LPARAM(objectID) y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) - if not isinstance(consoleObject, TerminalWithKeyboardSupport) and x Date: Wed, 17 Jul 2019 01:48:15 +0800 Subject: [PATCH 11/23] Update user guide. --- user_docs/en/userGuide.t2t | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t index 42c0e6e94c5..8b790b16205 100644 --- a/user_docs/en/userGuide.t2t +++ b/user_docs/en/userGuide.t2t @@ -1677,6 +1677,12 @@ When this option is enabled, NVDA will use a new, work in progress version of it ==== Speak passwords in UIA consoles ====[AdvancedSettingsWinConsoleSpeakPasswords] 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. +==== Use the new keyboard support in legacy Windows consoles when available ====[AdvancedSettingsKeyboardSupportInLegacy] +This option causes NVDA to use a new method for detecting typed characters in legacy Windows consoles. +This new support is in use by default from Windows 10 version 1607 until 1803, or on newer Windows 10 releases when UI Automation is unavailable or disabled. +While this new support improves performance and prevents some console output from being spelled out, it does not suppress typed passwords and may be incompatible with some terminal programs. +In untrusted environments, it is good practice to temporarily disable "speak typed characters" and "speak typed words" when entering passwords with this new functionality in use. + ==== Automatically set system focus to focusable elements in Browse Mode ====[BrowseModeSettingsAutoFocusFocusableElements] Key: NVDA+8 From ef9d4cc9efb0caba21279dca8725450a2b3ba977 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 27 Jul 2019 08:01:14 -0400 Subject: [PATCH 12/23] Revert "TerminalWithKeyboardSupport -> TerminalWithoutTypedCharDetection." This reverts commit f59dd6ae41feb1ef833bab07e55cda6d4dbedc3b. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- source/NVDAObjects/UIA/winConsoleUIA.py | 4 ++-- source/NVDAObjects/behaviors.py | 12 ++++++------ source/NVDAObjects/window/winConsole.py | 4 ++-- source/appModules/putty.py | 4 ++-- source/keyboardHandler.py | 4 ++-- source/winConsoleHandler.py | 4 ++-- 7 files changed, 18 insertions(+), 18 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 4b81e5b18d1..7f551dee64f 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -17,6 +17,6 @@ class WinConsole(LegacyWinConsole, IAccessible): def findExtraOverlayClasses(obj, clsList): if isWin10(1607) and config.conf['terminals']['keyboardSupportInLegacy']: - from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection - clsList.append(TerminalWithoutTypedCharDetection) + from NVDAObjects.behaviors import TerminalWithKeyboardSupport + clsList.append(TerminalWithKeyboardSupport) clsList.append(WinConsole) \ No newline at end of file diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index e66139692ea..c9b1ee0219b 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -11,7 +11,7 @@ from winVersion import isWin10 from . import UIATextInfo -from ..behaviors import TerminalWithoutTypedCharDetection +from ..behaviors import TerminalWithKeyboardSupport from ..window import Window @@ -219,7 +219,7 @@ def _get_focusRedirect(self): return None -class WinConsoleUIA(TerminalWithoutTypedCharDetection): +class WinConsoleUIA(TerminalWithKeyboardSupport): #: Disable the name as it won't be localized name = "" #: Only process text changes every 30 ms, in case the console is getting diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index e825ac20be3..23a2cf1f411 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -367,7 +367,7 @@ def event_loseFocus(self): self.stopMonitoring() -class TerminalWithoutTypedCharDetection(Terminal): +class TerminalWithKeyboardSupport(Terminal): """A Terminal object that also provides typed character support for console applications on Windows 10 1607 and later.""" #: Whether this object reliably sends textChange events. @@ -395,7 +395,7 @@ def _reportNewText(self, line): # This will need to be changed once #8110 is merged. speech.curWordChars = [] self._queuedChars = [] - super(TerminalWithoutTypedCharDetection, self)._reportNewText(line) + super(TerminalWithKeyboardSupport, self)._reportNewText(line) def event_typedCharacter(self, ch): if ch == '\t': @@ -415,11 +415,11 @@ def event_typedCharacter(self, ch): ): self._queuedChars.append(ch) else: - super(TerminalWithoutTypedCharDetection, self).event_typedCharacter(ch) + super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) def event_textChange(self): self._dispatchQueue() - super(TerminalWithoutTypedCharDetection, self).event_textChange() + super(TerminalWithKeyboardSupport, self).event_textChange() @script(gestures=[ "kb:enter", @@ -444,13 +444,13 @@ def _calculateNewText(self, newLines, oldLines): self._findNonBlankIndices(newLines) != self._findNonBlankIndices(oldLines) ) - return super(TerminalWithoutTypedCharDetection, self)._calculateNewText(newLines, oldLines) + return super(TerminalWithKeyboardSupport, self)._calculateNewText(newLines, oldLines) def _dispatchQueue(self): """Sends queued typedCharacter events through to NVDA.""" while self._queuedChars: ch = self._queuedChars.pop(0) - super(TerminalWithoutTypedCharDetection, self).event_typedCharacter(ch) + super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) def _findNonBlankIndices(self, lines): """ diff --git a/source/NVDAObjects/window/winConsole.py b/source/NVDAObjects/window/winConsole.py index 3e81f4cdd58..04fd0d546b3 100644 --- a/source/NVDAObjects/window/winConsole.py +++ b/source/NVDAObjects/window/winConsole.py @@ -6,7 +6,7 @@ import winConsoleHandler from . import Window -from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithoutTypedCharDetection +from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithKeyboardSupport import api import core @@ -21,7 +21,7 @@ class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window): def initOverlayClass(self): # Legacy consoles take quite a while to send textChange events. # This significantly impacts typing performance, so don't queue chars. - if isinstance(self, TerminalWithoutTypedCharDetection): + if isinstance(self, TerminalWithKeyboardSupport): self._supportsTextChange = False def _get_TextInfo(self): diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 86edbe3aa33..047e8a1f1b4 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -8,7 +8,7 @@ """ import oleacc -from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection +from NVDAObjects.behaviors import TerminalWithKeyboardSupport from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible @@ -23,4 +23,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (TerminalWithoutTypedCharDetection, DisplayModelLiveText) + clsList[0:0] = (TerminalWithKeyboardSupport, DisplayModelLiveText) diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index 909f7bb7fc9..c4cf8ed024e 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -197,7 +197,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # #6017: handle typed characters in Win10 RS2 and above where we can't detect typed characters in-process # This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code. focus=api.getFocusObject() - from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection + from NVDAObjects.behaviors import TerminalWithKeyboardSupport if ( # This is only possible in Windows 10 1607 and above winVersion.isWin10(1607) @@ -212,7 +212,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') #Or this is a console with keyboard support, where WM_CHAR messages are doubled - or isinstance(focus, TerminalWithoutTypedCharDetection) + or isinstance(focus, TerminalWithKeyboardSupport) ) ): keyStates=(ctypes.c_byte*256)() diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index f4b0a81c6f6..fe8d57cb150 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -133,7 +133,7 @@ def getConsoleVisibleLines(): @winUser.WINEVENTPROC def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp): - from NVDAObjects.behaviors import TerminalWithoutTypedCharDetection + from NVDAObjects.behaviors import TerminalWithKeyboardSupport #We don't want to do anything with the event if the event is not for the window this console is in if window!=consoleObject.windowHandle: return @@ -146,7 +146,7 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam x=winUser.GET_X_LPARAM(objectID) y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) - if not isinstance(consoleObject, TerminalWithoutTypedCharDetection) and x Date: Sat, 27 Jul 2019 08:31:55 -0400 Subject: [PATCH 13/23] Review actions. --- source/NVDAObjects/IAccessible/winConsole.py | 6 +++--- source/winConsoleHandler.py | 9 ++++++++- user_docs/en/userGuide.t2t | 9 +++++---- 3 files changed, 16 insertions(+), 8 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index 7f551dee64f..a28d98bae83 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -9,9 +9,9 @@ from winVersion import isWin10 from . import IAccessible -from ..window.winConsole import WinConsole as LegacyWinConsole +from ..window.winConsole import WinConsole as WinConsoleWindow -class WinConsole(LegacyWinConsole, IAccessible): +class WinConsole(WinConsoleWindow, IAccessible): "The legacy console implementation for situations where UIA isn't supported." pass @@ -19,4 +19,4 @@ def findExtraOverlayClasses(obj, clsList): if isWin10(1607) and config.conf['terminals']['keyboardSupportInLegacy']: from NVDAObjects.behaviors import TerminalWithKeyboardSupport clsList.append(TerminalWithKeyboardSupport) - clsList.append(WinConsole) \ No newline at end of file + clsList.append(WinConsole) diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index fe8d57cb150..d2d0e195a86 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -146,7 +146,14 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam x=winUser.GET_X_LPARAM(objectID) y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) - if not isinstance(consoleObject, TerminalWithKeyboardSupport) and x Date: Sat, 27 Jul 2019 08:37:07 -0400 Subject: [PATCH 14/23] Only enable the KeyboardSupportInLegacyCheckBox on Windows 10 1607 and later. --- source/gui/settingsDialogs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index e821da52004..f86d889da00 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -2080,6 +2080,7 @@ def __init__(self, parent): self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label)) self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"]) self.keyboardSupportInLegacyCheckBox.defaultValue = self._getDefaultValue(["terminals", "keyboardSupportInLegacy"]) + self.keyboardSupportInLegacyCheckBox.Enable(winVersion.isWin10(1607)) # Translators: This is the label for a group of advanced options in the # Advanced settings panel From 89e853d62c8f02aa091b87fd21ad8b93f1fc605e Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Sat, 27 Jul 2019 11:30:47 -0400 Subject: [PATCH 15/23] Fix submodules. --- include/comtypes | 2 +- include/pyserial | 2 +- include/wxPython | 2 +- miscDeps | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/include/comtypes b/include/comtypes index 1d3d38b2a61..8c45582fe49 160000 --- a/include/comtypes +++ b/include/comtypes @@ -1 +1 @@ -Subproject commit 1d3d38b2a616674309e7eabe6b5c581042caf32a +Subproject commit 8c45582fe497269594f127f93d1a786d6bc868fb diff --git a/include/pyserial b/include/pyserial index 8bec5552882..c54c81d933b 160000 --- a/include/pyserial +++ b/include/pyserial @@ -1 +1 @@ -Subproject commit 8bec55528827d09937f411e27195ec396993d75c +Subproject commit c54c81d933b847458d465cd77e96cd702ff2e7be diff --git a/include/wxPython b/include/wxPython index 5d878c302f9..11de9371b6b 160000 --- a/include/wxPython +++ b/include/wxPython @@ -1 +1 @@ -Subproject commit 5d878c302f91caaa8970826e9c16d714efec50bd +Subproject commit 11de9371b6b737673ad5ea2703d214ada69a158a diff --git a/miscDeps b/miscDeps index 3a0065301ec..e2049468258 160000 --- a/miscDeps +++ b/miscDeps @@ -1 +1 @@ -Subproject commit 3a0065301ec91c52ae5bebf2c4403ce429db078c +Subproject commit e20494682583ddb9ab466e9b5a6a5dcfb7a34bc8 From 9be007eb4ef3b702bc20bb10f29a233156a3eb20 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 30 Jul 2019 07:12:58 -0400 Subject: [PATCH 16/23] Review action. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index a28d98bae83..ccb0bfa9ae5 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -9,9 +9,9 @@ from winVersion import isWin10 from . import IAccessible -from ..window.winConsole import WinConsole as WinConsoleWindow +from ..window import winConsole -class WinConsole(WinConsoleWindow, IAccessible): +class WinConsole(winConsole.WinConsole, IAccessible): "The legacy console implementation for situations where UIA isn't supported." pass From 7886917fbdba13237162118c3979232e5affc3ca Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 30 Jul 2019 12:22:20 -0400 Subject: [PATCH 17/23] Review actions. --- source/NVDAObjects/IAccessible/winConsole.py | 4 ++-- source/NVDAObjects/UIA/winConsoleUIA.py | 4 ++-- source/NVDAObjects/behaviors.py | 22 +++++++++++++------- source/NVDAObjects/window/winConsole.py | 4 ++-- source/appModules/putty.py | 4 ++-- source/keyboardHandler.py | 4 ++-- source/winConsoleHandler.py | 4 ++-- 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/source/NVDAObjects/IAccessible/winConsole.py b/source/NVDAObjects/IAccessible/winConsole.py index ccb0bfa9ae5..9347169bc5d 100644 --- a/source/NVDAObjects/IAccessible/winConsole.py +++ b/source/NVDAObjects/IAccessible/winConsole.py @@ -17,6 +17,6 @@ class WinConsole(winConsole.WinConsole, IAccessible): def findExtraOverlayClasses(obj, clsList): if isWin10(1607) and config.conf['terminals']['keyboardSupportInLegacy']: - from NVDAObjects.behaviors import TerminalWithKeyboardSupport - clsList.append(TerminalWithKeyboardSupport) + from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport + clsList.append(KeyboardHandlerBasedTypedCharSupport) clsList.append(WinConsole) diff --git a/source/NVDAObjects/UIA/winConsoleUIA.py b/source/NVDAObjects/UIA/winConsoleUIA.py index c9b1ee0219b..0c1c35da9f0 100644 --- a/source/NVDAObjects/UIA/winConsoleUIA.py +++ b/source/NVDAObjects/UIA/winConsoleUIA.py @@ -11,7 +11,7 @@ from winVersion import isWin10 from . import UIATextInfo -from ..behaviors import TerminalWithKeyboardSupport +from ..behaviors import KeyboardHandlerBasedTypedCharSupport from ..window import Window @@ -219,7 +219,7 @@ def _get_focusRedirect(self): return None -class WinConsoleUIA(TerminalWithKeyboardSupport): +class WinConsoleUIA(KeyboardHandlerBasedTypedCharSupport): #: Disable the name as it won't be localized name = "" #: Only process text changes every 30 ms, in case the console is getting diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 2295c9cb284..40ffd13dc5d 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -368,10 +368,16 @@ def event_loseFocus(self): self.stopMonitoring() -class TerminalWithKeyboardSupport(Terminal): +class KeyboardHandlerBasedTypedCharSupport(Terminal): """A Terminal object that also provides typed character support for - console applications on Windows 10 1607 and later.""" - #: Whether this object reliably sends textChange events. + console applications via keyboardHandler events. + This class relies on the toUnicodeEx Windows function, and in particular + the flag to preserve keyboard state available in Windows 10 1607 + and later.""" + #: Whether this object quickly and reliably sends textChange events + #: when its contents update. + #: Timely and reliable textChange events are required + #: to support password suppression. _supportsTextChange = True #: A queue of typed characters, to be dispatched on C{textChange}. #: This queue allows NVDA to suppress typed passwords when needed. @@ -395,7 +401,7 @@ def _reportNewText(self, line): # Clear the typed word buffer for new text lines. speech.clearTypedWordBuffer() self._queuedChars = [] - super(TerminalWithKeyboardSupport, self)._reportNewText(line) + super(KeyboardHandlerBasedTypedCharSupport, self)._reportNewText(line) def event_typedCharacter(self, ch): if ch == '\t': @@ -414,11 +420,11 @@ def event_typedCharacter(self, ch): ): self._queuedChars.append(ch) else: - super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) + super(KeyboardHandlerBasedTypedCharSupport, self).event_typedCharacter(ch) def event_textChange(self): self._dispatchQueue() - super(TerminalWithKeyboardSupport, self).event_textChange() + super(KeyboardHandlerBasedTypedCharSupport, self).event_textChange() @script(gestures=[ "kb:enter", @@ -443,13 +449,13 @@ def _calculateNewText(self, newLines, oldLines): self._findNonBlankIndices(newLines) != self._findNonBlankIndices(oldLines) ) - return super(TerminalWithKeyboardSupport, self)._calculateNewText(newLines, oldLines) + return super(KeyboardHandlerBasedTypedCharSupport, self)._calculateNewText(newLines, oldLines) def _dispatchQueue(self): """Sends queued typedCharacter events through to NVDA.""" while self._queuedChars: ch = self._queuedChars.pop(0) - super(TerminalWithKeyboardSupport, self).event_typedCharacter(ch) + super(KeyboardHandlerBasedTypedCharSupport, self).event_typedCharacter(ch) def _findNonBlankIndices(self, lines): """ diff --git a/source/NVDAObjects/window/winConsole.py b/source/NVDAObjects/window/winConsole.py index d18838a8f02..b1e2ab9f83f 100644 --- a/source/NVDAObjects/window/winConsole.py +++ b/source/NVDAObjects/window/winConsole.py @@ -6,7 +6,7 @@ import winConsoleHandler from . import Window -from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, TerminalWithKeyboardSupport +from ..behaviors import Terminal, EditableTextWithoutAutoSelectDetection, KeyboardHandlerBasedTypedCharSupport import api import core from scriptHandler import script @@ -23,7 +23,7 @@ class WinConsole(Terminal, EditableTextWithoutAutoSelectDetection, Window): def initOverlayClass(self): # Legacy consoles take quite a while to send textChange events. # This significantly impacts typing performance, so don't queue chars. - if isinstance(self, TerminalWithKeyboardSupport): + if isinstance(self, KeyboardHandlerBasedTypedCharSupport): self._supportsTextChange = False def _get_TextInfo(self): diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 047e8a1f1b4..0bec61b00a7 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -8,7 +8,7 @@ """ import oleacc -from NVDAObjects.behaviors import TerminalWithKeyboardSupport +from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible @@ -23,4 +23,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (TerminalWithKeyboardSupport, DisplayModelLiveText) + clsList[0:0] = (KeyboardHandlerBasedTypedCharSupport, DisplayModelLiveText) diff --git a/source/keyboardHandler.py b/source/keyboardHandler.py index a90a76a512a..05190ad3aa9 100644 --- a/source/keyboardHandler.py +++ b/source/keyboardHandler.py @@ -197,7 +197,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # #6017: handle typed characters in Win10 RS2 and above where we can't detect typed characters in-process # This code must be in the 'finally' block as code above returns in several places yet we still want to execute this particular code. focus=api.getFocusObject() - from NVDAObjects.behaviors import TerminalWithKeyboardSupport + from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport if ( # This is only possible in Windows 10 1607 and above winVersion.isWin10(1607) @@ -212,7 +212,7 @@ def internal_keyDownEvent(vkCode,scanCode,extended,injected): # or the focus is within a UWP app, where WM_CHAR never gets sent or focus.windowClassName.startswith('Windows.UI.Core') #Or this is a console with keyboard support, where WM_CHAR messages are doubled - or isinstance(focus, TerminalWithKeyboardSupport) + or isinstance(focus, KeyboardHandlerBasedTypedCharSupport) ) ): keyStates=(ctypes.c_byte*256)() diff --git a/source/winConsoleHandler.py b/source/winConsoleHandler.py index 47d27fa4696..9ad690e3398 100755 --- a/source/winConsoleHandler.py +++ b/source/winConsoleHandler.py @@ -134,7 +134,7 @@ def getConsoleVisibleLines(): @winUser.WINEVENTPROC def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestamp): - from NVDAObjects.behaviors import TerminalWithKeyboardSupport + from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport #We don't want to do anything with the event if the event is not for the window this console is in if window!=consoleObject.windowHandle: return @@ -148,7 +148,7 @@ def consoleWinEventHook(handle,eventID,window,objectID,childID,threadID,timestam y=winUser.GET_Y_LPARAM(objectID) consoleScreenBufferInfo=wincon.GetConsoleScreenBufferInfo(consoleOutputHandle) if ( - not isinstance(consoleObject, TerminalWithKeyboardSupport) + not isinstance(consoleObject, KeyboardHandlerBasedTypedCharSupport) and x < consoleScreenBufferInfo.dwCursorPosition.x and ( y == consoleScreenBufferInfo.dwCursorPosition.y From bd00ae22149e5f925d9e48bc1708b9d2e10d2d8c Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 30 Jul 2019 12:25:40 -0400 Subject: [PATCH 18/23] Keyboard support -> typed character support. --- source/gui/settingsDialogs.py | 2 +- user_docs/en/userGuide.t2t | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 5ae218ef44c..1c1a42d984f 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -2078,7 +2078,7 @@ def __init__(self, parent): sHelper.addItem(terminalsGroup) # Translators: This is the label for a checkbox in the # Advanced settings panel. - label = _("Use the new &keyboard support in legacy Windows consoles when available") + label = _("Use the new &typed character support in legacy Windows consoles when available") self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label)) self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"]) self.keyboardSupportInLegacyCheckBox.defaultValue = self._getDefaultValue(["terminals", "keyboardSupportInLegacy"]) diff --git a/user_docs/en/userGuide.t2t b/user_docs/en/userGuide.t2t index 514fc91f9f4..846c676ae88 100644 --- a/user_docs/en/userGuide.t2t +++ b/user_docs/en/userGuide.t2t @@ -1677,7 +1677,7 @@ When this option is enabled, NVDA will use a new, work in progress version of it ==== Speak passwords in UIA consoles ====[AdvancedSettingsWinConsoleSpeakPasswords] 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. -==== Use the new keyboard support in legacy Windows consoles when available ====[AdvancedSettingsKeyboardSupportInLegacy] +==== Use the new typed character support in legacy Windows consoles when available ====[AdvancedSettingsKeyboardSupportInLegacy] This option enables an alternative method for detecting typed characters in legacy Windows consoles. While it improves performance and prevents some console output from being spelled out, it may be incompatible with some terminal programs. This feature is available and enabled by default on Windows 10 versions 1607, 1703 and 1709 as well as on newer Windows 10 releases when UI Automation is unavailable or disabled. From 5fd387497b051c27c751a575f311c76d66e585d2 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Tue, 30 Jul 2019 12:28:29 -0400 Subject: [PATCH 19/23] Change accelerator. --- source/gui/settingsDialogs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/gui/settingsDialogs.py b/source/gui/settingsDialogs.py index 1c1a42d984f..a574b0f7694 100644 --- a/source/gui/settingsDialogs.py +++ b/source/gui/settingsDialogs.py @@ -2078,7 +2078,7 @@ def __init__(self, parent): sHelper.addItem(terminalsGroup) # Translators: This is the label for a checkbox in the # Advanced settings panel. - label = _("Use the new &typed character support in legacy Windows consoles when available") + label = _("Use the new t&yped character support in legacy Windows consoles when available") self.keyboardSupportInLegacyCheckBox=terminalsGroup.addItem(wx.CheckBox(self, label=label)) self.keyboardSupportInLegacyCheckBox.SetValue(config.conf["terminals"]["keyboardSupportInLegacy"]) self.keyboardSupportInLegacyCheckBox.defaultValue = self._getDefaultValue(["terminals", "keyboardSupportInLegacy"]) From f30db3af611c2ed25d254af0b7149f125b5c9471 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 31 Jul 2019 06:39:58 -0400 Subject: [PATCH 20/23] Meeting actions. --- source/NVDAObjects/behaviors.py | 2 ++ source/appModules/putty.py | 8 ++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 40ffd13dc5d..12edb8fcb69 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -371,6 +371,8 @@ def event_loseFocus(self): class KeyboardHandlerBasedTypedCharSupport(Terminal): """A Terminal object that also provides typed character support for console applications via keyboardHandler events. + This approach is an alternative to monitoring the console output for + characters close to the caret, or injecting in-process with NVDAHelper. This class relies on the toUnicodeEx Windows function, and in particular the flag to preserve keyboard state available in Windows 10 1607 and later.""" diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 0bec61b00a7..9b84bb6ebbe 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -8,10 +8,11 @@ """ import oleacc -from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport +from NVDAObjects.behaviors import KeyboardHandlerBasedTypedCharSupport, Terminal from NVDAObjects.window import DisplayModelEditableText, DisplayModelLiveText import appModuleHandler from NVDAObjects.IAccessible import IAccessible +from winVersion import isWin10 class AppModule(appModuleHandler.AppModule): # Allow this to be overridden for derived applications. @@ -23,4 +24,7 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): clsList.remove(DisplayModelEditableText) except ValueError: pass - clsList[0:0] = (KeyboardHandlerBasedTypedCharSupport, DisplayModelLiveText) + if isWin10(1607): + clsList[0:0] = (KeyboardHandlerBasedTypedCharSupport, DisplayModelLiveText) + else: + clsList[0:0] = (Terminal, DisplayModelLiveText) \ No newline at end of file From 71e06d9e8bb48f318c3a72119b6b7c43c47b374c Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 31 Jul 2019 06:41:25 -0400 Subject: [PATCH 21/23] Add newline at end of file. --- source/appModules/putty.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/appModules/putty.py b/source/appModules/putty.py index 9b84bb6ebbe..cd116342d28 100644 --- a/source/appModules/putty.py +++ b/source/appModules/putty.py @@ -27,4 +27,4 @@ def chooseNVDAObjectOverlayClasses(self, obj, clsList): if isWin10(1607): clsList[0:0] = (KeyboardHandlerBasedTypedCharSupport, DisplayModelLiveText) else: - clsList[0:0] = (Terminal, DisplayModelLiveText) \ No newline at end of file + clsList[0:0] = (Terminal, DisplayModelLiveText) From d52fae07be8c00576307a5f5f1af23e67be99914 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 31 Jul 2019 12:05:38 -0400 Subject: [PATCH 22/23] Update source/NVDAObjects/behaviors.py Co-Authored-By: Leonard de Ruijter --- source/NVDAObjects/behaviors.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index 12edb8fcb69..d5281c1bb4f 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -371,6 +371,9 @@ def event_loseFocus(self): class KeyboardHandlerBasedTypedCharSupport(Terminal): """A Terminal object that also provides typed character support for console applications via keyboardHandler events. + These events are queued from NVDA"s global keyboard hook. + Therefore, an event is fired for every single character that is being typed, + even when a character is not written to the console (e.g. in read only console applications). This approach is an alternative to monitoring the console output for characters close to the caret, or injecting in-process with NVDAHelper. This class relies on the toUnicodeEx Windows function, and in particular From d6c8a26edf8a4f8f36e150b1404d00d5021012b8 Mon Sep 17 00:00:00 2001 From: Bill Dengler Date: Wed, 31 Jul 2019 12:15:07 -0400 Subject: [PATCH 23/23] Review actions. --- source/NVDAObjects/behaviors.py | 29 +++++++++++++---------------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/source/NVDAObjects/behaviors.py b/source/NVDAObjects/behaviors.py index d5281c1bb4f..60a751439a3 100755 --- a/source/NVDAObjects/behaviors.py +++ b/source/NVDAObjects/behaviors.py @@ -371,7 +371,7 @@ def event_loseFocus(self): class KeyboardHandlerBasedTypedCharSupport(Terminal): """A Terminal object that also provides typed character support for console applications via keyboardHandler events. - These events are queued from NVDA"s global keyboard hook. + These events are queued from NVDA's global keyboard hook. Therefore, an event is fired for every single character that is being typed, even when a character is not written to the console (e.g. in read only console applications). This approach is an alternative to monitoring the console output for @@ -387,26 +387,19 @@ class KeyboardHandlerBasedTypedCharSupport(Terminal): #: A queue of typed characters, to be dispatched on C{textChange}. #: This queue allows NVDA to suppress typed passwords when needed. _queuedChars = [] - #: Whether the console got new text lines in its last update. - #: Used to determine if typed character/word buffers should be flushed. - _hasNewLines = False #: Whether the last typed character is a tab. #: If so, we should temporarily disable filtering as completions may #: be short. _hasTab = False def _reportNewText(self, line): - # Additional typed character filtering beyond that in LiveText + # Perform typed character filtering, as typed characters are handled with events. if ( not self._hasTab and len(line.strip()) < max(len(speech.curWordChars) + 1, 3) ): return - if self._hasNewLines: - # Clear the typed word buffer for new text lines. - speech.clearTypedWordBuffer() - self._queuedChars = [] - super(KeyboardHandlerBasedTypedCharSupport, self)._reportNewText(line) + super()._reportNewText(line) def event_typedCharacter(self, ch): if ch == '\t': @@ -425,11 +418,11 @@ def event_typedCharacter(self, ch): ): self._queuedChars.append(ch) else: - super(KeyboardHandlerBasedTypedCharSupport, self).event_typedCharacter(ch) + super().event_typedCharacter(ch) def event_textChange(self): self._dispatchQueue() - super(KeyboardHandlerBasedTypedCharSupport, self).event_textChange() + super().event_textChange() @script(gestures=[ "kb:enter", @@ -445,22 +438,26 @@ def script_flush_queuedChars(self, gesture): Since these gestures clear the current word/line, we should flush the queue to avoid erroneously reporting these chars. """ - gesture.send() self._queuedChars = [] speech.clearTypedWordBuffer() + gesture.send() def _calculateNewText(self, newLines, oldLines): - self._hasNewLines = ( + hasNewLines = ( self._findNonBlankIndices(newLines) != self._findNonBlankIndices(oldLines) ) - return super(KeyboardHandlerBasedTypedCharSupport, self)._calculateNewText(newLines, oldLines) + if hasNewLines: + # Clear the typed word buffer for new text lines. + speech.clearTypedWordBuffer() + self._queuedChars = [] + return super()._calculateNewText(newLines, oldLines) def _dispatchQueue(self): """Sends queued typedCharacter events through to NVDA.""" while self._queuedChars: ch = self._queuedChars.pop(0) - super(KeyboardHandlerBasedTypedCharSupport, self).event_typedCharacter(ch) + super().event_typedCharacter(ch) def _findNonBlankIndices(self, lines): """