diff --git a/appveyor/crowdinSync.py b/appveyor/crowdinSync.py index f1373527969..038c959b661 100644 --- a/appveyor/crowdinSync.py +++ b/appveyor/crowdinSync.py @@ -20,19 +20,12 @@ def request( - path: str, - method=requests.get, - headers: dict[str, str] | None = None, - **kwargs + path: str, method=requests.get, headers: dict[str, str] | None = None, **kwargs ) -> requests.Response: if headers is None: headers = {} headers["Authorization"] = f"Bearer {AUTH_TOKEN}" - r = method( - f"https://api.crowdin.com/api/v2/{path}", - headers=headers, - **kwargs - ) + r = method(f"https://api.crowdin.com/api/v2/{path}", headers=headers, **kwargs) # Convert errors to exceptions, but print the response before raising. try: r.raise_for_status() @@ -50,32 +43,18 @@ def uploadSourceFile(crowdinFileID: int, localFilePath: str) -> None: fn = os.path.basename(localFilePath) print(f"Uploading {localFilePath} to Crowdin temporary storage as {fn}") with open(localFilePath, "rb") as f: - r = request( - "storages", - method=requests.post, - headers={"Crowdin-API-FileName": fn}, - data=f - ) + r = request("storages", method=requests.post, headers={"Crowdin-API-FileName": fn}, data=f) storageID = r.json()["data"]["id"] print(f"Updating file {crowdinFileID} on Crowdin with storage ID {storageID}") - r = projectRequest( - f"files/{crowdinFileID}", - method=requests.put, - json={"storageId": storageID} - ) + r = projectRequest(f"files/{crowdinFileID}", method=requests.put, json={"storageId": storageID}) revisionId = r.json()["data"]["revisionId"] print(f"Updated to revision {revisionId}") def main(): - parser = argparse.ArgumentParser( - description="Syncs translations with Crowdin." - ) + parser = argparse.ArgumentParser(description="Syncs translations with Crowdin.") commands = parser.add_subparsers(dest="command", required=True) - uploadCommand = commands.add_parser( - "uploadSourceFile", - help="Upload a source file to Crowdin." - ) + uploadCommand = commands.add_parser("uploadSourceFile", help="Upload a source file to Crowdin.") uploadCommand.add_argument("crowdinFileID", type=int, help="The Crowdin file ID.") uploadCommand.add_argument("localFilePath", help="The path to the local file.") args = parser.parse_args() diff --git a/appveyor/mozillaSyms.py b/appveyor/mozillaSyms.py index 365bcc19a30..ebf7e11e076 100644 --- a/appveyor/mozillaSyms.py +++ b/appveyor/mozillaSyms.py @@ -18,7 +18,7 @@ NVDA_LIB = os.path.join(NVDA_SOURCE, "lib") NVDA_LIB64 = os.path.join(NVDA_SOURCE, "lib64") ZIP_FILE = os.path.join(SCRIPT_DIR, "mozillaSyms.zip") -URL = 'https://symbols.mozilla.org/upload/' +URL = "https://symbols.mozilla.org/upload/" # The dlls for which symbols are to be uploaded to Mozilla. # This only needs to include dlls injected into Mozilla products. @@ -27,56 +27,59 @@ "ISimpleDOM.dll", "nvdaHelperRemote.dll", ] -DLL_FILES = [f +DLL_FILES = [ + f for dll in DLL_NAMES # We need both the 32 bit and 64 bit symbols. - for f in (os.path.join(NVDA_LIB, dll), os.path.join(NVDA_LIB64, dll))] + for f in (os.path.join(NVDA_LIB, dll), os.path.join(NVDA_LIB64, dll)) +] + class ProcError(Exception): def __init__(self, returncode, stderr): self.returncode = returncode self.stderr = stderr + def check_output(command): - proc = subprocess.Popen(command, - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True) + proc = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True) stdout, stderr = proc.communicate() if proc.returncode != 0: raise ProcError(proc.returncode, stderr) return stdout + def processFile(path): - print("dump_syms %s"%path) + print("dump_syms %s" % path) try: stdout = check_output([DUMP_SYMS, path]) except ProcError as e: print('Error: running "%s %s": %s' % (DUMP_SYMS, path, e.stderr)) return None, None, None - bits = stdout.splitlines()[0].split(' ', 4) + bits = stdout.splitlines()[0].split(" ", 4) if len(bits) != 5: return None, None, None _, platform, cpu_arch, debug_id, debug_file = bits # debug_file will have a .pdb extension; e.g. nvdaHelperRemote.dll.pdb. # The output file format should have a .sym extension instead. # Strip .pdb and add .sym. - sym_file = debug_file[:-4] + '.sym' + sym_file = debug_file[:-4] + ".sym" filename = os.path.join(debug_file, debug_id, sym_file) debug_filename = os.path.join(debug_file, debug_id, debug_file) return filename, stdout, debug_filename + def generate(): count = 0 - with zipfile.ZipFile(ZIP_FILE, 'w', zipfile.ZIP_DEFLATED) as zf: + with zipfile.ZipFile(ZIP_FILE, "w", zipfile.ZIP_DEFLATED) as zf: for f in DLL_FILES: filename, contents, debug_filename = processFile(f) if not (filename and contents): - print('Error dumping symbols') + print("Error dumping symbols") raise RuntimeError zf.writestr(filename, contents) count += 1 - print('Added %d files to %s' % (count, ZIP_FILE)) + print("Added %d files to %s" % (count, ZIP_FILE)) def upload(): @@ -85,37 +88,36 @@ def upload(): if i > 0: print("Sleeping for 15 seconds before next attempt.") import time + time.sleep(15) try: r = requests.post( URL, - files={'symbols.zip': open(ZIP_FILE, 'rb')}, - headers={'Auth-Token': os.getenv('mozillaSymsAuthToken')}, - allow_redirects=False + files={"symbols.zip": open(ZIP_FILE, "rb")}, + headers={"Auth-Token": os.getenv("mozillaSymsAuthToken")}, + allow_redirects=False, ) break # success except Exception as e: print(f"Attempt {i + 1} failed: {e!r}") errors.append(repr(e)) else: # no break in for loop - allErrors = "\n".join( - f"Attempt {index + 1} error: \n{e}" - for index, e in enumerate(errors) - ) + allErrors = "\n".join(f"Attempt {index + 1} error: \n{e}" for index, e in enumerate(errors)) raise RuntimeError(allErrors) if 200 <= r.status_code < 300: - print('Uploaded successfully!') + print("Uploaded successfully!") elif r.status_code < 400: - print('Error: bad auth token? (%d)' % r.status_code) + print("Error: bad auth token? (%d)" % r.status_code) raise RuntimeError else: - print('Error: %d' % r.status_code) + print("Error: %d" % r.status_code) print(r.text) raise RuntimeError return 0 -if __name__ == '__main__': + +if __name__ == "__main__": try: generate() upload() diff --git a/extras/controllerClient/examples/example_python.py b/extras/controllerClient/examples/example_python.py index bd013ac02d1..b6abedaea71 100644 --- a/extras/controllerClient/examples/example_python.py +++ b/extras/controllerClient/examples/example_python.py @@ -31,19 +31,19 @@ def onMarkReached(name: str) -> int: ssml = ( - '' - 'This is one sentence. ' + "" + "This is one sentence. " '' 'This sentence is pronounced with higher pitch.' '' - 'This is a third sentence. ' + "This is a third sentence. " '' - 'This is a fourth sentence. We will stay silent for a second after this one.' + "This is a fourth sentence. We will stay silent for a second after this one." '' '' - 'This is a fifth sentence. ' + "This is a fifth sentence. " '' - '' + "" ) clientLib.nvdaController_setOnSsmlMarkReachedCallback(onMarkReached) clientLib.nvdaController_speakSsml(ssml, -1, 0, False) diff --git a/projectDocs/dev/developerGuide/conf.py b/projectDocs/dev/developerGuide/conf.py index ea8cb7a4e1b..2cf9e92d421 100644 --- a/projectDocs/dev/developerGuide/conf.py +++ b/projectDocs/dev/developerGuide/conf.py @@ -9,6 +9,7 @@ import os import sys + _appDir = os.path.abspath(os.path.join("..", "..", "..", "source")) sys.path.insert(0, _appDir) @@ -23,11 +24,13 @@ # by comTypes. # This patch causes the error to be ignored, which matches the behavior at runtime. import monkeyPatches.comtypesMonkeyPatches # noqa: E402 + monkeyPatches.comtypesMonkeyPatches.replace_check_version() monkeyPatches.comtypesMonkeyPatches.appendComInterfacesToGenSearchPath() # Initialize languageHandler so that sphinx is able to deal with translatable strings. import languageHandler # noqa: E402 + languageHandler.setLanguage("en") # Initialize globalVars.appArgs to something sensible. @@ -47,6 +50,7 @@ # Import NVDA's versionInfo module. import versionInfo # noqa: E402 + # Set a suitable updateVersionType for the updateCheck module to be imported versionInfo.updateVersionType = "stable" @@ -58,9 +62,7 @@ # The major project version version = versionInfo.formatVersionForGUI( - versionInfo.version_year, - versionInfo.version_major, - versionInfo.version_minor + versionInfo.version_year, versionInfo.version_major, versionInfo.version_minor ) # The full version, including alpha/beta/rc tags @@ -68,24 +70,22 @@ # -- General configuration --------------------------------------------------- -default_role = 'py:obj' +default_role = "py:obj" # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ - 'sphinx.ext.autodoc', + "sphinx.ext.autodoc", ] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This pattern also affects html_static_path and html_extra_path. -exclude_patterns = [ - "_build" -] +exclude_patterns = ["_build"] # -- Options for HTML output ------------------------------------------------- @@ -100,7 +100,7 @@ # Both the class’ and the __init__ method’s docstring are concatenated and inserted. autoclass_content = "both" -autodoc_member_order = 'bysource' +autodoc_member_order = "bysource" autodoc_mock_imports = [ "louis", # Not our project ] @@ -110,5 +110,6 @@ from sphinx.ext.autodoc.mock import _make_subclass # noqa: E402 import config # noqa: E402 + # Mock an instance of the configuration manager. config.conf = _make_subclass("conf", "config")() diff --git a/site_scons/site_tools/doxygen.py b/site_scons/site_tools/doxygen.py index 09af7ebd65f..e164c0eb3ed 100644 --- a/site_scons/site_tools/doxygen.py +++ b/site_scons/site_tools/doxygen.py @@ -1,10 +1,10 @@ # # Copyright (C) 2005, 2006 Matthew A. Nicholson # Copyright (C) 2006 Tim Blechmann -#Copyright (C) 2011 Michael Curran -#Copyright (C) 2014 Alberto Buffolino -#Copyright (C) 2016 Babbage B.V. -#Based on code from http://www.scons.org/wiki/DoxygenBuilder +# Copyright (C) 2011 Michael Curran +# Copyright (C) 2014 Alberto Buffolino +# Copyright (C) 2016 Babbage B.V. +# Based on code from http://www.scons.org/wiki/DoxygenBuilder # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public @@ -26,195 +26,234 @@ from functools import reduce import winreg + def fetchDoxygenPath(): try: - with winreg.OpenKey(winreg.HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\doxygen_is1", 0, winreg.KEY_READ | winreg.KEY_WOW64_64KEY) as doxygenKey: - doxygenPath= '"%s"'%os.path.join(winreg.QueryValueEx(doxygenKey, "InstallLocation")[0], "Bin", "doxygen.exe") + with winreg.OpenKey( + winreg.HKEY_LOCAL_MACHINE, + r"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\doxygen_is1", + 0, + winreg.KEY_READ | winreg.KEY_WOW64_64KEY, + ) as doxygenKey: + doxygenPath = '"%s"' % os.path.join( + winreg.QueryValueEx(doxygenKey, "InstallLocation")[0], "Bin", "doxygen.exe" + ) except WindowsError: - return 'doxygen' + return "doxygen" return doxygenPath + def DoxyfileParse(file_contents): - """ - Parse a Doxygen source file and return a dictionary of all the values. - Values will be strings and lists of strings. - """ - data = {} - - import shlex - lex = shlex.shlex(instream = file_contents, posix = True) - lex.wordchars += "*+./-:" - lex.whitespace = lex.whitespace.replace("\n", "") - lex.escape = "" - - lineno = lex.lineno # noqa: F841 - token = lex.get_token() - key = token # the first token should be a key - last_token = "" - key_token = False - next_key = False # noqa: F841 - new_data = True - - def append_data(data, key, new_data, token): - if new_data or len(data[key]) == 0: - data[key].append(token) - else: - data[key][-1] += token - - while token: - if token in ['\n']: - if last_token not in ['\\']: - key_token = True - elif token in ['\\']: - pass - elif key_token: - key = token - key_token = False - else: - if token == "+=": - if key not in data: - data[key] = list() - elif token == "=": - data[key] = list() - else: - append_data( data, key, new_data, token ) - new_data = True - - last_token = token - token = lex.get_token() - - if last_token == '\\' and token != '\n': - new_data = False - append_data( data, key, new_data, '\\' ) - - # compress lists of len 1 into single strings - # Wrap items into a list, since we're mutating the dictionary - for (k, v) in list(data.items()): - if len(v) == 0: - data.pop(k) - - # items in the following list will be kept as lists and not converted to strings - if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]: - continue - - if len(v) == 1: - data[k] = v[0] - - return data + """ + Parse a Doxygen source file and return a dictionary of all the values. + Values will be strings and lists of strings. + """ + data = {} + + import shlex + + lex = shlex.shlex(instream=file_contents, posix=True) + lex.wordchars += "*+./-:" + lex.whitespace = lex.whitespace.replace("\n", "") + lex.escape = "" + + lineno = lex.lineno # noqa: F841 + token = lex.get_token() + key = token # the first token should be a key + last_token = "" + key_token = False + next_key = False # noqa: F841 + new_data = True + + def append_data(data, key, new_data, token): + if new_data or len(data[key]) == 0: + data[key].append(token) + else: + data[key][-1] += token + + while token: + if token in ["\n"]: + if last_token not in ["\\"]: + key_token = True + elif token in ["\\"]: + pass + elif key_token: + key = token + key_token = False + else: + if token == "+=": + if key not in data: + data[key] = list() + elif token == "=": + data[key] = list() + else: + append_data(data, key, new_data, token) + new_data = True + + last_token = token + token = lex.get_token() + + if last_token == "\\" and token != "\n": + new_data = False + append_data(data, key, new_data, "\\") + + # compress lists of len 1 into single strings + # Wrap items into a list, since we're mutating the dictionary + for k, v in list(data.items()): + if len(v) == 0: + data.pop(k) + + # items in the following list will be kept as lists and not converted to strings + if k in ["INPUT", "FILE_PATTERNS", "EXCLUDE_PATTERNS"]: + continue + + if len(v) == 1: + data[k] = v[0] + + return data + def DoxySourceScan(node, env, path): - """ - Doxygen Doxyfile source scanner. This should scan the Doxygen file and add - any files used to generate docs to the list of source files. - """ - default_file_patterns = [ - '*.c', '*.cc', '*.cxx', '*.cpp', '*.c++', '*.java', '*.ii', '*.ixx', - '*.ipp', '*.i++', '*.inl', '*.h', '*.hh ', '*.hxx', '*.hpp', '*.h++', - '*.idl', '*.odl', '*.cs', '*.php', '*.php3', '*.inc', '*.m', '*.mm', - '*.py', - ] - - default_exclude_patterns = [ - '*~', - ] - - sources = [] - - with open(node.abspath) as contents: - data = DoxyfileParse(contents) - - if data.get("RECURSIVE", "NO") == "YES": - recursive = True - else: - recursive = False - - file_patterns = data.get("FILE_PATTERNS", default_file_patterns) - exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) - - for node in data.get("INPUT", []): - if os.path.isfile(node): - sources.append(node) - elif os.path.isdir(node): - if recursive: - for root, dirs, files in os.walk(node): - for f in files: - filename = os.path.join(root, f) - - pattern_check = reduce(lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False) - exclude_check = reduce(lambda x, y: x and fnmatch(filename, y), exclude_patterns, True) - - if pattern_check and not exclude_check: - sources.append(filename) - else: - for pattern in file_patterns: - sources.extend(glob.glob("/".join([node, pattern]))) - - sources = [env.File(path) for path in sources] - return sources + """ + Doxygen Doxyfile source scanner. This should scan the Doxygen file and add + any files used to generate docs to the list of source files. + """ + default_file_patterns = [ + "*.c", + "*.cc", + "*.cxx", + "*.cpp", + "*.c++", + "*.java", + "*.ii", + "*.ixx", + "*.ipp", + "*.i++", + "*.inl", + "*.h", + "*.hh ", + "*.hxx", + "*.hpp", + "*.h++", + "*.idl", + "*.odl", + "*.cs", + "*.php", + "*.php3", + "*.inc", + "*.m", + "*.mm", + "*.py", + ] + + default_exclude_patterns = [ + "*~", + ] + + sources = [] + + with open(node.abspath) as contents: + data = DoxyfileParse(contents) + + if data.get("RECURSIVE", "NO") == "YES": + recursive = True + else: + recursive = False + + file_patterns = data.get("FILE_PATTERNS", default_file_patterns) + exclude_patterns = data.get("EXCLUDE_PATTERNS", default_exclude_patterns) + + for node in data.get("INPUT", []): + if os.path.isfile(node): + sources.append(node) + elif os.path.isdir(node): + if recursive: + for root, dirs, files in os.walk(node): + for f in files: + filename = os.path.join(root, f) + + pattern_check = reduce( + lambda x, y: x or bool(fnmatch(filename, y)), file_patterns, False + ) + exclude_check = reduce( + lambda x, y: x and fnmatch(filename, y), exclude_patterns, True + ) + + if pattern_check and not exclude_check: + sources.append(filename) + else: + for pattern in file_patterns: + sources.extend(glob.glob("/".join([node, pattern]))) + + sources = [env.File(path) for path in sources] + return sources def DoxySourceScanCheck(node, env): - """Check if we should scan this file""" - return os.path.isfile(node.path) + """Check if we should scan this file""" + return os.path.isfile(node.path) + def DoxyEmitter(source, target, env): - """Doxygen Doxyfile emitter""" - # possible output formats and their default values and output locations - output_formats = { - "HTML": ("YES", "html"), - "LATEX": ("YES", "latex"), - "RTF": ("NO", "rtf"), - "MAN": ("NO", "man"), - "XML": ("NO", "xml"), - } + """Doxygen Doxyfile emitter""" + # possible output formats and their default values and output locations + output_formats = { + "HTML": ("YES", "html"), + "LATEX": ("YES", "latex"), + "RTF": ("NO", "rtf"), + "MAN": ("NO", "man"), + "XML": ("NO", "xml"), + } + + with open(source[0].abspath) as contents: + data = DoxyfileParse(contents) - with open(source[0].abspath) as contents: - data = DoxyfileParse(contents) + targets = [] + out_dir = source[0].Dir(data.get("OUTPUT_DIRECTORY", ".")) - targets = [] - out_dir = source[0].Dir(data.get("OUTPUT_DIRECTORY", ".")) + # add our output locations + for k, v in list(output_formats.items()): + if data.get("GENERATE_" + k, v[0]) == "YES": + targets.append(out_dir.Dir(v[1])) - # add our output locations - for (k, v) in list(output_formats.items()): - if data.get("GENERATE_" + k, v[0]) == "YES": - targets.append(out_dir.Dir(v[1])) + # set up cleaning stuff + for node in targets: + env.Clean(node, node) - # set up cleaning stuff - for node in targets: - env.Clean(node, node) + return (targets, source) - return (targets, source) def generate(env): - """ - Add builders and construction variables for the - Doxygen tool. This is currently for Doxygen 1.4.6. - """ - doxyfile_scanner = env.Scanner( - DoxySourceScan, - "DoxySourceScan", - scan_check = DoxySourceScanCheck, - ) - - import SCons.Builder - doxyfile_builder = SCons.Builder.Builder( - action = "cd ${SOURCE.dir} && ${DOXYGEN} ${SOURCE.file}", - emitter = DoxyEmitter, - single_source = True, - source_scanner = doxyfile_scanner, - ) - - env.Append(BUILDERS = { - 'Doxygen': doxyfile_builder, - }) - - env.AppendUnique( - DOXYGEN = fetchDoxygenPath() - ) + """ + Add builders and construction variables for the + Doxygen tool. This is currently for Doxygen 1.4.6. + """ + doxyfile_scanner = env.Scanner( + DoxySourceScan, + "DoxySourceScan", + scan_check=DoxySourceScanCheck, + ) + + import SCons.Builder + + doxyfile_builder = SCons.Builder.Builder( + action="cd ${SOURCE.dir} && ${DOXYGEN} ${SOURCE.file}", + emitter=DoxyEmitter, + single_source=True, + source_scanner=doxyfile_scanner, + ) + + env.Append( + BUILDERS={ + "Doxygen": doxyfile_builder, + } + ) + + env.AppendUnique(DOXYGEN=fetchDoxygenPath()) + def exists(env): """ Make sure doxygen exists. """ return bool(fetchDoxygenPath()) - diff --git a/site_scons/site_tools/gettextTool.py b/site_scons/site_tools/gettextTool.py index 9daaac33757..8d21f84ba54 100644 --- a/site_scons/site_tools/gettextTool.py +++ b/site_scons/site_tools/gettextTool.py @@ -1,15 +1,15 @@ ### -#This file is a part of the NVDA project. -#URL: http://www.nvda-project.org/ -#Copyright 2010-2012 NV Access Limited -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU General Public License version 2.0, as published by -#the Free Software Foundation. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#This license can be found at: -#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# This file is a part of the NVDA project. +# URL: http://www.nvda-project.org/ +# Copyright 2010-2012 NV Access Limited +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2.0, as published by +# the Free Software Foundation. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# This license can be found at: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html ### import os @@ -17,13 +17,17 @@ # Get the path to msgfmt. MSGFMT = os.path.abspath(os.path.join("miscDeps", "tools", "msgfmt.exe")) + def exists(env): return True + def generate(env): - env['BUILDERS']['gettextMoFile']=env.Builder( - action=env.Action([[MSGFMT,"-o","$TARGET","$SOURCE"]], - lambda t,s,e: 'Compiling gettext template %s'%s[0].path), - suffix='.mo', - src_suffix='.po' + env["BUILDERS"]["gettextMoFile"] = env.Builder( + action=env.Action( + [[MSGFMT, "-o", "$TARGET", "$SOURCE"]], + lambda t, s, e: "Compiling gettext template %s" % s[0].path, + ), + suffix=".mo", + src_suffix=".po", ) diff --git a/site_scons/site_tools/listModules.py b/site_scons/site_tools/listModules.py index 8c0950893e9..0c658513365 100644 --- a/site_scons/site_tools/listModules.py +++ b/site_scons/site_tools/listModules.py @@ -12,9 +12,7 @@ def _generateModuleList( - target: list[SCons.Node.FS.File], - source: list[SCons.Node.FS.Dir], - env: SCons.Environment.Environment + target: list[SCons.Node.FS.File], source: list[SCons.Node.FS.Dir], env: SCons.Environment.Environment ) -> None: """ Generate a list of Python modules from compiled '.pyc' files within `library.zip` in the source folder. @@ -42,10 +40,12 @@ def _generateModuleList( # Convert the file paths to python module format # eg: NVDAObjects/IAccessible/__init__.pyc --> NVDAObjects.IAccessible - importedModules = sorted({ - re.sub(r"(.__init__|.__version__|._version)?\.pyc$", "", module_path).replace("/", ".") - for module_path in pycFiles - }) + importedModules = sorted( + { + re.sub(r"(.__init__|.__version__|._version)?\.pyc$", "", module_path).replace("/", ".") + for module_path in pycFiles + } + ) # Sanity check for something guaranteed to be in library.zip if "NVDAObjects.UIA" not in importedModules: @@ -58,7 +58,8 @@ def _generateModuleList( def generate(env: SCons.Environment.Environment): env["BUILDERS"]["GenerateModuleList"] = SCons.Builder.Builder( - action=SCons.Action.Action(_generateModuleList)) + action=SCons.Action.Action(_generateModuleList) + ) def exists(env: SCons.Environment.Environment) -> bool: diff --git a/site_scons/site_tools/md2html.py b/site_scons/site_tools/md2html.py index 5bb71e2fce4..e6c61cf0588 100644 --- a/site_scons/site_tools/md2html.py +++ b/site_scons/site_tools/md2html.py @@ -13,18 +13,20 @@ import SCons.Node.FS import SCons.Environment -DEFAULT_EXTENSIONS = frozenset({ - # Supports tables, HTML mixed with markdown, code blocks, custom attributes and more - "markdown.extensions.extra", - # Allows TOC with [TOC]" - "markdown.extensions.toc", - # Makes list behaviour better, including 2 space indents by default - "mdx_truly_sane_lists", - # External links will open in a new tab, and title will be set to the link text - "markdown_link_attr_modifier", - # Adds links to GitHub authors, issues and PRs - "mdx_gh_links", -}) +DEFAULT_EXTENSIONS = frozenset( + { + # Supports tables, HTML mixed with markdown, code blocks, custom attributes and more + "markdown.extensions.extra", + # Allows TOC with [TOC]" + "markdown.extensions.toc", + # Makes list behaviour better, including 2 space indents by default + "mdx_truly_sane_lists", + # External links will open in a new tab, and title will be set to the link text + "markdown_link_attr_modifier", + # Adds links to GitHub authors, issues and PRs + "mdx_gh_links", + } +) EXTENSIONS_CONFIG = { "markdown_link_attr_modifier": { @@ -55,6 +57,7 @@ def _replaceNVDATags(md: str, env: SCons.Environment.Environment) -> str: import versionInfo + # Replace tags in source file md = md.replace("NVDA_VERSION", env["version"]) md = md.replace("NVDA_URL", versionInfo.url) @@ -86,6 +89,7 @@ def _getTitle(mdBuffer: io.StringIO, isKeyCommands: bool = False) -> str: def _createAttributeFilter() -> dict[str, set[str]]: # Create attribute filter exceptions for HTML sanitization import nh3 + allowedAttributes: dict[str, set[str]] = deepcopy(nh3.ALLOWED_ATTRIBUTES) attributesWithAnchors = {"h1", "h2", "h3", "h4", "h5", "h6", "td"} @@ -119,6 +123,7 @@ def _generateSanitizedHTML(md: str, isKeyCommands: bool = False) -> str: extensions = set(DEFAULT_EXTENSIONS) if isKeyCommands: from user_docs.keyCommandsDoc import KeyCommandsExtension + extensions.add(KeyCommandsExtension()) htmlOutput = markdown.markdown( @@ -141,9 +146,7 @@ def _generateSanitizedHTML(md: str, isKeyCommands: bool = False) -> str: def md2html_actionFunc( - target: list[SCons.Node.FS.File], - source: list[SCons.Node.FS.File], - env: SCons.Environment.Environment + target: list[SCons.Node.FS.File], source: list[SCons.Node.FS.File], env: SCons.Environment.Environment ): isKeyCommands = target[0].path.endswith("keyCommands.html") isUserGuide = target[0].path.endswith("userGuide.html") @@ -216,5 +219,5 @@ def generate(env: SCons.Environment.Environment): env["BUILDERS"]["md2html"] = env.Builder( action=env.Action(md2html_actionFunc, lambda t, s, e: f"Converting {s[0].path} to {t[0].path}"), suffix=".html", - src_suffix=".md" + src_suffix=".md", ) diff --git a/site_scons/site_tools/msrpc.py b/site_scons/site_tools/msrpc.py index e55cecf4e85..c7e88ea46ff 100644 --- a/site_scons/site_tools/msrpc.py +++ b/site_scons/site_tools/msrpc.py @@ -1,83 +1,92 @@ ### -#This file is a part of the NVDA project. -#URL: http://www.nvda-project.org/ -#Copyright 2006-2010 NVDA contributers. -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU General Public License version 2.0, as published by -#the Free Software Foundation. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#This license can be found at: -#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html +# This file is a part of the NVDA project. +# URL: http://www.nvda-project.org/ +# Copyright 2006-2010 NVDA contributers. +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License version 2.0, as published by +# the Free Software Foundation. +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# This license can be found at: +# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html ### -#MSRPC tool -#Provides the MSRPCStubs builder which can use MIDL to generate header, client stub, and server stub files from an IDL. +# MSRPC tool +# Provides the MSRPCStubs builder which can use MIDL to generate header, client stub, and server stub files from an IDL. from SCons import Util from SCons.Builder import Builder -#This build emitter tells the builder that a header file, a client stub c file, and a server stub c file will be generated -def MSRPCStubs_buildEmitter(target,source,env): - base,ext=Util.splitext(str(target[0] if len(target)>0 else source[0])) - newTargets=['%s.h'%base] - if not env['MSRPCStubs_noServer']: - newTargets.append('%s_S.c'%base) - if not env['MSRPCStubs_noClient']: - newTargets.append('%s_C.c'%base) - return (newTargets,source) - -def MSRPCStubs_builder_actionGenerator(target,source,env,for_signature): - sources=[] + +# This build emitter tells the builder that a header file, a client stub c file, and a server stub c file will be generated +def MSRPCStubs_buildEmitter(target, source, env): + base, ext = Util.splitext(str(target[0] if len(target) > 0 else source[0])) + newTargets = ["%s.h" % base] + if not env["MSRPCStubs_noServer"]: + newTargets.append("%s_S.c" % base) + if not env["MSRPCStubs_noClient"]: + newTargets.append("%s_C.c" % base) + return (newTargets, source) + + +def MSRPCStubs_builder_actionGenerator(target, source, env, for_signature): + sources = [] for src in source: - src=str(src) - if src.endswith('.acf'): - sources.append('/acf %s'%src) + src = str(src) + if src.endswith(".acf"): + sources.append("/acf %s" % src) else: sources.append(src) - sources=" ".join(sources) - targets=[] + sources = " ".join(sources) + targets = [] for tg in target: - tg=str(tg) - if tg.endswith('.h'): - targets.append('/header %s'%tg) - elif tg.endswith('_S.c'): - targets.append('/sstub %s'%tg) - elif tg.endswith('_C.c'): - targets.append('/cstub %s'%tg) + tg = str(tg) + if tg.endswith(".h"): + targets.append("/header %s" % tg) + elif tg.endswith("_S.c"): + targets.append("/sstub %s" % tg) + elif tg.endswith("_C.c"): + targets.append("/cstub %s" % tg) else: - raise ValueError("Don't know what to do with %s"%tg) - targets=" ".join(targets) - noServer="/server none" if env.get('MSRPCStubs_noServer',False) else "" - noClient="/client none" if env.get('MSRPCStubs_noClient',False) else "" + raise ValueError("Don't know what to do with %s" % tg) + targets = " ".join(targets) + noServer = "/server none" if env.get("MSRPCStubs_noServer", False) else "" + noClient = "/client none" if env.get("MSRPCStubs_noClient", False) else "" - prefix=env.get('MSRPCStubs_prefix',"") + prefix = env.get("MSRPCStubs_prefix", "") if prefix: - prefix="/prefix all %s"%prefix - serverPrefix=env.get('MSRPCStubs_serverPrefix',"") + prefix = "/prefix all %s" % prefix + serverPrefix = env.get("MSRPCStubs_serverPrefix", "") if serverPrefix: - serverPrefix="/prefix server %s"%serverPrefix - clientPrefix=env.get('MSRPCStubs_clientPrefix',"") + serverPrefix = "/prefix server %s" % serverPrefix + clientPrefix = env.get("MSRPCStubs_clientPrefix", "") if clientPrefix: - clientPrefix="/prefix client %s"%clientPrefix + clientPrefix = "/prefix client %s" % clientPrefix + + return " ".join( + ["${MIDL}", "${MIDLFLAGS}", noServer, noClient, prefix, serverPrefix, clientPrefix, targets, sources] + ) - return " ".join(['${MIDL}','${MIDLFLAGS}',noServer,noClient,prefix,serverPrefix,clientPrefix,targets,sources]) -MSRPCStubs_builder=Builder( +MSRPCStubs_builder = Builder( generator=MSRPCStubs_builder_actionGenerator, - src_suffix=['.idl','.acf'], + src_suffix=[".idl", ".acf"], emitter=MSRPCStubs_buildEmitter, ) + def exists(env): from SCons.Tool import midl + return midl.exists(env) + def generate(env): - if 'MIDL' not in env: + if "MIDL" not in env: from SCons.Tool import midl + midl.generate(env) - env['BUILDERS']['MSRPCStubs']=MSRPCStubs_builder - env['MSRPCStubs_noServer']=False - env['MSRPCStubs_noClient']=False + env["BUILDERS"]["MSRPCStubs"] = MSRPCStubs_builder + env["MSRPCStubs_noServer"] = False + env["MSRPCStubs_noClient"] = False diff --git a/site_scons/site_tools/recursiveInstall.py b/site_scons/site_tools/recursiveInstall.py index 386f54bb4d9..5fe1a27cc6c 100644 --- a/site_scons/site_tools/recursiveInstall.py +++ b/site_scons/site_tools/recursiveInstall.py @@ -1,10 +1,10 @@ -#from http://xtargets.com/2010/04/21/recursive-install-builder-for-scons/ +# from http://xtargets.com/2010/04/21/recursive-install-builder-for-scons/ # This tool adds an # # env.RecursiveInstall( target, path ) # -# This is usefull for doing -# +# This is usefull for doing +# # k = env.RecursiveInstall(dir_target, dir_source) # # and if any thing in dir_source is updated @@ -31,37 +31,38 @@ import os -def recursive_install(env, path ): - nodes = env.Glob \ - ( os.path.join(path, '*') - , strings=False - ) - out = [] - for n in nodes: - if n.isdir(): - out.extend( recursive_install(env, n.abspath )) - else: - out.append(n) - return out +def recursive_install(env, path): + nodes = env.Glob(os.path.join(path, "*"), strings=False) + out = [] + for n in nodes: + if n.isdir(): + out.extend(recursive_install(env, n.abspath)) + else: + out.append(n) + + return out + def RecursiveInstall(env, target, dir): - nodes = recursive_install(env, dir) + nodes = recursive_install(env, dir) - dir = env.Dir(dir).abspath - target = env.Dir(target).abspath + dir = env.Dir(dir).abspath + target = env.Dir(target).abspath - l = len(dir) + 1 # noqa: E741 + l = len(dir) + 1 # noqa: E741 - relnodes = [ n.abspath[l:] for n in nodes ] + relnodes = [n.abspath[l:] for n in nodes] + + for n in relnodes: + t = os.path.join(target, n) + s = os.path.join(dir, n) + env.InstallAs(env.File(t), env.File(s)) - for n in relnodes: - t = os.path.join(target, n) - s = os.path.join(dir, n) - env.InstallAs ( env.File(t), env.File(s)) def generate(env): - env.AddMethod(RecursiveInstall) + env.AddMethod(RecursiveInstall) + def exists(env): - return True + return True diff --git a/source/COMRegistrationFixes/__init__.py b/source/COMRegistrationFixes/__init__.py index 5ccdc0a8338..32e7cda7fef 100644 --- a/source/COMRegistrationFixes/__init__.py +++ b/source/COMRegistrationFixes/__init__.py @@ -117,8 +117,9 @@ def fixCOMRegistrations() -> None: OSMajorMinor = (winVer.major, winVer.minor) is64bit = winVer.processorArchitecture.endswith("64") log.debug( - f"Fixing COM registrations for Windows {OSMajorMinor[0]}.{OSMajorMinor[1]}, " - "{} bit.".format("64" if is64bit else "32") + f"Fixing COM registrations for Windows {OSMajorMinor[0]}.{OSMajorMinor[1]}, " "{} bit.".format( + "64" if is64bit else "32" + ) ) # OLEACC (MSAA) proxies apply32bitRegistryPatch(OLEACC_REG_FILE_PATH) diff --git a/source/IAccessibleHandler/__init__.py b/source/IAccessibleHandler/__init__.py index 9c8b4f9685e..5df45f04784 100644 --- a/source/IAccessibleHandler/__init__.py +++ b/source/IAccessibleHandler/__init__.py @@ -4,6 +4,7 @@ # See the file COPYING for more details. import typing + # F401 imported but unused. RelationType should be exposed from IAccessibleHandler, in future __all__ # should be used to export it. from .types import RelationType # noqa: F401 @@ -247,9 +248,7 @@ State = controlTypes.State -def _getStatesSetFromIAccessibleStates( - IAccessibleStates: int -) -> Set[controlTypes.State]: +def _getStatesSetFromIAccessibleStates(IAccessibleStates: int) -> Set[controlTypes.State]: return set( IAccessibleStatesToNVDAStates[IAState] for IAState in IAccessibleStatesToNVDAStates.keys() @@ -270,7 +269,7 @@ def getStatesSetFromIAccessibleAttrs(attrs: "textInfos.ControlField") -> Set[Sta # The value for the state is used in the attribute name. # The attribute value is always 1. # EG IAccessible::state_40="1" - IAccessibleStateAttrName = 'IAccessible::state_{}' + IAccessibleStateAttrName = "IAccessible::state_{}" return set( IAccessibleStatesToNVDAStates[IAState] for IAState in IAccessibleStatesToNVDAStates.keys() @@ -283,7 +282,7 @@ def getStatesSetFromIAccessible2Attrs(attrs: "textInfos.ControlField") -> Set[St # The value for the state is used in the attribute name. # The attribute value is always 1. # EG IAccessible2::state_40="1" - IAccessible2StateAttrName = 'IAccessible2::state_{}' + IAccessible2StateAttrName = "IAccessible2::state_{}" return set( IAccessible2StatesToNVDAStates[IA2State] for IA2State in IAccessible2StatesToNVDAStates.keys() @@ -292,8 +291,7 @@ def getStatesSetFromIAccessible2Attrs(attrs: "textInfos.ControlField") -> Set[St def calculateNvdaRole(IARole: int, IAStates: int) -> Role: - """Convert IARole value into an NVDA role, and apply any required transformations. - """ + """Convert IARole value into an NVDA role, and apply any required transformations.""" role = IAccessibleRolesToNVDARoles.get(IARole, Role.UNKNOWN) states = _getStatesSetFromIAccessibleStates(IAStates) role, states = controlTypes.transformRoleStates(role, states) @@ -301,8 +299,7 @@ def calculateNvdaRole(IARole: int, IAStates: int) -> Role: def calculateNvdaStates(IARole: int, IAStates: int) -> Set[State]: - """Convert IAStates bit set into a Set of NVDA States and apply any required transformations. - """ + """Convert IAStates bit set into a Set of NVDA States and apply any required transformations.""" role = IAccessibleRolesToNVDARoles.get(IARole, Role.UNKNOWN) states = _getStatesSetFromIAccessibleStates(IAStates) role, states = controlTypes.transformRoleStates(role, states) @@ -321,8 +318,7 @@ def NVDARoleFromAttr(accRole: Optional[str]) -> Role: def normalizeIAccessible( - pacc: Union[IUnknown, IA.IAccessible, IA2.IAccessible2], - childID: int = 0 + pacc: Union[IUnknown, IA.IAccessible, IA2.IAccessible2], childID: int = 0 ) -> Union[IA.IAccessible, IA2.IAccessible2]: if not isinstance(pacc, IA.IAccessible): try: @@ -515,11 +511,7 @@ def accNavigate(pacc, childID, direction): # Note: when working on winEventToNVDAEvent, look for opportunities to simplify # and move logic out into smaller helper functions. def winEventToNVDAEvent( # noqa: C901 - eventID: int, - window: int, - objectID: int, - childID: int, - useCache: bool = True + eventID: int, window: int, objectID: int, childID: int, useCache: bool = True ) -> Optional[Tuple[str, NVDAObjects.IAccessible.IAccessible]]: """Tries to convert a win event ID to an NVDA event name, and instantiate or fetch an NVDAObject for the win event parameters. @@ -586,8 +578,8 @@ def winEventToNVDAEvent( # noqa: C901 # SDM MSAA objects sometimes don't contain enough information to be useful Sometimes there is a real # window that does, so try to get the SDMChild property on the NVDAObject, and if successull use that as # obj instead. - if 'bosa_sdm' in obj.windowClassName: - SDMChild = getattr(obj, 'SDMChild', None) + if "bosa_sdm" in obj.windowClassName: + SDMChild = getattr(obj, "SDMChild", None) if SDMChild: obj = SDMChild if isMSAADebugLoggingEnabled(): @@ -614,16 +606,14 @@ def processGenericWinEvent(eventID, window, objectID, childID): @rtype: boolean """ if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing generic winEvent: {getWinEventLogInfo(window, objectID, childID, eventID)}" - ) + log.debug(f"Processing generic winEvent: {getWinEventLogInfo(window, objectID, childID, eventID)}") # Notify appModuleHandler of this new window appModuleHandler.update(winUser.getWindowThreadProcessID(window)[0]) # Handle particular events for the special MSAA caret object just as if they were for the focus object focus = eventHandler.lastQueuedFocusObject if objectID == winUser.OBJID_CARET and eventID in ( winUser.EVENT_OBJECT_LOCATIONCHANGE, - winUser.EVENT_OBJECT_SHOW + winUser.EVENT_OBJECT_SHOW, ): if not isinstance(focus, NVDAObjects.IAccessible.IAccessible): # #12855: Ignore MSAA caret event on non-MSAA focus. @@ -633,6 +623,7 @@ def processGenericWinEvent(eventID, window, objectID, childID): # Seem to rely on MSAA caret events, # as they do not fire their own UIA caret events. from NVDAObjects.UIA.wordDocument import WordDocument + if isinstance(focus, WordDocument): if isMSAADebugLoggingEnabled(): log.debug( @@ -658,10 +649,7 @@ def processGenericWinEvent(eventID, window, objectID, childID): # if the winEvent is for the object with focus, # Ensure that that the event is send to the existing focus instance, # rather than a new instance of the object with focus. - if ( - NVDAEvent[1] is not focus - and NVDAEvent[1] == focus - ): + if NVDAEvent[1] is not focus and NVDAEvent[1] == focus: if isMSAADebugLoggingEnabled(): log.debug( f"Directing winEvent to existing focus object {focus}. " @@ -688,8 +676,7 @@ def processFocusWinEvent(window, objectID, childID, force=False): """ if isMSAADebugLoggingEnabled(): log.debug( - f"Processing focus winEvent: {getWinEventLogInfo(window, objectID, childID)}, " - f"force {force}" + f"Processing focus winEvent: {getWinEventLogInfo(window, objectID, childID)}, " f"force {force}" ) windowClassName = winUser.getClassName(window) # Generally, we must ignore focus on child windows of SDM windows as we only want the SDM MSAA events. @@ -697,8 +684,8 @@ def processFocusWinEvent(window, objectID, childID, force=False): # as this is a child control and the SDM MSAA events don't handle child controls. if ( childID == 0 - and not windowClassName.startswith('bosa_sdm') - and winUser.getClassName(winUser.getAncestor(window, winUser.GA_PARENT)).startswith('bosa_sdm') + and not windowClassName.startswith("bosa_sdm") + and winUser.getClassName(winUser.getAncestor(window, winUser.GA_PARENT)).startswith("bosa_sdm") ): if isMSAADebugLoggingEnabled(): log.debug( @@ -726,9 +713,8 @@ def processFocusWinEvent(window, objectID, childID, force=False): if not NVDAEvent: return False eventName, obj = NVDAEvent - if ( - (childID == 0 and obj.IAccessibleRole == oleacc.ROLE_SYSTEM_LIST) - or (objectID == winUser.OBJID_CLIENT and "SysListView32" in obj.windowClassName) + if (childID == 0 and obj.IAccessibleRole == oleacc.ROLE_SYSTEM_LIST) or ( + objectID == winUser.OBJID_CLIENT and "SysListView32" in obj.windowClassName ): # Some controls incorrectly fire focus on child ID 0, even when there is a child with focus. try: @@ -741,7 +727,7 @@ def processFocusWinEvent(window, objectID, childID, force=False): IAccessibleChildID=realChildID, event_windowHandle=window, event_objectID=objectID, - event_childID=realChildID + event_childID=realChildID, ) if realObj: obj = realObj @@ -768,16 +754,15 @@ def processFocusNVDAEvent(obj, force=False): if isMSAADebugLoggingEnabled(): log.debug(f"IAccessible focus event not allowed by {obj}") return False - eventHandler.queueEvent('gainFocus', obj) + eventHandler.queueEvent("gainFocus", obj) return True def processDesktopSwitchWinEvent(window, objectID, childID): from winAPI.secureDesktop import _handleSecureDesktopChange + if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing desktopSwitch winEvent: {getWinEventLogInfo(window, objectID, childID)}" - ) + log.debug(f"Processing desktopSwitch winEvent: {getWinEventLogInfo(window, objectID, childID)}") hDesk = windll.user32.OpenInputDesktop(0, False, 0) if hDesk != 0: windll.user32.CloseDesktop(hDesk) @@ -796,6 +781,7 @@ def processDesktopSwitchWinEvent(window, objectID, childID): def _handleUserDesktop(): from winAPI.secureDesktop import post_secureDesktopStateChange + eventHandler.queueEvent("gainFocus", api.getDesktopObject().objectWithFocus()) post_secureDesktopStateChange.notify(isSecureDesktop=False) @@ -815,9 +801,7 @@ def processForegroundWinEvent(window, objectID, childID): @rtype: boolean """ if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing foreground winEvent: {getWinEventLogInfo(window, objectID, childID)}" - ) + log.debug(f"Processing foreground winEvent: {getWinEventLogInfo(window, objectID, childID)}") # Ignore foreground events on windows that aren't the current foreground window if window != winUser.getForegroundWindow(): if isMSAADebugLoggingEnabled(): @@ -829,9 +813,8 @@ def processForegroundWinEvent(window, objectID, childID): # If there is a pending gainFocus, it will handle the foreground object. oldFocus = eventHandler.lastQueuedFocusObject # If this foreground win event's window is an ancestor of the existing focus's window, then ignore it - if ( - isinstance(oldFocus, NVDAObjects.window.Window) - and winUser.isDescendantWindow(window, oldFocus.windowHandle) + if isinstance(oldFocus, NVDAObjects.window.Window) and winUser.isDescendantWindow( + window, oldFocus.windowHandle ): if isMSAADebugLoggingEnabled(): log.debug( @@ -864,7 +847,9 @@ def processForegroundWinEvent(window, objectID, childID): ) return True # Convert the win event to an NVDA event - NVDAEvent = winEventToNVDAEvent(winUser.EVENT_SYSTEM_FOREGROUND, window, objectID, childID, useCache=False) + NVDAEvent = winEventToNVDAEvent( + winUser.EVENT_SYSTEM_FOREGROUND, window, objectID, childID, useCache=False + ) if not NVDAEvent: if isMSAADebugLoggingEnabled(): log.debug( @@ -878,9 +863,7 @@ def processForegroundWinEvent(window, objectID, childID): def processShowWinEvent(window, objectID, childID): if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing show winEvent: {getWinEventLogInfo(window, objectID, childID)}" - ) + log.debug(f"Processing show winEvent: {getWinEventLogInfo(window, objectID, childID)}") # eventHandler.shouldAcceptEvent only accepts show events for a few specific cases. # Narrow this further to only accept events for clients or custom objects. if objectID == winUser.OBJID_CLIENT or objectID > 0: @@ -895,9 +878,7 @@ def processDestroyWinEvent(window, objectID, childID): such an object exists. """ if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing destroy winEvent: {getWinEventLogInfo(window, objectID, childID)}" - ) + log.debug(f"Processing destroy winEvent: {getWinEventLogInfo(window, objectID, childID)}") try: del liveNVDAObjectTable[(window, objectID, childID)] except KeyError: @@ -907,6 +888,7 @@ def processDestroyWinEvent(window, objectID, childID): # so can't use generic focus correction. (#2695) focus = api.getFocusObject() from NVDAObjects.IAccessible.mscandui import BaseCandidateItem + if ( objectID == 0 and childID == 0 @@ -930,9 +912,9 @@ def processMenuStartWinEvent(eventID, window, objectID, childID, validFocus): ) if validFocus: lastFocus = eventHandler.lastQueuedFocusObject - if ( - isinstance(lastFocus, NVDAObjects.IAccessible.IAccessible) - and lastFocus.IAccessibleRole in (oleacc.ROLE_SYSTEM_MENUPOPUP, oleacc.ROLE_SYSTEM_MENUITEM) + if isinstance(lastFocus, NVDAObjects.IAccessible.IAccessible) and lastFocus.IAccessibleRole in ( + oleacc.ROLE_SYSTEM_MENUPOPUP, + oleacc.ROLE_SYSTEM_MENUITEM, ): # Focus has already been set to a menu or menu item, so we don't need to handle the menuStart. return @@ -962,9 +944,7 @@ def processFakeFocusWinEvent(eventID, window, objectID, childID): # However, it is possible that the focus event has simply been delayed, so wait a bit and only do it if # the focus hasn't changed yet. if isMSAADebugLoggingEnabled(): - log.debug( - f"Processing fake focus winEvent {getWinEventLogInfo(window, objectID, childID)}" - ) + log.debug(f"Processing fake focus winEvent {getWinEventLogInfo(window, objectID, childID)}") core.callLater(50, _fakeFocus, api.getFocusObject()) @@ -976,9 +956,7 @@ def _fakeFocus(oldFocus): if not focus: return if isMSAADebugLoggingEnabled(): - log.debug( - f"Faking focus on {focus}" - ) + log.debug(f"Faking focus on {focus}") processFocusNVDAEvent(focus) @@ -1015,10 +993,9 @@ def pumpAll(): # noqa: C901 for winEvent in winEvents: isEventOnCaret = winEvent[2] == winUser.OBJID_CARET - showHideCaretEvent = focus and isEventOnCaret and winEvent[0] in [ - winUser.EVENT_OBJECT_SHOW, - winUser.EVENT_OBJECT_HIDE - ] + showHideCaretEvent = ( + focus and isEventOnCaret and winEvent[0] in [winUser.EVENT_OBJECT_SHOW, winUser.EVENT_OBJECT_HIDE] + ) # #4001: Ideally, we'd call shouldAcceptEvent in winEventCallback, but this causes focus issues when # starting applications. #7332: If this is a show event, which would normally be dropped by # `shouldAcceptEvent` and this event is for the caret, later it will be mapped to a caret event, @@ -1027,15 +1004,11 @@ def pumpAll(): # noqa: C901 if not focus.shouldAcceptShowHideCaretEvent: continue elif not eventHandler.shouldAcceptEvent( - internalWinEventHandler.winEventIDsToNVDAEventNames[winEvent[0]], - windowHandle=winEvent[1] + internalWinEventHandler.winEventIDsToNVDAEventNames[winEvent[0]], windowHandle=winEvent[1] ): continue # We want to only pass on one focus event to NVDA, but we always want to use the most recent possible one - if winEvent[0] in ( - winUser.EVENT_OBJECT_FOCUS, - winUser.EVENT_SYSTEM_FOREGROUND - ): + if winEvent[0] in (winUser.EVENT_OBJECT_FOCUS, winUser.EVENT_SYSTEM_FOREGROUND): focusWinEvents.append(winEvent) continue else: @@ -1065,10 +1038,7 @@ def pumpAll(): # noqa: C901 break if fakeFocusEvent: # Try this as a last resort. - if fakeFocusEvent[0] in ( - winUser.EVENT_SYSTEM_MENUSTART, - winUser.EVENT_SYSTEM_MENUPOPUPSTART - ): + if fakeFocusEvent[0] in (winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUPOPUPSTART): # menuStart needs to be handled specially and might act even if there was a valid focus event. processMenuStartWinEvent(*fakeFocusEvent, validFocus=validFocus) elif not validFocus: @@ -1094,14 +1064,14 @@ def getIAccIdentity(pacc, childID): # comtypes transparently does this for wireHWND. return dict(menuHandle=cast(hmenu, wintypes.HMENU).value, childID=childID) stringPtr = cast(stringPtr, POINTER(c_char * stringSize)) - fields = struct.unpack('IIiI', stringPtr.contents.raw) + fields = struct.unpack("IIiI", stringPtr.contents.raw) d = {} - d['childID'] = fields[3] + d["childID"] = fields[3] if fields[0] & 2: - d['menuHandle'] = fields[2] + d["menuHandle"] = fields[2] else: - d['objectID'] = fields[2] - d['windowHandle'] = fields[1] + d["objectID"] = fields[2] + d["windowHandle"] = fields[1] return d finally: windll.ole32.CoTaskMemFree(stringPtr) @@ -1185,19 +1155,17 @@ def getRecursiveTextFromIAccessibleTextObject(obj, startOffset=0, endOffset=-1): except: # noqa: E722 Bare except pass textList.append(t) - return "".join(textList).replace(' ', ' ') + return "".join(textList).replace(" ", " ") -ATTRIBS_STRING_BASE64_PATTERN = re.compile( - r"(([^\\](\\\\)*);src:data\\:[^\\;]+\\;base64\\,)[A-Za-z0-9+/=]+" -) +ATTRIBS_STRING_BASE64_PATTERN = re.compile(r"(([^\\](\\\\)*);src:data\\:[^\\;]+\\;base64\\,)[A-Za-z0-9+/=]+") ATTRIBS_STRING_BASE64_REPL = r"\1" ATTRIBS_STRING_BASE64_THRESHOLD = 4096 # C901: splitIA2Attribs is too complex def splitIA2Attribs( # noqa: C901 - attribsString: str + attribsString: str, ) -> Dict[str, Union[str, Dict]]: """Split an IAccessible2 attributes string into a dict of attribute keys and values. An invalid attributes string does not cause an error, but strange results may be returned. @@ -1278,9 +1246,12 @@ def isMarshalledIAccessible(IAccessibleObject): if not isinstance(IAccessibleObject, IA.IAccessible): raise TypeError("object should be of type IAccessible, not %s" % IAccessibleObject) buf = create_unicode_buffer(1024) - addr = POINTER(c_void_p).from_address( - super(comtypes._compointer_base, IAccessibleObject).value).contents.value + addr = ( + POINTER(c_void_p) + .from_address(super(comtypes._compointer_base, IAccessibleObject).value) + .contents.value + ) handle = HANDLE() windll.kernel32.GetModuleHandleExW(6, addr, byref(handle)) windll.kernel32.GetModuleFileNameW(handle, buf, 1024) - return not buf.value.lower().endswith('oleacc.dll') + return not buf.value.lower().endswith("oleacc.dll") diff --git a/source/IAccessibleHandler/internalWinEventHandler.py b/source/IAccessibleHandler/internalWinEventHandler.py index 6534b5e49a0..9c2e20ba95c 100644 --- a/source/IAccessibleHandler/internalWinEventHandler.py +++ b/source/IAccessibleHandler/internalWinEventHandler.py @@ -101,7 +101,9 @@ def winEventCallback(handle, eventID, window, objectID, childID, threadID, times # Ignore events with invalid window handles isWindow = winUser.isWindow(window) if window else 0 if window == 0 or ( - not isWindow and eventID in ( + not isWindow + and eventID + in ( winUser.EVENT_SYSTEM_SWITCHSTART, winUser.EVENT_SYSTEM_SWITCHEND, winUser.EVENT_SYSTEM_MENUEND, @@ -189,11 +191,14 @@ def winEventCallback(handle, eventID, window, objectID, childID, threadID, times def initialize( - processDestroyWinEventFunc: Callable[[ + processDestroyWinEventFunc: Callable[ + [ c_int, # window c_int, # objectID c_int, # childID - ], None] + ], + None, + ], ): global _processDestroyWinEvent _processDestroyWinEvent = processDestroyWinEventFunc @@ -222,10 +227,7 @@ def _shouldGetEvents(): curForegroundWindow = winUser.getForegroundWindow() curForegroundClassName = winUser.getClassName(curForegroundWindow) futureForegroundClassName = winUser.getClassName(_deferUntilForegroundWindow) - if ( - _foregroundDefers < MAX_FOREGROUND_DEFERS - and curForegroundWindow != _deferUntilForegroundWindow - ): + if _foregroundDefers < MAX_FOREGROUND_DEFERS and curForegroundWindow != _deferUntilForegroundWindow: # Wait a core cycle before handling events to give the foreground window time to update. core.requestPump() _foregroundDefers += 1 diff --git a/source/IAccessibleHandler/orderedWinEventLimiter.py b/source/IAccessibleHandler/orderedWinEventLimiter.py index 54a1ca73e4f..72340558248 100644 --- a/source/IAccessibleHandler/orderedWinEventLimiter.py +++ b/source/IAccessibleHandler/orderedWinEventLimiter.py @@ -14,7 +14,7 @@ winUser.EVENT_SYSTEM_MENUSTART, winUser.EVENT_SYSTEM_MENUEND, winUser.EVENT_SYSTEM_MENUPOPUPSTART, - winUser.EVENT_SYSTEM_MENUPOPUPEND + winUser.EVENT_SYSTEM_MENUPOPUPEND, ) @@ -41,14 +41,7 @@ def __init__(self, maxFocusItems=4): self._eventCounter = itertools.count() self._lastMenuEvent = None - def addEvent( - self, - eventID: int, - window: int, - objectID: int, - childID: int, - threadID: int - ) -> bool: + def addEvent(self, eventID: int, window: int, objectID: int, childID: int, threadID: int) -> bool: """Adds a winEvent to the limiter. @param eventID: the winEvent type @param window: the window handle of the winEvent @@ -81,8 +74,7 @@ def addEvent( return True def flushEvents( - self, - alwaysAllowedObjects: Optional[List[IAccessibleObjectIdentifierType]] = None + self, alwaysAllowedObjects: Optional[List[IAccessibleObjectIdentifierType]] = None ) -> List: """Returns a list of winEvents that have been added. Due to limiting, it will not necessarily be all the winEvents that were originally added. @@ -113,7 +105,7 @@ def flushEvents( heapq.heappush(self._eventHeap, (v,) + k) f = self._focusEventCache self._focusEventCache = {} - for k, v in sorted(f.items(), key=lambda item: item[1])[0 - self.maxFocusItems:]: + for k, v in sorted(f.items(), key=lambda item: item[1])[0 - self.maxFocusItems :]: heapq.heappush(self._eventHeap, (v,) + k) e = self._eventHeap self._eventHeap = [] diff --git a/source/IAccessibleHandler/types.py b/source/IAccessibleHandler/types.py index c7c3e5f3428..2d9e81a39ba 100644 --- a/source/IAccessibleHandler/types.py +++ b/source/IAccessibleHandler/types.py @@ -7,6 +7,7 @@ """Types used in IAccessibleHander. Kept here so they can be re-used without having to worry about circular imports. """ + import enum from typing import Tuple diff --git a/source/IAccessibleHandler/utils.py b/source/IAccessibleHandler/utils.py index 2ede0b86f14..70e375b7f59 100644 --- a/source/IAccessibleHandler/utils.py +++ b/source/IAccessibleHandler/utils.py @@ -18,11 +18,11 @@ def getWinEventName(eventID): - """ Looks up the name of an EVENT_* winEvent constant. """ + """Looks up the name of an EVENT_* winEvent constant.""" global _winEventNameCache if not _winEventNameCache: - _winEventNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('EVENT_')} - _winEventNameCache.update({y: x for x, y in vars(IA2).items() if x.startswith('IA2_EVENT_')}) + _winEventNameCache = {y: x for x, y in vars(winUser).items() if x.startswith("EVENT_")} + _winEventNameCache.update({y: x for x, y in vars(IA2).items() if x.startswith("IA2_EVENT_")}) name = _winEventNameCache.get(eventID) if not name: name = "unknown event ({eventID})" @@ -33,10 +33,10 @@ def getWinEventName(eventID): def getObjectIDName(objectID): - """ Looks up the name of an OBJID_* winEvent constant. """ + """Looks up the name of an OBJID_* winEvent constant.""" global _objectIDNameCache if not _objectIDNameCache: - _objectIDNameCache = {y: x for x, y in vars(winUser).items() if x.startswith('OBJID_')} + _objectIDNameCache = {y: x for x, y in vars(winUser).items() if x.startswith("OBJID_")} name = _objectIDNameCache.get(objectID) if not name: name = str(objectID) @@ -70,5 +70,5 @@ def getWinEventLogInfo(window, objectID, childID, eventID=None, threadID=None): def isMSAADebugLoggingEnabled(): - """ Whether the user has configured NVDA to log extra information about MSAA events. """ + """Whether the user has configured NVDA to log extra information about MSAA events.""" return config.conf["debugLog"]["MSAA"] diff --git a/source/JABHandler.py b/source/JABHandler.py index 2d662d22879..dc0fa01bb87 100644 --- a/source/JABHandler.py +++ b/source/JABHandler.py @@ -26,7 +26,7 @@ CFUNCTYPE, WinError, create_string_buffer, - create_unicode_buffer + create_unicode_buffer, ) from ctypes.wintypes import BOOL, HWND, WCHAR import time @@ -48,130 +48,142 @@ A11Y_PROPS_PATH = os.path.expanduser(r"~\.accessibility.properties") #: The content of ".accessibility.properties" when JAB is enabled. A11Y_PROPS_CONTENT = ( - "assistive_technologies=com.sun.java.accessibility.AccessBridge\n" - "screen_magnifier_present=true\n" + "assistive_technologies=com.sun.java.accessibility.AccessBridge\n" "screen_magnifier_present=true\n" ) -#Some utility functions to help with function defines +# Some utility functions to help with function defines + def _errcheck(res, func, args): if not res: raise RuntimeError("Result %s" % res) return res -def _fixBridgeFunc(restype,name,*argtypes,**kwargs): + +def _fixBridgeFunc(restype, name, *argtypes, **kwargs): try: - func=getattr(bridgeDll,name) + func = getattr(bridgeDll, name) except AttributeError: - log.warning("%s not found in Java Access Bridge dll"%name) + log.warning("%s not found in Java Access Bridge dll" % name) return - func.restype=restype - func.argtypes=argtypes - if kwargs.get('errcheck'): - func.errcheck=_errcheck + func.restype = restype + func.argtypes = argtypes + if kwargs.get("errcheck"): + func.errcheck = _errcheck bridgeDll = None -#Definitions of access bridge types, structs and prototypes +# Definitions of access bridge types, structs and prototypes -jchar=c_wchar -jint=c_int -jfloat=c_float -jboolean=c_bool +jchar = c_wchar +jint = c_int +jfloat = c_float +jboolean = c_bool class JOBJECT64(c_int64): pass -AccessibleTable=JOBJECT64 -MAX_STRING_SIZE=1024 -SHORT_STRING_SIZE=256 + +AccessibleTable = JOBJECT64 + +MAX_STRING_SIZE = 1024 +SHORT_STRING_SIZE = 256 + class AccessBridgeVersionInfo(Structure): - _fields_=[ - ('VMVersion',WCHAR*SHORT_STRING_SIZE), - ('bridgeJavaClassVersion',WCHAR*SHORT_STRING_SIZE), - ('bridgeJavaDLLVersion',WCHAR*SHORT_STRING_SIZE), - ('bridgeWinDLLVersion',WCHAR*SHORT_STRING_SIZE), + _fields_ = [ + ("VMVersion", WCHAR * SHORT_STRING_SIZE), + ("bridgeJavaClassVersion", WCHAR * SHORT_STRING_SIZE), + ("bridgeJavaDLLVersion", WCHAR * SHORT_STRING_SIZE), + ("bridgeWinDLLVersion", WCHAR * SHORT_STRING_SIZE), ] + class AccessibleContextInfo(Structure): - _fields_=[ - ('name',WCHAR*MAX_STRING_SIZE), - ('description',WCHAR*MAX_STRING_SIZE), - ('role',WCHAR*SHORT_STRING_SIZE), - ('role_en_US',WCHAR*SHORT_STRING_SIZE), - ('states',WCHAR*SHORT_STRING_SIZE), - ('states_en_US',WCHAR*SHORT_STRING_SIZE), - ('indexInParent',jint), - ('childrenCount',jint), - ('x',jint), - ('y',jint), - ('width',jint), - ('height',jint), - ('accessibleComponent',BOOL), - ('accessibleAction',BOOL), - ('accessibleSelection',BOOL), - ('accessibleText',BOOL), - ('accessibleValue',BOOL), + _fields_ = [ + ("name", WCHAR * MAX_STRING_SIZE), + ("description", WCHAR * MAX_STRING_SIZE), + ("role", WCHAR * SHORT_STRING_SIZE), + ("role_en_US", WCHAR * SHORT_STRING_SIZE), + ("states", WCHAR * SHORT_STRING_SIZE), + ("states_en_US", WCHAR * SHORT_STRING_SIZE), + ("indexInParent", jint), + ("childrenCount", jint), + ("x", jint), + ("y", jint), + ("width", jint), + ("height", jint), + ("accessibleComponent", BOOL), + ("accessibleAction", BOOL), + ("accessibleSelection", BOOL), + ("accessibleText", BOOL), + ("accessibleValue", BOOL), ] + class AccessibleTextInfo(Structure): - _fields_=[ - ('charCount',jint), - ('caretIndex',jint), - ('indexAtPoint',jint), + _fields_ = [ + ("charCount", jint), + ("caretIndex", jint), + ("indexAtPoint", jint), ] + class AccessibleTextItemsInfo(Structure): - _fields_=[ - ('letter',WCHAR), - ('word',WCHAR*SHORT_STRING_SIZE), - ('sentence',WCHAR*MAX_STRING_SIZE), + _fields_ = [ + ("letter", WCHAR), + ("word", WCHAR * SHORT_STRING_SIZE), + ("sentence", WCHAR * MAX_STRING_SIZE), ] + class AccessibleTextSelectionInfo(Structure): - _fields_=[ - ('selectionStartIndex',jint), - ('selectionEndIndex',jint), - ('selectedText',WCHAR*MAX_STRING_SIZE), + _fields_ = [ + ("selectionStartIndex", jint), + ("selectionEndIndex", jint), + ("selectedText", WCHAR * MAX_STRING_SIZE), ] + class AccessibleTextRectInfo(Structure): - _fields_=[ - ('x',jint), - ('y',jint), - ('width',jint), - ('height',jint), + _fields_ = [ + ("x", jint), + ("y", jint), + ("width", jint), + ("height", jint), ] + class AccessibleTextAttributesInfo(Structure): - _fields_=[ - ('bold',BOOL), - ('italic',BOOL), - ('underline',BOOL), - ('strikethrough',BOOL), - ('superscript',BOOL), - ('subscript',BOOL), - ('backgroundColor',WCHAR*SHORT_STRING_SIZE), - ('foregroundColor',WCHAR*SHORT_STRING_SIZE), - ('fontFamily',WCHAR*SHORT_STRING_SIZE), - ('fontSize',jint), - ('alignment',jint), - ('bidiLevel',jint), - ('firstLineIndent',jfloat), - ('LeftIndent',jfloat), - ('rightIndent',jfloat), - ('lineSpacing',jfloat), - ('spaceAbove',jfloat), - ('spaceBelow',jfloat), - ('fullAttributesString',WCHAR*MAX_STRING_SIZE), + _fields_ = [ + ("bold", BOOL), + ("italic", BOOL), + ("underline", BOOL), + ("strikethrough", BOOL), + ("superscript", BOOL), + ("subscript", BOOL), + ("backgroundColor", WCHAR * SHORT_STRING_SIZE), + ("foregroundColor", WCHAR * SHORT_STRING_SIZE), + ("fontFamily", WCHAR * SHORT_STRING_SIZE), + ("fontSize", jint), + ("alignment", jint), + ("bidiLevel", jint), + ("firstLineIndent", jfloat), + ("LeftIndent", jfloat), + ("rightIndent", jfloat), + ("lineSpacing", jfloat), + ("spaceAbove", jfloat), + ("spaceBelow", jfloat), + ("fullAttributesString", WCHAR * MAX_STRING_SIZE), ] + MAX_RELATION_TARGETS = 25 MAX_RELATIONS = 5 + class AccessibleRelationInfo(Structure): _fields_ = [ ("key", WCHAR * SHORT_STRING_SIZE), @@ -179,19 +191,21 @@ class AccessibleRelationInfo(Structure): ("targets", JOBJECT64 * MAX_RELATION_TARGETS), ] + class AccessibleRelationSetInfo(Structure): _fields_ = [ ("relationCount", jint), ("relations", AccessibleRelationInfo * MAX_RELATIONS), ] + MAX_ACTION_INFO = 256 MAX_ACTIONS_TO_DO = 32 + class AccessibleActionInfo(Structure): - _fields_ = ( - ("name", c_wchar * SHORT_STRING_SIZE), - ) + _fields_ = (("name", c_wchar * SHORT_STRING_SIZE),) + class AccessibleActions(Structure): _fields_ = ( @@ -199,40 +213,45 @@ class AccessibleActions(Structure): ("actionInfo", AccessibleActionInfo * MAX_ACTION_INFO), ) + class AccessibleActionsToDo(Structure): _fields_ = ( ("actionsCount", jint), ("actions", AccessibleActionInfo * MAX_ACTIONS_TO_DO), ) + class AccessibleTableInfo(Structure): - _fields_=[ - ('caption',JOBJECT64), - ('summary',JOBJECT64), - ('rowCount',jint), - ('columnCount',jint), - ('accessibleContext',JOBJECT64), - ('accessibleTable',JOBJECT64), + _fields_ = [ + ("caption", JOBJECT64), + ("summary", JOBJECT64), + ("rowCount", jint), + ("columnCount", jint), + ("accessibleContext", JOBJECT64), + ("accessibleTable", JOBJECT64), ] + class AccessibleTableCellInfo(Structure): - _fields_=[ - ('accessibleContext',JOBJECT64), - ('index',jint), - ('row',jint), - ('column',jint), - ('rowExtent',jint), - ('columnExtent',jint), - ('isSelected',jboolean), + _fields_ = [ + ("accessibleContext", JOBJECT64), + ("index", jint), + ("row", jint), + ("column", jint), + ("rowExtent", jint), + ("columnExtent", jint), + ("isSelected", jboolean), ] -MAX_KEY_BINDINGS=50 + +MAX_KEY_BINDINGS = 50 class AccessibleKeystroke(IntFlag): """ Defined in the JDK in header include/win32/bridge/AccessBridgePackages.h """ + SHIFT = 1 CONTROL = 2 META = 4 @@ -266,6 +285,7 @@ class AccessibleVK(IntEnum): The supported control code keys related to AccessibleKeystroke.CONTROLCODE. Defined in the JDK in header include/win32/bridge/AccessBridgePackages.h """ + BACK_SPACE = 8 DELETE = 127 DOWN = 40 @@ -284,149 +304,243 @@ class AccessibleVK(IntEnum): class AccessibleKeyBindingInfo(Structure): - _fields_=[ - ('character',jchar), - ('modifiers',jint), + _fields_ = [ + ("character", jchar), + ("modifiers", jint), ] + class AccessibleKeyBindings(Structure): - _fields_=[ - ('keyBindingsCount',c_int), - ('keyBindingInfo',AccessibleKeyBindingInfo*MAX_KEY_BINDINGS), + _fields_ = [ + ("keyBindingsCount", c_int), + ("keyBindingInfo", AccessibleKeyBindingInfo * MAX_KEY_BINDINGS), ] -AccessBridge_FocusGainedFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64) -AccessBridge_PropertyNameChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_wchar_p,c_wchar_p) -AccessBridge_PropertyDescriptionChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_wchar_p,c_wchar_p) -AccessBridge_PropertyValueChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_wchar_p,c_wchar_p) -AccessBridge_PropertyStateChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_wchar_p,c_wchar_p) -AccessBridge_PropertyCaretChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_int,c_int) -AccessBridge_PropertyActiveDescendentChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,JOBJECT64,JOBJECT64) + +AccessBridge_FocusGainedFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64) +AccessBridge_PropertyNameChangeFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64, c_wchar_p, c_wchar_p) +AccessBridge_PropertyDescriptionChangeFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64, c_wchar_p, c_wchar_p) +AccessBridge_PropertyValueChangeFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64, c_wchar_p, c_wchar_p) +AccessBridge_PropertyStateChangeFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64, c_wchar_p, c_wchar_p) +AccessBridge_PropertyCaretChangeFP = CFUNCTYPE(None, c_long, JOBJECT64, JOBJECT64, c_int, c_int) +AccessBridge_PropertyActiveDescendentChangeFP = CFUNCTYPE( + None, c_long, JOBJECT64, JOBJECT64, JOBJECT64, JOBJECT64 +) def _fixBridgeFuncs(): - """Appropriately set the return and argument types of all the access bridge dll functions - """ - _fixBridgeFunc(None,'Windows_run') - _fixBridgeFunc(None,'setFocusGainedFP',c_void_p) - _fixBridgeFunc(None,'setPropertyNameChangeFP',c_void_p) - _fixBridgeFunc(None,'setPropertyDescriptionChangeFP',c_void_p) - _fixBridgeFunc(None,'setPropertyValueChangeFP',c_void_p) - _fixBridgeFunc(None,'setPropertyStateChangeFP',c_void_p) - _fixBridgeFunc(None,'setPropertyCaretChangeFP',c_void_p) - _fixBridgeFunc(None,'setPropertyActiveDescendentChangeFP',c_void_p) - _fixBridgeFunc(None,'releaseJavaObject',c_long,JOBJECT64) - _fixBridgeFunc(BOOL,'getVersionInfo',POINTER(AccessBridgeVersionInfo),errcheck=True) - _fixBridgeFunc(BOOL,'isJavaWindow',HWND) - _fixBridgeFunc(BOOL,'isSameObject',c_long,JOBJECT64,JOBJECT64) - _fixBridgeFunc(BOOL,'getAccessibleContextFromHWND',HWND,POINTER(c_long),POINTER(JOBJECT64),errcheck=True) - _fixBridgeFunc(HWND,'getHWNDFromAccessibleContext',c_long,JOBJECT64,errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleContextAt',c_long,JOBJECT64,jint,jint,POINTER(JOBJECT64),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleContextWithFocus',HWND,POINTER(c_long),POINTER(JOBJECT64),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleContextInfo',c_long,JOBJECT64,POINTER(AccessibleContextInfo),errcheck=True) - _fixBridgeFunc(JOBJECT64,'getAccessibleChildFromContext',c_long,JOBJECT64,jint,errcheck=True) - _fixBridgeFunc(JOBJECT64,'getAccessibleParentFromContext',c_long,JOBJECT64) - _fixBridgeFunc(JOBJECT64,'getParentWithRole',c_long,JOBJECT64,POINTER(c_wchar)) - _fixBridgeFunc(BOOL,'getAccessibleRelationSet',c_long,JOBJECT64,POINTER(AccessibleRelationSetInfo),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTextInfo',c_long,JOBJECT64,POINTER(AccessibleTextInfo),jint,jint,errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTextItems',c_long,JOBJECT64,POINTER(AccessibleTextItemsInfo),jint,errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTextSelectionInfo',c_long,JOBJECT64,POINTER(AccessibleTextSelectionInfo),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTextAttributes',c_long,JOBJECT64,jint,POINTER(AccessibleTextAttributesInfo),errcheck=True) + """Appropriately set the return and argument types of all the access bridge dll functions""" + _fixBridgeFunc(None, "Windows_run") + _fixBridgeFunc(None, "setFocusGainedFP", c_void_p) + _fixBridgeFunc(None, "setPropertyNameChangeFP", c_void_p) + _fixBridgeFunc(None, "setPropertyDescriptionChangeFP", c_void_p) + _fixBridgeFunc(None, "setPropertyValueChangeFP", c_void_p) + _fixBridgeFunc(None, "setPropertyStateChangeFP", c_void_p) + _fixBridgeFunc(None, "setPropertyCaretChangeFP", c_void_p) + _fixBridgeFunc(None, "setPropertyActiveDescendentChangeFP", c_void_p) + _fixBridgeFunc(None, "releaseJavaObject", c_long, JOBJECT64) + _fixBridgeFunc(BOOL, "getVersionInfo", POINTER(AccessBridgeVersionInfo), errcheck=True) + _fixBridgeFunc(BOOL, "isJavaWindow", HWND) + _fixBridgeFunc(BOOL, "isSameObject", c_long, JOBJECT64, JOBJECT64) + _fixBridgeFunc( + BOOL, "getAccessibleContextFromHWND", HWND, POINTER(c_long), POINTER(JOBJECT64), errcheck=True + ) + _fixBridgeFunc(HWND, "getHWNDFromAccessibleContext", c_long, JOBJECT64, errcheck=True) + _fixBridgeFunc( + BOOL, "getAccessibleContextAt", c_long, JOBJECT64, jint, jint, POINTER(JOBJECT64), errcheck=True + ) + _fixBridgeFunc( + BOOL, "getAccessibleContextWithFocus", HWND, POINTER(c_long), POINTER(JOBJECT64), errcheck=True + ) + _fixBridgeFunc( + BOOL, "getAccessibleContextInfo", c_long, JOBJECT64, POINTER(AccessibleContextInfo), errcheck=True + ) + _fixBridgeFunc(JOBJECT64, "getAccessibleChildFromContext", c_long, JOBJECT64, jint, errcheck=True) + _fixBridgeFunc(JOBJECT64, "getAccessibleParentFromContext", c_long, JOBJECT64) + _fixBridgeFunc(JOBJECT64, "getParentWithRole", c_long, JOBJECT64, POINTER(c_wchar)) + _fixBridgeFunc( + BOOL, "getAccessibleRelationSet", c_long, JOBJECT64, POINTER(AccessibleRelationSetInfo), errcheck=True + ) + _fixBridgeFunc( + BOOL, + "getAccessibleTextInfo", + c_long, + JOBJECT64, + POINTER(AccessibleTextInfo), + jint, + jint, + errcheck=True, + ) + _fixBridgeFunc( + BOOL, + "getAccessibleTextItems", + c_long, + JOBJECT64, + POINTER(AccessibleTextItemsInfo), + jint, + errcheck=True, + ) + _fixBridgeFunc( + BOOL, + "getAccessibleTextSelectionInfo", + c_long, + JOBJECT64, + POINTER(AccessibleTextSelectionInfo), + errcheck=True, + ) _fixBridgeFunc( BOOL, - 'getAccessibleTextRect', + "getAccessibleTextAttributes", c_long, JOBJECT64, - POINTER(AccessibleTextRectInfo), jint, - errcheck=True + POINTER(AccessibleTextAttributesInfo), + errcheck=True, + ) + _fixBridgeFunc( + BOOL, "getAccessibleTextRect", c_long, JOBJECT64, POINTER(AccessibleTextRectInfo), jint, errcheck=True + ) + _fixBridgeFunc( + BOOL, + "getAccessibleTextLineBounds", + c_long, + JOBJECT64, + jint, + POINTER(jint), + POINTER(jint), + errcheck=True, + ) + _fixBridgeFunc( + BOOL, "getAccessibleTextRange", c_long, JOBJECT64, jint, jint, POINTER(c_char), c_short, errcheck=True + ) + _fixBridgeFunc( + BOOL, + "getCurrentAccessibleValueFromContext", + c_long, + JOBJECT64, + POINTER(c_wchar), + c_short, + errcheck=True, + ) + _fixBridgeFunc(BOOL, "selectTextRange", c_long, JOBJECT64, c_int, c_int, errcheck=True) + _fixBridgeFunc( + BOOL, + "getTextAttributesInRange", + c_long, + JOBJECT64, + c_int, + c_int, + POINTER(AccessibleTextAttributesInfo), + POINTER(c_short), + errcheck=True, + ) + _fixBridgeFunc(JOBJECT64, "getTopLevelObject", c_long, JOBJECT64, errcheck=True) + _fixBridgeFunc(c_int, "getObjectDepth", c_long, JOBJECT64) + _fixBridgeFunc(JOBJECT64, "getActiveDescendent", c_long, JOBJECT64) + _fixBridgeFunc(BOOL, "requestFocus", c_long, JOBJECT64, errcheck=True) + _fixBridgeFunc(BOOL, "setCaretPosition", c_long, JOBJECT64, c_int, errcheck=True) + _fixBridgeFunc( + BOOL, "getCaretLocation", c_long, JOBJECT64, POINTER(AccessibleTextRectInfo), jint, errcheck=True + ) + _fixBridgeFunc(BOOL, "getAccessibleActions", c_long, JOBJECT64, POINTER(AccessibleActions), errcheck=True) + _fixBridgeFunc( + BOOL, + "doAccessibleActions", + c_long, + JOBJECT64, + POINTER(AccessibleActionsToDo), + POINTER(jint), + errcheck=True, + ) + _fixBridgeFunc(BOOL, "getAccessibleTableInfo", c_long, JOBJECT64, POINTER(AccessibleTableInfo)) + _fixBridgeFunc( + BOOL, + "getAccessibleTableCellInfo", + c_long, + AccessibleTable, + jint, + jint, + POINTER(AccessibleTableCellInfo), + errcheck=True, ) - _fixBridgeFunc(BOOL,'getAccessibleTextLineBounds',c_long,JOBJECT64,jint,POINTER(jint),POINTER(jint),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTextRange',c_long,JOBJECT64,jint,jint,POINTER(c_char),c_short,errcheck=True) - _fixBridgeFunc(BOOL,'getCurrentAccessibleValueFromContext',c_long,JOBJECT64,POINTER(c_wchar),c_short,errcheck=True) - _fixBridgeFunc(BOOL,'selectTextRange',c_long,JOBJECT64,c_int,c_int,errcheck=True) - _fixBridgeFunc(BOOL,'getTextAttributesInRange',c_long,JOBJECT64,c_int,c_int,POINTER(AccessibleTextAttributesInfo),POINTER(c_short),errcheck=True) - _fixBridgeFunc(JOBJECT64,'getTopLevelObject',c_long,JOBJECT64,errcheck=True) - _fixBridgeFunc(c_int,'getObjectDepth',c_long,JOBJECT64) - _fixBridgeFunc(JOBJECT64,'getActiveDescendent',c_long,JOBJECT64) - _fixBridgeFunc(BOOL,'requestFocus',c_long,JOBJECT64,errcheck=True) - _fixBridgeFunc(BOOL,'setCaretPosition',c_long,JOBJECT64,c_int,errcheck=True) - _fixBridgeFunc(BOOL,'getCaretLocation',c_long,JOBJECT64,POINTER(AccessibleTextRectInfo),jint,errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleActions',c_long,JOBJECT64,POINTER(AccessibleActions),errcheck=True) - _fixBridgeFunc(BOOL,'doAccessibleActions',c_long,JOBJECT64,POINTER(AccessibleActionsToDo),POINTER(jint),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTableInfo',c_long,JOBJECT64,POINTER(AccessibleTableInfo)) - _fixBridgeFunc(BOOL,'getAccessibleTableCellInfo',c_long,AccessibleTable,jint,jint,POINTER(AccessibleTableCellInfo),errcheck=True) - _fixBridgeFunc(BOOL,'getAccessibleTableRowHeader',c_long,JOBJECT64,POINTER(AccessibleTableInfo)) - _fixBridgeFunc(BOOL,'getAccessibleTableColumnHeader',c_long,JOBJECT64,POINTER(AccessibleTableInfo)) - _fixBridgeFunc(JOBJECT64,'getAccessibleTableRowDescription',c_long,JOBJECT64,jint) - _fixBridgeFunc(JOBJECT64,'getAccessibleTableColumnDescription',c_long,JOBJECT64,jint) - _fixBridgeFunc(jint,'getAccessibleTableRow',c_long,AccessibleTable,jint) - _fixBridgeFunc(jint,'getAccessibleTableColumn',c_long,AccessibleTable,jint) - _fixBridgeFunc(jint,'getAccessibleTableIndex',c_long,AccessibleTable,jint,jint) - _fixBridgeFunc(BOOL,'getAccessibleKeyBindings',c_long,JOBJECT64,POINTER(AccessibleKeyBindings),errcheck=True) - -#NVDA specific code - -isRunning=False + _fixBridgeFunc(BOOL, "getAccessibleTableRowHeader", c_long, JOBJECT64, POINTER(AccessibleTableInfo)) + _fixBridgeFunc(BOOL, "getAccessibleTableColumnHeader", c_long, JOBJECT64, POINTER(AccessibleTableInfo)) + _fixBridgeFunc(JOBJECT64, "getAccessibleTableRowDescription", c_long, JOBJECT64, jint) + _fixBridgeFunc(JOBJECT64, "getAccessibleTableColumnDescription", c_long, JOBJECT64, jint) + _fixBridgeFunc(jint, "getAccessibleTableRow", c_long, AccessibleTable, jint) + _fixBridgeFunc(jint, "getAccessibleTableColumn", c_long, AccessibleTable, jint) + _fixBridgeFunc(jint, "getAccessibleTableIndex", c_long, AccessibleTable, jint, jint) + _fixBridgeFunc( + BOOL, "getAccessibleKeyBindings", c_long, JOBJECT64, POINTER(AccessibleKeyBindings), errcheck=True + ) + + +# NVDA specific code + +isRunning = False # Cache of the last active window handle for a given JVM ID. In theory, this # cache should not be needed, as it should always be possible to retrieve the # window handle of a given accessible context by calling getTopLevelObject then -# getHWNDFromAccessibleContext. However, getTopLevelObject sometimes returns +# getHWNDFromAccessibleContext. However, getTopLevelObject sometimes returns # accessible contexts that make getHWNDFromAccessibleContext fail. To workaround # the issue, we use this cache as a fallback when either getTopLevelObject or # getHWNDFromAccessibleContext fails. -vmIDsToWindowHandles={} -internalFunctionQueue=queue.Queue(1000) -internalFunctionQueue.__name__="JABHandler.internalFunctionQueue" +vmIDsToWindowHandles = {} +internalFunctionQueue = queue.Queue(1000) +internalFunctionQueue.__name__ = "JABHandler.internalFunctionQueue" -def internalQueueFunction(func,*args,**kwargs): - internalFunctionQueue.put_nowait((func,args,kwargs)) + +def internalQueueFunction(func, *args, **kwargs): + internalFunctionQueue.put_nowait((func, args, kwargs)) core.requestPump() -def internal_getWindowHandleFromAccContext(vmID,accContext): + +def internal_getWindowHandleFromAccContext(vmID, accContext): try: - topAC=bridgeDll.getTopLevelObject(vmID,accContext) + topAC = bridgeDll.getTopLevelObject(vmID, accContext) try: - return bridgeDll.getHWNDFromAccessibleContext(vmID,topAC) + return bridgeDll.getHWNDFromAccessibleContext(vmID, topAC) finally: - bridgeDll.releaseJavaObject(vmID,topAC) + bridgeDll.releaseJavaObject(vmID, topAC) except: # noqa: E722 return None -def getWindowHandleFromAccContext(vmID,accContext): - hwnd=internal_getWindowHandleFromAccContext(vmID,accContext) + +def getWindowHandleFromAccContext(vmID, accContext): + hwnd = internal_getWindowHandleFromAccContext(vmID, accContext) if hwnd: - vmIDsToWindowHandles[vmID]=hwnd + vmIDsToWindowHandles[vmID] = hwnd return hwnd else: return vmIDsToWindowHandles.get(vmID) -class JABContext(object): - def __init__(self,hwnd=None,vmID=None,accContext=None): +class JABContext(object): + def __init__(self, hwnd=None, vmID=None, accContext=None): if hwnd and not vmID: - vmID=c_long() - accContext=JOBJECT64() - bridgeDll.getAccessibleContextFromHWND(hwnd,byref(vmID),byref(accContext)) - #Record this vm ID and window handle for later use with other objects - vmID=vmID.value - vmIDsToWindowHandles[vmID]=hwnd + vmID = c_long() + accContext = JOBJECT64() + bridgeDll.getAccessibleContextFromHWND(hwnd, byref(vmID), byref(accContext)) + # Record this vm ID and window handle for later use with other objects + vmID = vmID.value + vmIDsToWindowHandles[vmID] = hwnd elif vmID and not hwnd: - hwnd = getWindowHandleFromAccContext(vmID,accContext) - self.hwnd=hwnd - self.vmID=vmID - self.accContext=accContext + hwnd = getWindowHandleFromAccContext(vmID, accContext) + self.hwnd = hwnd + self.vmID = vmID + self.accContext = accContext def __del__(self): if isRunning: try: - bridgeDll.releaseJavaObject(self.vmID,self.accContext) + bridgeDll.releaseJavaObject(self.vmID, self.accContext) except: # noqa: E722 - log.debugWarning("Error releasing java object",exc_info=True) - + log.debugWarning("Error releasing java object", exc_info=True) - def __eq__(self,jabContext): - if self.vmID==jabContext.vmID and bridgeDll.isSameObject(self.vmID,self.accContext,jabContext.accContext): + def __eq__(self, jabContext): + if self.vmID == jabContext.vmID and bridgeDll.isSameObject( + self.vmID, self.accContext, jabContext.accContext + ): return True else: return False @@ -436,145 +550,154 @@ def __eq__(self,jabContext): def __hash__(self): return super().__hash__() - def __ne__(self,jabContext): - if self.vmID!=jabContext.vmID or not bridgeDll.isSameObject(self.vmID,self.accContext,jabContext.accContext): + def __ne__(self, jabContext): + if self.vmID != jabContext.vmID or not bridgeDll.isSameObject( + self.vmID, self.accContext, jabContext.accContext + ): return True else: return False def getVersionInfo(self): - info=AccessBridgeVersionInfo() - bridgeDll.getVersionInfo(self.vmID,byref(info)) + info = AccessBridgeVersionInfo() + bridgeDll.getVersionInfo(self.vmID, byref(info)) return info def getObjectDepth(self): - return bridgeDll.getObjectDepth(self.vmID,self.accContext) + return bridgeDll.getObjectDepth(self.vmID, self.accContext) def getAccessibleContextInfo(self): - info=AccessibleContextInfo() - bridgeDll.getAccessibleContextInfo(self.vmID,self.accContext,byref(info)) + info = AccessibleContextInfo() + bridgeDll.getAccessibleContextInfo(self.vmID, self.accContext, byref(info)) return info - def getAccessibleTextInfo(self,x,y): - textInfo=AccessibleTextInfo() - bridgeDll.getAccessibleTextInfo(self.vmID,self.accContext,byref(textInfo),x,y) + def getAccessibleTextInfo(self, x, y): + textInfo = AccessibleTextInfo() + bridgeDll.getAccessibleTextInfo(self.vmID, self.accContext, byref(textInfo), x, y) return textInfo - def getAccessibleTextItems(self,index): - textItemsInfo=AccessibleTextItemsInfo() - bridgeDll.getAccessibleTextItems(self.vmID,self.accContext,byref(textItemsInfo),index) + def getAccessibleTextItems(self, index): + textItemsInfo = AccessibleTextItemsInfo() + bridgeDll.getAccessibleTextItems(self.vmID, self.accContext, byref(textItemsInfo), index) return textItemsInfo def getAccessibleTextSelectionInfo(self): - textSelectionInfo=AccessibleTextSelectionInfo() - bridgeDll.getAccessibleTextSelectionInfo(self.vmID,self.accContext,byref(textSelectionInfo)) + textSelectionInfo = AccessibleTextSelectionInfo() + bridgeDll.getAccessibleTextSelectionInfo(self.vmID, self.accContext, byref(textSelectionInfo)) return textSelectionInfo - def getAccessibleTextRange(self,start,end): - length=((end+1)-start) - if length<=0: - return u"" + def getAccessibleTextRange(self, start, end): + length = (end + 1) - start + if length <= 0: + return "" # Use a string buffer, as from an unicode buffer, we can't get the raw data. - buf = create_string_buffer((length +1) * 2) + buf = create_string_buffer((length + 1) * 2) bridgeDll.getAccessibleTextRange(self.vmID, self.accContext, start, end, buf, length) return textUtils.getTextFromRawBytes(buf.raw, numChars=length, encoding=textUtils.WCHAR_ENCODING) - def getAccessibleTextLineBounds(self,index): - index=max(index,0) - log.debug("lineBounds: index %s"%index) - #Java returns end as the last character, not end as past the last character - startIndex=c_int() - endIndex=c_int() - bridgeDll.getAccessibleTextLineBounds(self.vmID,self.accContext,index,byref(startIndex),byref(endIndex)) - start=startIndex.value - end=endIndex.value - log.debug("line bounds: start %s, end %s"%(start,end)) - if end(index+1): + bridgeDll.getAccessibleTextLineBounds( + self.vmID, self.accContext, end, byref(startIndex), byref(endIndex) + ) + tempStart = max(startIndex.value, 0) + tempEnd = max(endIndex.value, 0) + log.debug("line bounds: tempStart %s, tempEnd %s" % (tempStart, tempEnd)) + if tempStart > (index + 1): # This line starts after the requested index, so set end to point at the line before. - end=tempStart-1 + end = tempStart - 1 else: - ok=True - ok=False + ok = True + ok = False # Try to retract the start. while not ok: - bridgeDll.getAccessibleTextLineBounds(self.vmID,self.accContext,start,byref(startIndex),byref(endIndex)) - tempStart=max(startIndex.value,0) - tempEnd=max(endIndex.value,0) - log.debug("line bounds: tempStart %s, tempEnd %s"%(tempStart,tempEnd)) - if tempEnd<(index-1): + bridgeDll.getAccessibleTextLineBounds( + self.vmID, self.accContext, start, byref(startIndex), byref(endIndex) + ) + tempStart = max(startIndex.value, 0) + tempEnd = max(endIndex.value, 0) + log.debug("line bounds: tempStart %s, tempEnd %s" % (tempStart, tempEnd)) + if tempEnd < (index - 1): # This line ends before the requested index, so set start to point at the line after. - start=tempEnd+1 + start = tempEnd + 1 else: - ok=True - log.debug("line bounds: returning %s, %s"%(start,end)) - return (start,end) - + ok = True + log.debug("line bounds: returning %s, %s" % (start, end)) + return (start, end) def getAccessibleParentFromContext(self): - accContext=bridgeDll.getAccessibleParentFromContext(self.vmID,self.accContext) + accContext = bridgeDll.getAccessibleParentFromContext(self.vmID, self.accContext) if accContext: - return self.__class__(self.hwnd,self.vmID,accContext) + return self.__class__(self.hwnd, self.vmID, accContext) else: return None def getAccessibleParentWithRole(self, role): - accContext=bridgeDll.getParentWithRole(self.vmID,self.accContext, role) + accContext = bridgeDll.getParentWithRole(self.vmID, self.accContext, role) if accContext: - return self.__class__(self.hwnd,self.vmID,accContext) + return self.__class__(self.hwnd, self.vmID, accContext) else: return None - def getAccessibleChildFromContext(self,index): - accContext=bridgeDll.getAccessibleChildFromContext(self.vmID,self.accContext,index) + def getAccessibleChildFromContext(self, index): + accContext = bridgeDll.getAccessibleChildFromContext(self.vmID, self.accContext, index) if accContext: - return self.__class__(self.hwnd,self.vmID,accContext) + return self.__class__(self.hwnd, self.vmID, accContext) else: return None def getActiveDescendent(self): - accContext=bridgeDll.getActiveDescendent(self.vmID,self.accContext) + accContext = bridgeDll.getActiveDescendent(self.vmID, self.accContext) if accContext: - return self.__class__(self.hwnd,self.vmID,accContext) + return self.__class__(self.hwnd, self.vmID, accContext) else: return None - def getAccessibleContextAt(self,x,y): - newAccContext=JOBJECT64() - res=bridgeDll.getAccessibleContextAt(self.vmID,self.accContext,x,y,byref(newAccContext)) + def getAccessibleContextAt(self, x, y): + newAccContext = JOBJECT64() + res = bridgeDll.getAccessibleContextAt(self.vmID, self.accContext, x, y, byref(newAccContext)) if not res or not newAccContext: return None - if not bridgeDll.isSameObject(self.vmID,newAccContext,self.accContext): - return self.__class__(self.hwnd,self.vmID,newAccContext) - elif newAccContext!=self.accContext: - bridgeDll.releaseJavaObject(self.vmID,newAccContext) + if not bridgeDll.isSameObject(self.vmID, newAccContext, self.accContext): + return self.__class__(self.hwnd, self.vmID, newAccContext) + elif newAccContext != self.accContext: + bridgeDll.releaseJavaObject(self.vmID, newAccContext) return None def getCurrentAccessibleValueFromContext(self): - buf=create_unicode_buffer(SHORT_STRING_SIZE+1) - bridgeDll.getCurrentAccessibleValueFromContext(self.vmID,self.accContext,buf,SHORT_STRING_SIZE) + buf = create_unicode_buffer(SHORT_STRING_SIZE + 1) + bridgeDll.getCurrentAccessibleValueFromContext(self.vmID, self.accContext, buf, SHORT_STRING_SIZE) return buf.value def selectTextRange(self, start: int, end: int) -> None: bridgeDll.selectTextRange(self.vmID, self.accContext, start, end) - def setCaretPosition(self,offset): - bridgeDll.setCaretPosition(self.vmID,self.accContext,offset) + def setCaretPosition(self, offset): + bridgeDll.setCaretPosition(self.vmID, self.accContext, offset) def getTextAttributesInRange(self, startIndex, endIndex): attributes = AccessibleTextAttributesInfo() length = c_short() - bridgeDll.getTextAttributesInRange(self.vmID, self.accContext, startIndex, endIndex, byref(attributes), byref(length)) + bridgeDll.getTextAttributesInRange( + self.vmID, self.accContext, startIndex, endIndex, byref(attributes), byref(length) + ) return attributes, length.value def getAccessibleTextRect(self, index): @@ -588,46 +711,74 @@ def getAccessibleRelationSet(self): return relations def getAccessibleTableInfo(self): - info=AccessibleTableInfo() - if bridgeDll.getAccessibleTableInfo(self.vmID,self.accContext,byref(info)): + info = AccessibleTableInfo() + if bridgeDll.getAccessibleTableInfo(self.vmID, self.accContext, byref(info)): # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, # so just pass the hwnd for the querying object. - info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None - info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None - info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None - info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None + info.jabCaption = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.caption) if info.caption else None + ) + info.jabSummary = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.summary) if info.summary else None + ) + info.jabContext = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleContext) + if info.accessibleContext + else None + ) + info.jabTable = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleTable) + if info.accessibleTable + else None + ) return info - def getAccessibleTableCellInfo(self,row,col): - info=AccessibleTableCellInfo() - if bridgeDll.getAccessibleTableCellInfo(self.vmID,self.accContext,row,col,byref(info)): + def getAccessibleTableCellInfo(self, row, col): + info = AccessibleTableCellInfo() + if bridgeDll.getAccessibleTableCellInfo(self.vmID, self.accContext, row, col, byref(info)): # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, # so just pass the hwnd for the querying object. - info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None + info.jabContext = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleContext) + if info.accessibleContext + else None + ) return info - def getAccessibleTableRow(self,index): - return bridgeDll.getAccessibleTableRow(self.vmID,self.accContext,index) + def getAccessibleTableRow(self, index): + return bridgeDll.getAccessibleTableRow(self.vmID, self.accContext, index) - def getAccessibleTableColumn(self,index): - return bridgeDll.getAccessibleTableColumn(self.vmID,self.accContext,index) + def getAccessibleTableColumn(self, index): + return bridgeDll.getAccessibleTableColumn(self.vmID, self.accContext, index) def getAccessibleTableRowHeader(self): - info=AccessibleTableInfo() - if bridgeDll.getAccessibleTableRowHeader(self.vmID,self.accContext,byref(info)): + info = AccessibleTableInfo() + if bridgeDll.getAccessibleTableRowHeader(self.vmID, self.accContext, byref(info)): # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, # so just pass the hwnd for the querying object. - info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None - info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None - info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None - info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None + info.jabCaption = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.caption) if info.caption else None + ) + info.jabSummary = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.summary) if info.summary else None + ) + info.jabContext = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleContext) + if info.accessibleContext + else None + ) + info.jabTable = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleTable) + if info.accessibleTable + else None + ) return info - def getAccessibleTableRowDescription(self,row): - accContext=bridgeDll.getAccessibleTableRowDescription(self.vmID,self.accContext,row) + def getAccessibleTableRowDescription(self, row): + accContext = bridgeDll.getAccessibleTableRowDescription(self.vmID, self.accContext, row) if accContext: # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, @@ -635,19 +786,31 @@ def getAccessibleTableRowDescription(self,row): return JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=accContext) def getAccessibleTableColumnHeader(self): - info=AccessibleTableInfo() - if bridgeDll.getAccessibleTableColumnHeader(self.vmID,self.accContext,byref(info)): + info = AccessibleTableInfo() + if bridgeDll.getAccessibleTableColumnHeader(self.vmID, self.accContext, byref(info)): # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, # so just pass the hwnd for the querying object. - info.jabCaption=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.caption) if info.caption else None - info.jabSummary=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.summary) if info.summary else None - info.jabContext=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleContext) if info.accessibleContext else None - info.jabTable=JABContext(hwnd=self.hwnd,vmID=self.vmID,accContext=info.accessibleTable) if info.accessibleTable else None + info.jabCaption = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.caption) if info.caption else None + ) + info.jabSummary = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.summary) if info.summary else None + ) + info.jabContext = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleContext) + if info.accessibleContext + else None + ) + info.jabTable = ( + JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=info.accessibleTable) + if info.accessibleTable + else None + ) return info - def getAccessibleTableColumnDescription(self,column): - accContext=bridgeDll.getAccessibleTableColumnDescription(self.vmID,self.accContext,column) + def getAccessibleTableColumnDescription(self, column): + accContext = bridgeDll.getAccessibleTableColumnDescription(self.vmID, self.accContext, column) if accContext: # #6992: Querying the hwnd for table related objects can cause the app to crash. # A table is almost certainly contained within a single hwnd, @@ -655,27 +818,30 @@ def getAccessibleTableColumnDescription(self,column): return JABContext(hwnd=self.hwnd, vmID=self.vmID, accContext=accContext) def getAccessibleKeyBindings(self): - bindings=AccessibleKeyBindings() - if bridgeDll.getAccessibleKeyBindings(self.vmID,self.accContext,byref(bindings)): + bindings = AccessibleKeyBindings() + if bridgeDll.getAccessibleKeyBindings(self.vmID, self.accContext, byref(bindings)): return bindings + @AccessBridge_FocusGainedFP -def internal_event_focusGained(vmID, event,source): - hwnd=getWindowHandleFromAccContext(vmID,source) - internalQueueFunction(event_gainFocus,vmID,source,hwnd) - bridgeDll.releaseJavaObject(vmID,event) - -def event_gainFocus(vmID,accContext,hwnd): - jabContext=JABContext(hwnd=hwnd,vmID=vmID,accContext=accContext) - if not winUser.isDescendantWindow(winUser.getForegroundWindow(),jabContext.hwnd): +def internal_event_focusGained(vmID, event, source): + hwnd = getWindowHandleFromAccContext(vmID, source) + internalQueueFunction(event_gainFocus, vmID, source, hwnd) + bridgeDll.releaseJavaObject(vmID, event) + + +def event_gainFocus(vmID, accContext, hwnd): + jabContext = JABContext(hwnd=hwnd, vmID=vmID, accContext=accContext) + if not winUser.isDescendantWindow(winUser.getForegroundWindow(), jabContext.hwnd): return - focus=eventHandler.lastQueuedFocusObject - if (isinstance(focus,NVDAObjects.JAB.JAB) and focus.jabContext==jabContext): - return - obj=NVDAObjects.JAB.JAB(jabContext=jabContext) - if obj.role==controlTypes.Role.UNKNOWN: + focus = eventHandler.lastQueuedFocusObject + if isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext: return - eventHandler.queueEvent("gainFocus",obj) + obj = NVDAObjects.JAB.JAB(jabContext=jabContext) + if obj.role == controlTypes.Role.UNKNOWN: + return + eventHandler.queueEvent("gainFocus", obj) + @AccessBridge_PropertyActiveDescendentChangeFP def internal_event_activeDescendantChange(vmID, event, source, oldDescendant, newDescendant): @@ -696,89 +862,109 @@ def internal_hasFocus(sourceContext): @AccessBridge_PropertyNameChangeFP -def event_nameChange(vmID,event,source,oldVal,newVal): +def event_nameChange(vmID, event, source, oldVal, newVal): jabContext = JABContext(vmID=vmID, accContext=source) if jabContext.hwnd: focus = api.getFocusObject() - obj = focus if ( - isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext - ) else NVDAObjects.JAB.JAB(jabContext=jabContext) + obj = ( + focus + if (isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext) + else NVDAObjects.JAB.JAB(jabContext=jabContext) + ) if obj: eventHandler.queueEvent("nameChange", obj) else: log.debugWarning("Unable to obtain window handle for accessible context") - bridgeDll.releaseJavaObject(vmID,event) + bridgeDll.releaseJavaObject(vmID, event) + @AccessBridge_PropertyDescriptionChangeFP -def event_descriptionChange(vmID,event,source,oldVal,newVal): +def event_descriptionChange(vmID, event, source, oldVal, newVal): jabContext = JABContext(vmID=vmID, accContext=source) if jabContext.hwnd: focus = api.getFocusObject() - obj = focus if ( - isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext - ) else NVDAObjects.JAB.JAB(jabContext=jabContext) + obj = ( + focus + if (isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext) + else NVDAObjects.JAB.JAB(jabContext=jabContext) + ) if obj: eventHandler.queueEvent("descriptionChange", obj) else: log.debugWarning("Unable to obtain window handle for accessible context") - bridgeDll.releaseJavaObject(vmID,event) + bridgeDll.releaseJavaObject(vmID, event) + @AccessBridge_PropertyValueChangeFP -def event_valueChange(vmID,event,source,oldVal,newVal): +def event_valueChange(vmID, event, source, oldVal, newVal): jabContext = JABContext(vmID=vmID, accContext=source) if jabContext.hwnd: focus = api.getFocusObject() - obj = focus if ( - isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext - ) else NVDAObjects.JAB.JAB(jabContext=jabContext) + obj = ( + focus + if (isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext) + else NVDAObjects.JAB.JAB(jabContext=jabContext) + ) if obj: eventHandler.queueEvent("valueChange", obj) else: log.debugWarning("Unable to obtain window handle for accessible context") - bridgeDll.releaseJavaObject(vmID,event) + bridgeDll.releaseJavaObject(vmID, event) + @AccessBridge_PropertyStateChangeFP -def internal_event_stateChange(vmID,event,source,oldState,newState): - internalQueueFunction(event_stateChange,vmID,source,oldState,newState) - bridgeDll.releaseJavaObject(vmID,event) +def internal_event_stateChange(vmID, event, source, oldState, newState): + internalQueueFunction(event_stateChange, vmID, source, oldState, newState) + bridgeDll.releaseJavaObject(vmID, event) + -def event_stateChange(vmID,accContext,oldState,newState): +def event_stateChange(vmID, accContext, oldState, newState): jabContext = JABContext(vmID=vmID, accContext=accContext) if not jabContext.hwnd: log.debugWarning("Unable to obtain window handle for accessible context") return focus = api.getFocusObject() - #For broken tabs and menus, we need to watch for things being selected and pretend its a focus change - stateList = newState.split(',') + # For broken tabs and menus, we need to watch for things being selected and pretend its a focus change + stateList = newState.split(",") if "focused" in stateList or "selected" in stateList: obj = NVDAObjects.JAB.JAB(jabContext=jabContext) if not obj: return - if focus!=obj and eventHandler.lastQueuedFocusObject!=obj and obj.role in (controlTypes.Role.MENUITEM,controlTypes.Role.TAB,controlTypes.Role.MENU): - eventHandler.queueEvent("gainFocus",obj) + if ( + focus != obj + and eventHandler.lastQueuedFocusObject != obj + and obj.role in (controlTypes.Role.MENUITEM, controlTypes.Role.TAB, controlTypes.Role.MENU) + ): + eventHandler.queueEvent("gainFocus", obj) return - obj = focus if ( - isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext - ) else NVDAObjects.JAB.JAB(jabContext=jabContext) + obj = ( + focus + if (isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext) + else NVDAObjects.JAB.JAB(jabContext=jabContext) + ) if obj: eventHandler.queueEvent("stateChange", obj) + @AccessBridge_PropertyCaretChangeFP -def internal_event_caretChange(vmID, event,source,oldPos,newPos): - hwnd=getWindowHandleFromAccContext(vmID,source) - if oldPos<0 and newPos>=0: - internalQueueFunction(event_gainFocus,vmID,source,hwnd) +def internal_event_caretChange(vmID, event, source, oldPos, newPos): + hwnd = getWindowHandleFromAccContext(vmID, source) + if oldPos < 0 and newPos >= 0: + internalQueueFunction(event_gainFocus, vmID, source, hwnd) else: - internalQueueFunction(event_caret,vmID,source,hwnd) - bridgeDll.releaseJavaObject(vmID,event) + internalQueueFunction(event_caret, vmID, source, hwnd) + bridgeDll.releaseJavaObject(vmID, event) + def event_caret(vmID, accContext, hwnd): jabContext = JABContext(hwnd=hwnd, vmID=vmID, accContext=accContext) if jabContext.hwnd: focus = api.getFocusObject() - obj = focus if ( - isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext - ) else NVDAObjects.JAB.JAB(jabContext=jabContext) + obj = ( + focus + if (isinstance(focus, NVDAObjects.JAB.JAB) and focus.jabContext == jabContext) + else NVDAObjects.JAB.JAB(jabContext=jabContext) + ) if obj: eventHandler.queueEvent("caret", obj) else: @@ -786,31 +972,33 @@ def event_caret(vmID, accContext, hwnd): def event_enterJavaWindow(hwnd): - internalQueueFunction(enterJavaWindow_helper,hwnd) + internalQueueFunction(enterJavaWindow_helper, hwnd) + def enterJavaWindow_helper(hwnd): - vmID=c_long() - accContext=JOBJECT64() - timeout=time.time()+0.2 - while time.time() SystemErrorCodes: focus = api.getFocusObject() if focus.sleepMode == focus.SLEEP_FULL: @@ -146,7 +149,7 @@ def markCallable(name: str): speech.speak, speechSequence=sequence, symbolLevel=symbolLevel, - priority=priority + priority=priority, ) if not asynchronous: try: @@ -168,60 +171,85 @@ def markCallable(name: str): @WINFUNCTYPE(c_long) def nvdaController_cancelSpeech(): - focus=api.getFocusObject() - if focus.sleepMode==focus.SLEEP_FULL: + focus = api.getFocusObject() + if focus.sleepMode == focus.SLEEP_FULL: return -1 import speech - queueHandler.queueFunction(queueHandler.eventQueue,speech.cancelSpeech) + + queueHandler.queueFunction(queueHandler.eventQueue, speech.cancelSpeech) return SystemErrorCodes.SUCCESS -@WINFUNCTYPE(c_long,c_wchar_p) +@WINFUNCTYPE(c_long, c_wchar_p) def nvdaController_brailleMessage(text: str) -> SystemErrorCodes: - focus=api.getFocusObject() - if focus.sleepMode==focus.SLEEP_FULL: + focus = api.getFocusObject() + if focus.sleepMode == focus.SLEEP_FULL: return -1 if config.conf["braille"]["reportLiveRegions"]: import braille + queueHandler.queueFunction(queueHandler.eventQueue, braille.handler.message, text) return SystemErrorCodes.SUCCESS def _lookupKeyboardLayoutNameWithHexString(layoutString): - buf=create_unicode_buffer(1024) - bufSize=c_int(2048) - key=HKEY() # noqa: F405 - if windll.advapi32.RegOpenKeyExW(winreg.HKEY_LOCAL_MACHINE,u"SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\"+ layoutString,0,winreg.KEY_QUERY_VALUE,byref(key))==0: # noqa: F405 + buf = create_unicode_buffer(1024) + bufSize = c_int(2048) + key = HKEY() # noqa: F405 + if ( + windll.advapi32.RegOpenKeyExW( + winreg.HKEY_LOCAL_MACHINE, + "SYSTEM\\CurrentControlSet\\Control\\Keyboard Layouts\\" + layoutString, + 0, + winreg.KEY_QUERY_VALUE, + byref(key), + ) + == 0 + ): # noqa: F405 try: - if windll.advapi32.RegQueryValueExW(key,u"Layout Display Name",0,None,buf,byref(bufSize))==0: # noqa: F405 - windll.shlwapi.SHLoadIndirectString(buf.value,buf,1023,None) + if ( + windll.advapi32.RegQueryValueExW(key, "Layout Display Name", 0, None, buf, byref(bufSize)) + == 0 + ): # noqa: F405 + windll.shlwapi.SHLoadIndirectString(buf.value, buf, 1023, None) return buf.value - if windll.advapi32.RegQueryValueExW(key,u"Layout Text",0,None,buf,byref(bufSize))==0: # noqa: F405 + if windll.advapi32.RegQueryValueExW(key, "Layout Text", 0, None, buf, byref(bufSize)) == 0: # noqa: F405 return buf.value finally: windll.advapi32.RegCloseKey(key) -@WINFUNCTYPE(c_long,c_wchar_p) + +@WINFUNCTYPE(c_long, c_wchar_p) def nvdaControllerInternal_requestRegistration(uuidString): - pid=c_long() - windll.rpcrt4.I_RpcBindingInqLocalClientPID(None,byref(pid)) # noqa: F405 - pid=pid.value + pid = c_long() + windll.rpcrt4.I_RpcBindingInqLocalClientPID(None, byref(pid)) # noqa: F405 + pid = pid.value if not pid: log.error("Could not get process ID for RPC call") return -1 - bindingHandle=c_long() - bindingHandle.value=localLib.createRemoteBindingHandle(uuidString) - if not bindingHandle: - log.error("Could not bind to inproc rpc server for pid %d"%pid) + bindingHandle = c_long() + bindingHandle.value = localLib.createRemoteBindingHandle(uuidString) + if not bindingHandle: + log.error("Could not bind to inproc rpc server for pid %d" % pid) return -1 - registrationHandle=c_long() - res=localLib.nvdaInProcUtils_registerNVDAProcess(bindingHandle,byref(registrationHandle)) # noqa: F405 - if res!=0 or not registrationHandle: - log.error("Could not register NVDA with inproc rpc server for pid %d, res %d, registrationHandle %s"%(pid,res,registrationHandle)) + registrationHandle = c_long() + res = localLib.nvdaInProcUtils_registerNVDAProcess(bindingHandle, byref(registrationHandle)) # noqa: F405 + if res != 0 or not registrationHandle: + log.error( + "Could not register NVDA with inproc rpc server for pid %d, res %d, registrationHandle %s" + % (pid, res, registrationHandle) + ) windll.rpcrt4.RpcBindingFree(byref(bindingHandle)) # noqa: F405 return -1 import appModuleHandler - queueHandler.queueFunction(queueHandler.eventQueue,appModuleHandler.update,pid,helperLocalBindingHandle=bindingHandle,inprocRegistrationHandle=registrationHandle) + + queueHandler.queueFunction( + queueHandler.eventQueue, + appModuleHandler.update, + pid, + helperLocalBindingHandle=bindingHandle, + inprocRegistrationHandle=registrationHandle, + ) return 0 @@ -238,10 +266,13 @@ def nvdaControllerInternal_reportLiveRegion(text: str, politeness: str): import braille from aria import AriaLivePoliteness from speech.priorities import Spri + try: politenessValue = AriaLivePoliteness(politeness.lower()) except ValueError: - log.error(f"nvdaControllerInternal_reportLiveRegion got unknown politeness of {politeness}", exc_info=True) + log.error( + f"nvdaControllerInternal_reportLiveRegion got unknown politeness of {politeness}", exc_info=True + ) return -1 if politenessValue == AriaLivePoliteness.OFF: log.error(f"nvdaControllerInternal_reportLiveRegion got unexpected politeness of {politeness}") @@ -249,176 +280,209 @@ def nvdaControllerInternal_reportLiveRegion(text: str, politeness: str): queueHandler.eventQueue, speech.speakText, text, - priority=( - Spri.NEXT - if politenessValue == AriaLivePoliteness.ASSERTIVE - else Spri.NORMAL - ) - ) - queueHandler.queueFunction( - queueHandler.eventQueue, - braille.handler.message, - text + priority=(Spri.NEXT if politenessValue == AriaLivePoliteness.ASSERTIVE else Spri.NORMAL), ) + queueHandler.queueFunction(queueHandler.eventQueue, braille.handler.message, text) return 0 -@WINFUNCTYPE(c_long,c_long,c_long,c_long,c_long,c_long) + +@WINFUNCTYPE(c_long, c_long, c_long, c_long, c_long, c_long) def nvdaControllerInternal_displayModelTextChangeNotify(hwnd, left, top, right, bottom): import displayModel + displayModel.textChangeNotify(hwnd, left, top, right, bottom) return 0 -@WINFUNCTYPE(c_long,c_long,c_long,c_long,c_long,c_long) + +@WINFUNCTYPE(c_long, c_long, c_long, c_long, c_long, c_long) def nvdaControllerInternal_drawFocusRectNotify(hwnd, left, top, right, bottom): import eventHandler from NVDAObjects.window import Window - focus=api.getFocusObject() - if isinstance(focus,Window) and hwnd==focus.windowHandle: - eventHandler.queueEvent("displayModel_drawFocusRectNotify",focus,rect=(left,top,right,bottom)) + + focus = api.getFocusObject() + if isinstance(focus, Window) and hwnd == focus.windowHandle: + eventHandler.queueEvent("displayModel_drawFocusRectNotify", focus, rect=(left, top, right, bottom)) return 0 -@WINFUNCTYPE(c_long,c_long,c_long,c_wchar_p) -def nvdaControllerInternal_logMessage(level,pid,message): + +@WINFUNCTYPE(c_long, c_long, c_long, c_wchar_p) +def nvdaControllerInternal_logMessage(level, pid, message): if not log.isEnabledFor(level): return 0 if pid: from appModuleHandler import getAppNameFromProcessID - codepath="RPC process %s (%s)"%(pid,getAppNameFromProcessID(pid,includeExt=True)) + + codepath = "RPC process %s (%s)" % (pid, getAppNameFromProcessID(pid, includeExt=True)) else: - codepath="NVDAHelperLocal" - log._log(level,message,[],codepath=codepath) + codepath = "NVDAHelperLocal" + log._log(level, message, [], codepath=codepath) return 0 + def handleInputCompositionEnd(result): import speech import characterProcessing from NVDAObjects.inputComposition import InputComposition from NVDAObjects.IAccessible.mscandui import ModernCandidateUICandidateItem - focus=api.getFocusObject() - result=result.lstrip(u'\u3000 ') - curInputComposition=None - if isinstance(focus,InputComposition): - curInputComposition=focus + + focus = api.getFocusObject() + result = result.lstrip("\u3000 ") + curInputComposition = None + if isinstance(focus, InputComposition): + curInputComposition = focus oldSpeechMode = speech.getState().speechMode speech.setSpeechMode(speech.SpeechMode.off) - eventHandler.executeEvent("gainFocus",focus.parent) + eventHandler.executeEvent("gainFocus", focus.parent) speech.setSpeechMode(oldSpeechMode) - elif isinstance(focus.parent,InputComposition): - #Candidate list is still up - curInputComposition=focus.parent - focus.parent=focus.parent.parent + elif isinstance(focus.parent, InputComposition): + # Candidate list is still up + curInputComposition = focus.parent + focus.parent = focus.parent.parent if isinstance(focus, ModernCandidateUICandidateItem): # Correct focus for ModernCandidateUICandidateItem # Find the InputComposition object and # correct focus to its parent if isinstance(focus.container, InputComposition): - curInputComposition=focus.container - newFocus=curInputComposition.parent + curInputComposition = focus.container + newFocus = curInputComposition.parent else: # Sometimes InputCompositon object is gone # Correct to container of CandidateItem - newFocus=focus.container + newFocus = focus.container oldSpeechMode = speech.getState().speechMode speech.setSpeechMode(speech.SpeechMode.off) - eventHandler.executeEvent("gainFocus",newFocus) + eventHandler.executeEvent("gainFocus", newFocus) speech.setSpeechMode(oldSpeechMode) if curInputComposition and not result: - result=curInputComposition.compositionString.lstrip(u'\u3000 ') + result = curInputComposition.compositionString.lstrip("\u3000 ") if result: speech.speakText(result, symbolLevel=characterProcessing.SymbolLevel.ALL) -def handleInputCompositionStart(compositionString,selectionStart,selectionEnd,isReading): + +def handleInputCompositionStart(compositionString, selectionStart, selectionEnd, isReading): import speech from NVDAObjects.inputComposition import InputComposition from NVDAObjects.behaviors import CandidateItem - focus=api.getFocusObject() - if focus.parent and isinstance(focus.parent,InputComposition): - #Candidates infront of existing composition string - announce=not config.conf["inputComposition"]["announceSelectedCandidate"] - focus.parent.compositionUpdate(compositionString,selectionStart,selectionEnd,isReading,announce=announce) + + focus = api.getFocusObject() + if focus.parent and isinstance(focus.parent, InputComposition): + # Candidates infront of existing composition string + announce = not config.conf["inputComposition"]["announceSelectedCandidate"] + focus.parent.compositionUpdate( + compositionString, selectionStart, selectionEnd, isReading, announce=announce + ) return 0 - #IME keeps updating input composition while the candidate list is open - #Therefore ignore new composition updates if candidate selections are configured for speaking. - if config.conf["inputComposition"]["announceSelectedCandidate"] and isinstance(focus,CandidateItem): + # IME keeps updating input composition while the candidate list is open + # Therefore ignore new composition updates if candidate selections are configured for speaking. + if config.conf["inputComposition"]["announceSelectedCandidate"] and isinstance(focus, CandidateItem): return 0 - if not isinstance(focus,InputComposition): - parent=api.getDesktopObject().objectWithFocus() + if not isinstance(focus, InputComposition): + parent = api.getDesktopObject().objectWithFocus() # #5640: Although we want to use the most correct focus (I.e. OS, not NVDA), if they are the same, we definitely want to use the original instance, so that state such as auto selection is maintained. - if parent==focus: - parent=focus - curInputComposition=InputComposition(parent=parent) + if parent == focus: + parent = focus + curInputComposition = InputComposition(parent=parent) oldSpeechMode = speech.getState().speechMode speech.setSpeechMode(speech.SpeechMode.off) - eventHandler.executeEvent("gainFocus",curInputComposition) - focus=curInputComposition + eventHandler.executeEvent("gainFocus", curInputComposition) + focus = curInputComposition speech.setSpeechMode(oldSpeechMode) - focus.compositionUpdate(compositionString,selectionStart,selectionEnd,isReading) + focus.compositionUpdate(compositionString, selectionStart, selectionEnd, isReading) + -@WINFUNCTYPE(c_long,c_wchar_p,c_int,c_int,c_int) -def nvdaControllerInternal_inputCompositionUpdate(compositionString,selectionStart,selectionEnd,isReading): +@WINFUNCTYPE(c_long, c_wchar_p, c_int, c_int, c_int) +def nvdaControllerInternal_inputCompositionUpdate(compositionString, selectionStart, selectionEnd, isReading): from NVDAObjects.inputComposition import InputComposition from NVDAObjects.IAccessible.mscandui import ModernCandidateUICandidateItem - if selectionStart==-1: - queueHandler.queueFunction(queueHandler.eventQueue,handleInputCompositionEnd,compositionString) + + if selectionStart == -1: + queueHandler.queueFunction(queueHandler.eventQueue, handleInputCompositionEnd, compositionString) return 0 - focus=api.getFocusObject() - if isinstance(focus,InputComposition): - focus.compositionUpdate(compositionString,selectionStart,selectionEnd,isReading) + focus = api.getFocusObject() + if isinstance(focus, InputComposition): + focus.compositionUpdate(compositionString, selectionStart, selectionEnd, isReading) # Eliminate InputCompositionStart events from Microsoft Pinyin to avoid reading composition string instead of candidates - elif not isinstance(focus,ModernCandidateUICandidateItem): - queueHandler.queueFunction(queueHandler.eventQueue,handleInputCompositionStart,compositionString,selectionStart,selectionEnd,isReading) + elif not isinstance(focus, ModernCandidateUICandidateItem): + queueHandler.queueFunction( + queueHandler.eventQueue, + handleInputCompositionStart, + compositionString, + selectionStart, + selectionEnd, + isReading, + ) return 0 -def handleInputCandidateListUpdate(candidatesString,selectionIndex,inputMethod): - candidateStrings=candidatesString.split('\n') + +def handleInputCandidateListUpdate(candidatesString, selectionIndex, inputMethod): + candidateStrings = candidatesString.split("\n") import speech from NVDAObjects.inputComposition import CandidateItem - focus=api.getFocusObject() - if not (0<=selectionIndex0: - queueHandler.queueFunction(queueHandler.eventQueue,ui.message," ".join(textList)) + if len(textList) > 0: + queueHandler.queueFunction(queueHandler.eventQueue, ui.message, " ".join(textList)) + -@WINFUNCTYPE(c_long,c_long,c_long,c_ulong) -def nvdaControllerInternal_inputConversionModeUpdate(oldFlags,newFlags,lcid): - queueHandler.queueFunction(queueHandler.eventQueue,handleInputConversionModeUpdate,oldFlags,newFlags,lcid) +@WINFUNCTYPE(c_long, c_long, c_long, c_ulong) +def nvdaControllerInternal_inputConversionModeUpdate(oldFlags, newFlags, lcid): + queueHandler.queueFunction( + queueHandler.eventQueue, handleInputConversionModeUpdate, oldFlags, newFlags, lcid + ) return 0 -@WINFUNCTYPE(c_long,c_long) + +@WINFUNCTYPE(c_long, c_long) def nvdaControllerInternal_IMEOpenStatusUpdate(opened): if opened: # Translators: a message when the IME open status changes to opened - message=_("IME opened") + message = _("IME opened") else: # Translators: a message when the IME open status changes to closed - message=_("IME closed") + message = _("IME closed") import ui - queueHandler.queueFunction(queueHandler.eventQueue,ui.message,message) + + queueHandler.queueFunction(queueHandler.eventQueue, ui.message, message) return 0 -@WINFUNCTYPE(c_long,c_long,c_ulong,c_wchar_p) -def nvdaControllerInternal_inputLangChangeNotify(threadID,hkl,layoutString): + +@WINFUNCTYPE(c_long, c_long, c_ulong, c_wchar_p) +def nvdaControllerInternal_inputLangChangeNotify(threadID, hkl, layoutString): global lastLanguageID, lastLayoutString - languageID=winUser.LOWORD(hkl) - #Simple case where there is no change - if languageID==lastLanguageID and layoutString==lastLayoutString: + languageID = winUser.LOWORD(hkl) + # Simple case where there is no change + if languageID == lastLanguageID and layoutString == lastLayoutString: return 0 - focus=api.getFocusObject() - #This callback can be called before NVDa is fully initialized - #So also handle focus object being None as well as checking for sleepMode + focus = api.getFocusObject() + # This callback can be called before NVDa is fully initialized + # So also handle focus object being None as well as checking for sleepMode if not focus or focus.sleepMode: return 0 import NVDAObjects.window - #Generally we should not allow input lang changes from threads that are not focused. - #But threadIDs for console windows are always wrong so don't ignore for those. - if not isinstance(focus,NVDAObjects.window.Window) or (threadID!=focus.windowThreadID and focus.windowClassName!="ConsoleWindowClass"): + + # Generally we should not allow input lang changes from threads that are not focused. + # But threadIDs for console windows are always wrong so don't ignore for those. + if not isinstance(focus, NVDAObjects.window.Window) or ( + threadID != focus.windowThreadID and focus.windowClassName != "ConsoleWindowClass" + ): return 0 from speech import sayAll - #Never announce changes while in sayAll (#1676) + + # Never announce changes while in sayAll (#1676) if sayAll.SayAllHandler.isRunning(): return 0 import ui - buf=create_unicode_buffer(1024) - res=windll.kernel32.GetLocaleInfoW(languageID,2,buf,1024) + + buf = create_unicode_buffer(1024) + res = windll.kernel32.GetLocaleInfoW(languageID, 2, buf, 1024) # Translators: the label for an unknown language when switching input methods. - inputLanguageName=buf.value if res else _("unknown language") - layoutStringCodes=[] - inputMethodName=None - #layoutString can either be a real input method name, a hex string for an input method name in the registry, or an empty string. - #If it is a real input method name, then it is used as is. - #If it is a hex string or it is empty, then the method name is looked up by trying: - #The full hex string, the hkl as a hex string, the low word of the hex string or hkl, the high word of the hex string or hkl. + inputLanguageName = buf.value if res else _("unknown language") + layoutStringCodes = [] + inputMethodName = None + # layoutString can either be a real input method name, a hex string for an input method name in the registry, or an empty string. + # If it is a real input method name, then it is used as is. + # If it is a hex string or it is empty, then the method name is looked up by trying: + # The full hex string, the hkl as a hex string, the low word of the hex string or hkl, the high word of the hex string or hkl. if layoutString: try: - int(layoutString,16) + int(layoutString, 16) layoutStringCodes.append(layoutString) except ValueError: - inputMethodName=layoutString + inputMethodName = layoutString if not inputMethodName: - layoutStringCodes.insert(0,hex(hkl)[2:].rstrip('L').upper().rjust(8,'0')) + layoutStringCodes.insert(0, hex(hkl)[2:].rstrip("L").upper().rjust(8, "0")) for stringCode in list(layoutStringCodes): - layoutStringCodes.append(stringCode[4:].rjust(8,'0')) - if stringCode[0]<'D': - layoutStringCodes.append(stringCode[0:4].rjust(8,'0')) + layoutStringCodes.append(stringCode[4:].rjust(8, "0")) + if stringCode[0] < "D": + layoutStringCodes.append(stringCode[0:4].rjust(8, "0")) for stringCode in layoutStringCodes: - inputMethodName=_lookupKeyboardLayoutNameWithHexString(stringCode) - if inputMethodName: break # noqa: E701 + inputMethodName = _lookupKeyboardLayoutNameWithHexString(stringCode) + if inputMethodName: + break # noqa: E701 if not inputMethodName: - log.debugWarning("Could not find layout name for keyboard layout, reporting as unknown") - # Translators: The label for an unknown input method when switching input methods. - inputMethodName=_("unknown input method") - #Remove the language name if it is in the input method name. - if ' - ' in inputMethodName: - inputMethodName="".join(inputMethodName.split(' - ')[1:]) - #Include the language only if it changed. - if languageID!=lastLanguageID: - msg=u"{language} - {layout}".format(language=inputLanguageName,layout=inputMethodName) + log.debugWarning("Could not find layout name for keyboard layout, reporting as unknown") + # Translators: The label for an unknown input method when switching input methods. + inputMethodName = _("unknown input method") + # Remove the language name if it is in the input method name. + if " - " in inputMethodName: + inputMethodName = "".join(inputMethodName.split(" - ")[1:]) + # Include the language only if it changed. + if languageID != lastLanguageID: + msg = "{language} - {layout}".format(language=inputLanguageName, layout=inputMethodName) else: - msg=inputMethodName - lastLanguageID=languageID - lastLayoutString=layoutString - queueHandler.queueFunction(queueHandler.eventQueue,ui.message,msg) + msg = inputMethodName + lastLanguageID = languageID + lastLayoutString = layoutString + queueHandler.queueFunction(queueHandler.eventQueue, ui.message, msg) return 0 @WINFUNCTYPE(c_long, c_wchar) def nvdaControllerInternal_typedCharacterNotify(ch): - focus=api.getFocusObject() - if focus.windowClassName!="ConsoleWindowClass": + focus = api.getFocusObject() + if focus.windowClassName != "ConsoleWindowClass": eventHandler.queueEvent("typedCharacter", focus, ch=ch) return 0 + @WINFUNCTYPE(c_long, c_int, c_int) def nvdaControllerInternal_vbufChangeNotify(rootDocHandle, rootID): import virtualBuffers + virtualBuffers.VirtualBuffer.changeNotify(rootDocHandle, rootID) return 0 + @WINFUNCTYPE(c_long, c_wchar_p) def nvdaControllerInternal_installAddonPackageFromPath(addonPath): if globalVars.appArgs.launcher: @@ -568,6 +650,7 @@ def nvdaControllerInternal_installAddonPackageFromPath(addonPath): return import wx from gui import addonGui + log.debug("Requesting installation of add-on from %s", addonPath) wx.CallAfter(addonGui.handleRemoteAddonInstall, addonPath) return 0 @@ -582,12 +665,12 @@ def nvdaControllerInternal_openConfigDirectory(): log.debugWarning("Unable to open user config directory while Windows is locked.") return import systemUtils + systemUtils.openUserConfigurationDirectory() return 0 class _RemoteLoader: - def __init__(self, loaderDir: str): # Create a pipe so we can write to stdin of the loader process. pipeReadOrig, self._pipeWrite = winKernel.CreatePipe(None, 0) @@ -600,7 +683,12 @@ def __init__(self, loaderDir: str): with open("nul", "wb") as nul: nulHandle = self._duplicateAsInheritable(msvcrt.get_osfhandle(nul.fileno())) # Set the process to start with the appropriate std* handles. - si = winKernel.STARTUPINFO(dwFlags=winKernel.STARTF_USESTDHANDLES, hSTDInput=pipeRead, hSTDOutput=nulHandle, hSTDError=nulHandle) + si = winKernel.STARTUPINFO( + dwFlags=winKernel.STARTF_USESTDHANDLES, + hSTDInput=pipeRead, + hSTDOutput=nulHandle, + hSTDError=nulHandle, + ) pi = winKernel.PROCESS_INFORMATION() # Even if we have uiAccess privileges, they will not be inherited by default. # Therefore, explicitly specify our own process token, which causes them to be inherited. @@ -635,51 +723,64 @@ def initialize() -> None: global _remoteLib, _remoteLoaderAMD64, _remoteLoaderARM64 global localLib, generateBeep, onSsmlMarkReached, VBuf_getTextInRange global lastLanguageID, lastLayoutString - hkl=c_ulong(windll.User32.GetKeyboardLayout(0)).value - lastLanguageID=winUser.LOWORD(hkl) - KL_NAMELENGTH=9 - buf=create_unicode_buffer(KL_NAMELENGTH) - res=windll.User32.GetKeyboardLayoutNameW(buf) + hkl = c_ulong(windll.User32.GetKeyboardLayout(0)).value + lastLanguageID = winUser.LOWORD(hkl) + KL_NAMELENGTH = 9 + buf = create_unicode_buffer(KL_NAMELENGTH) + res = windll.User32.GetKeyboardLayoutNameW(buf) if res: - lastLayoutString=buf.value - localLib=cdll.LoadLibrary(os.path.join(versionedLibPath,'nvdaHelperLocal.dll')) # noqa: F405 - for name,func in [ - ("nvdaController_speakText",nvdaController_speakText), + lastLayoutString = buf.value + localLib = cdll.LoadLibrary(os.path.join(versionedLibPath, "nvdaHelperLocal.dll")) # noqa: F405 + for name, func in [ + ("nvdaController_speakText", nvdaController_speakText), ("nvdaController_speakSsml", nvdaController_speakSsml), - ("nvdaController_cancelSpeech",nvdaController_cancelSpeech), - ("nvdaController_brailleMessage",nvdaController_brailleMessage), - ("nvdaControllerInternal_requestRegistration",nvdaControllerInternal_requestRegistration), + ("nvdaController_cancelSpeech", nvdaController_cancelSpeech), + ("nvdaController_brailleMessage", nvdaController_brailleMessage), + ("nvdaControllerInternal_requestRegistration", nvdaControllerInternal_requestRegistration), ("nvdaControllerInternal_reportLiveRegion", nvdaControllerInternal_reportLiveRegion), - ("nvdaControllerInternal_inputLangChangeNotify",nvdaControllerInternal_inputLangChangeNotify), - ("nvdaControllerInternal_typedCharacterNotify",nvdaControllerInternal_typedCharacterNotify), - ("nvdaControllerInternal_displayModelTextChangeNotify",nvdaControllerInternal_displayModelTextChangeNotify), - ("nvdaControllerInternal_logMessage",nvdaControllerInternal_logMessage), - ("nvdaControllerInternal_inputCompositionUpdate",nvdaControllerInternal_inputCompositionUpdate), - ("nvdaControllerInternal_inputCandidateListUpdate",nvdaControllerInternal_inputCandidateListUpdate), - ("nvdaControllerInternal_IMEOpenStatusUpdate",nvdaControllerInternal_IMEOpenStatusUpdate), - ("nvdaControllerInternal_inputConversionModeUpdate",nvdaControllerInternal_inputConversionModeUpdate), - ("nvdaControllerInternal_vbufChangeNotify",nvdaControllerInternal_vbufChangeNotify), - ("nvdaControllerInternal_installAddonPackageFromPath",nvdaControllerInternal_installAddonPackageFromPath), - ("nvdaControllerInternal_drawFocusRectNotify",nvdaControllerInternal_drawFocusRectNotify), + ("nvdaControllerInternal_inputLangChangeNotify", nvdaControllerInternal_inputLangChangeNotify), + ("nvdaControllerInternal_typedCharacterNotify", nvdaControllerInternal_typedCharacterNotify), + ( + "nvdaControllerInternal_displayModelTextChangeNotify", + nvdaControllerInternal_displayModelTextChangeNotify, + ), + ("nvdaControllerInternal_logMessage", nvdaControllerInternal_logMessage), + ("nvdaControllerInternal_inputCompositionUpdate", nvdaControllerInternal_inputCompositionUpdate), + ("nvdaControllerInternal_inputCandidateListUpdate", nvdaControllerInternal_inputCandidateListUpdate), + ("nvdaControllerInternal_IMEOpenStatusUpdate", nvdaControllerInternal_IMEOpenStatusUpdate), + ( + "nvdaControllerInternal_inputConversionModeUpdate", + nvdaControllerInternal_inputConversionModeUpdate, + ), + ("nvdaControllerInternal_vbufChangeNotify", nvdaControllerInternal_vbufChangeNotify), + ( + "nvdaControllerInternal_installAddonPackageFromPath", + nvdaControllerInternal_installAddonPackageFromPath, + ), + ("nvdaControllerInternal_drawFocusRectNotify", nvdaControllerInternal_drawFocusRectNotify), ("nvdaControllerInternal_openConfigDirectory", nvdaControllerInternal_openConfigDirectory), ]: try: - _setDllFuncPointer(localLib,"_%s"%name,func) + _setDllFuncPointer(localLib, "_%s" % name, func) except AttributeError as e: - log.error("nvdaHelperLocal function pointer for %s could not be found, possibly old nvdaHelperLocal dll"%name,exc_info=True) + log.error( + "nvdaHelperLocal function pointer for %s could not be found, possibly old nvdaHelperLocal dll" + % name, + exc_info=True, + ) raise e localLib.nvdaHelperLocal_initialize(globalVars.appArgs.secure) - generateBeep=localLib.generateBeep - generateBeep.argtypes=[c_char_p,c_float,c_int,c_int,c_int] # noqa: F405 - generateBeep.restype=c_int + generateBeep = localLib.generateBeep + generateBeep.argtypes = [c_char_p, c_float, c_int, c_int, c_int] # noqa: F405 + generateBeep.restype = c_int onSsmlMarkReached = localLib.nvdaController_onSsmlMarkReached onSsmlMarkReached.argtypes = [c_wchar_p] onSsmlMarkReached.restype = c_ulong # The rest of this function (to do with injection) only applies if NVDA is not running as a Windows store application # Handle VBuf_getTextInRange's BSTR out parameter so that the BSTR will be freed automatically. VBuf_getTextInRange = CFUNCTYPE(c_int, c_int, c_int, c_int, POINTER(BSTR), c_int)( # noqa: F405 - ("VBuf_getTextInRange", localLib), - ((1,), (1,), (1,), (2,), (1,))) + ("VBuf_getTextInRange", localLib), ((1,), (1,), (1,), (2,), (1,)) + ) if config.isAppX: log.info("Remote injection disabled due to running as a Windows Store Application") return @@ -690,22 +791,22 @@ def initialize() -> None: # Using an altered search path is necessary here # As NVDAHelperRemote needs to locate dependent dlls in the same directory # such as IAccessible2proxy.dll. - winKernel.LOAD_WITH_ALTERED_SEARCH_PATH + winKernel.LOAD_WITH_ALTERED_SEARCH_PATH, ) if not h: log.critical("Error loading nvdaHelperRemote.dll: %s" % WinError()) # noqa: F405 return - _remoteLib=CDLL("nvdaHelperRemote",handle=h) # noqa: F405 + _remoteLib = CDLL("nvdaHelperRemote", handle=h) # noqa: F405 if _remoteLib.injection_initialize() == 0: raise RuntimeError("Error initializing NVDAHelperRemote") if not _remoteLib.installIA2Support(): log.error("Error installing IA2 support") - #Manually start the in-process manager thread for this NVDA main thread now, as a slow system can cause this action to confuse WX + # Manually start the in-process manager thread for this NVDA main thread now, as a slow system can cause this action to confuse WX _remoteLib.initInprocManagerThreadIfNeeded() arch = winVersion.getWinVer().processorArchitecture - if arch == 'AMD64': + if arch == "AMD64": _remoteLoaderAMD64 = _RemoteLoader(versionedLibAMD64Path) - elif arch == 'ARM64': + elif arch == "ARM64": _remoteLoaderARM64 = _RemoteLoader(versionedLibARM64Path) # Windows on ARM from Windows 11 supports running AMD64 apps. # Thus we also need to be able to inject into these. @@ -721,25 +822,29 @@ def terminate(): log.debugWarning("Error uninstalling IA2 support") if _remoteLib.injection_terminate() == 0: raise RuntimeError("Error terminating NVDAHelperRemote") - _remoteLib=None + _remoteLib = None if _remoteLoaderAMD64: _remoteLoaderAMD64.terminate() _remoteLoaderAMD64 = None if _remoteLoaderARM64: _remoteLoaderARM64.terminate() _remoteLoaderARM64 = None - generateBeep=None - VBuf_getTextInRange=None + generateBeep = None + VBuf_getTextInRange = None localLib.nvdaHelperLocal_terminate() - localLib=None + localLib = None + + +LOCAL_WIN10_DLL_PATH = os.path.join(versionedLibPath, "nvdaHelperLocalWin10.dll") + -LOCAL_WIN10_DLL_PATH = os.path.join(versionedLibPath,"nvdaHelperLocalWin10.dll") def getHelperLocalWin10Dll(): """Get a ctypes WinDLL instance for the nvdaHelperLocalWin10 dll. This is a C++/CX dll used to provide access to certain UWP functionality. """ return windll[LOCAL_WIN10_DLL_PATH] + def bstrReturn(address): """Handle a BSTR returned from a ctypes function call. This includes freeing the memory. diff --git a/source/NVDAObjects/IAccessible/MSHTML.py b/source/NVDAObjects/IAccessible/MSHTML.py index 8d097d63718..d04285ad65a 100644 --- a/source/NVDAObjects/IAccessible/MSHTML.py +++ b/source/NVDAObjects/IAccessible/MSHTML.py @@ -1,8 +1,8 @@ -#NVDAObjects/MSHTML.py -#A part of NonVisual Desktop Access (NVDA) -#Copyright (C) 2006-2015 NV Access Limited, Aleksey Sadovoy -#This file is covered by the GNU General Public License. -#See the file COPYING for more details. +# NVDAObjects/MSHTML.py +# A part of NonVisual Desktop Access (NVDA) +# Copyright (C) 2006-2015 NV Access Limited, Aleksey Sadovoy +# This file is covered by the GNU General Public License. +# See the file COPYING for more details. from comtypes import COMError import comtypes.client @@ -29,115 +29,122 @@ from locationHelper import RectLTRB from typing import Dict -IID_IHTMLElement=comtypes.GUID('{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}') +IID_IHTMLElement = comtypes.GUID("{3050F1FF-98B5-11CF-BB82-00AA00BDCE0B}") -class UIAMSHTMLTextInfo(UIATextInfo): +class UIAMSHTMLTextInfo(UIATextInfo): # #4174: MSHTML's UIAutomation implementation does not handle the insertion point at the end of the control correcly. # Therefore get around it by detecting when the TextInfo is instanciated on it, and ensure that expand and move do the expected thing. - - _atEndOfStory=False - def __init__(self,obj,position,_rangeObj=None): - super(UIAMSHTMLTextInfo,self).__init__(obj,position,_rangeObj) - if position==textInfos.POSITION_CARET: - tempRange=self._rangeObj.clone() + _atEndOfStory = False + + def __init__(self, obj, position, _rangeObj=None): + super(UIAMSHTMLTextInfo, self).__init__(obj, position, _rangeObj) + if position == textInfos.POSITION_CARET: + tempRange = self._rangeObj.clone() tempRange.ExpandToEnclosingUnit(UIAHandler.TextUnit_Character) - if self._rangeObj.CompareEndpoints(UIAHandler.TextPatternRangeEndpoint_Start,tempRange,UIAHandler.TextPatternRangeEndpoint_Start)>0: - self._atEndOfStory=True + if ( + self._rangeObj.CompareEndpoints( + UIAHandler.TextPatternRangeEndpoint_Start, + tempRange, + UIAHandler.TextPatternRangeEndpoint_Start, + ) + > 0 + ): + self._atEndOfStory = True def copy(self): - info=super(UIAMSHTMLTextInfo,self).copy() - info._atEndOfStory=self._atEndOfStory + info = super(UIAMSHTMLTextInfo, self).copy() + info._atEndOfStory = self._atEndOfStory return info - def expand(self,unit): - if unit in (textInfos.UNIT_CHARACTER,textInfos.UNIT_WORD) and self._atEndOfStory: + def expand(self, unit): + if unit in (textInfos.UNIT_CHARACTER, textInfos.UNIT_WORD) and self._atEndOfStory: return - self._atEndOfStory=False - return super(UIAMSHTMLTextInfo,self).expand(unit) + self._atEndOfStory = False + return super(UIAMSHTMLTextInfo, self).expand(unit) - def move(self,unit,direction,endPoint=None): - if direction==0: + def move(self, unit, direction, endPoint=None): + if direction == 0: return 0 - if self._atEndOfStory and direction<0: - direction+=1 - self._atEndOfStory=False - if direction==0: + if self._atEndOfStory and direction < 0: + direction += 1 + self._atEndOfStory = False + if direction == 0: return -1 - return super(UIAMSHTMLTextInfo,self).move(unit,direction,endPoint=endPoint) + return super(UIAMSHTMLTextInfo, self).move(unit, direction, endPoint=endPoint) -class HTMLAttribCache(object): - def __init__(self,HTMLNode): - self.HTMLNode=HTMLNode - self.cache={} - self.containsCache={} +class HTMLAttribCache(object): + def __init__(self, HTMLNode): + self.HTMLNode = HTMLNode + self.cache = {} + self.containsCache = {} - def __getitem__(self,item): + def __getitem__(self, item): try: return self.cache[item] except LookupError: pass try: - value=self.HTMLNode.getAttribute(item) - except (COMError,NameError): - value=None - self.cache[item]=value + value = self.HTMLNode.getAttribute(item) + except (COMError, NameError): + value = None + self.cache[item] = value return value - def __contains__(self,item): + def __contains__(self, item): try: return self.containsCache[item] except LookupError: pass - contains=item in self.cache + contains = item in self.cache if not contains: try: - contains=self.HTMLNode.hasAttribute(item) - except (COMError,NameError): + contains = self.HTMLNode.hasAttribute(item) + except (COMError, NameError): pass - self.containsCache[item]=contains + self.containsCache[item] = contains return contains nodeNamesToNVDARoles: Dict[str, int] = { - "FRAME":controlTypes.Role.FRAME, - "IFRAME":controlTypes.Role.INTERNALFRAME, - "FRAMESET":controlTypes.Role.DOCUMENT, - "BODY":controlTypes.Role.DOCUMENT, - "TH":controlTypes.Role.TABLECELL, - "IMG":controlTypes.Role.GRAPHIC, - "A":controlTypes.Role.LINK, - "LABEL":controlTypes.Role.LABEL, - "#text":controlTypes.Role.STATICTEXT, - "#TEXT":controlTypes.Role.STATICTEXT, - "H1":controlTypes.Role.HEADING, - "H2":controlTypes.Role.HEADING, - "H3":controlTypes.Role.HEADING, - "H4":controlTypes.Role.HEADING, - "H5":controlTypes.Role.HEADING, - "H6":controlTypes.Role.HEADING, - "DIV":controlTypes.Role.SECTION, - "P":controlTypes.Role.PARAGRAPH, - "FORM":controlTypes.Role.FORM, - "UL":controlTypes.Role.LIST, - "OL":controlTypes.Role.LIST, - "DL":controlTypes.Role.LIST, - "LI":controlTypes.Role.LISTITEM, - "DD":controlTypes.Role.LISTITEM, - "DT":controlTypes.Role.LISTITEM, - "TR":controlTypes.Role.TABLEROW, - "THEAD":controlTypes.Role.TABLEHEADER, - "TBODY":controlTypes.Role.TABLEBODY, - "HR":controlTypes.Role.SEPARATOR, - "OBJECT":controlTypes.Role.EMBEDDEDOBJECT, - "APPLET":controlTypes.Role.EMBEDDEDOBJECT, - "EMBED":controlTypes.Role.EMBEDDEDOBJECT, + "FRAME": controlTypes.Role.FRAME, + "IFRAME": controlTypes.Role.INTERNALFRAME, + "FRAMESET": controlTypes.Role.DOCUMENT, + "BODY": controlTypes.Role.DOCUMENT, + "TH": controlTypes.Role.TABLECELL, + "IMG": controlTypes.Role.GRAPHIC, + "A": controlTypes.Role.LINK, + "LABEL": controlTypes.Role.LABEL, + "#text": controlTypes.Role.STATICTEXT, + "#TEXT": controlTypes.Role.STATICTEXT, + "H1": controlTypes.Role.HEADING, + "H2": controlTypes.Role.HEADING, + "H3": controlTypes.Role.HEADING, + "H4": controlTypes.Role.HEADING, + "H5": controlTypes.Role.HEADING, + "H6": controlTypes.Role.HEADING, + "DIV": controlTypes.Role.SECTION, + "P": controlTypes.Role.PARAGRAPH, + "FORM": controlTypes.Role.FORM, + "UL": controlTypes.Role.LIST, + "OL": controlTypes.Role.LIST, + "DL": controlTypes.Role.LIST, + "LI": controlTypes.Role.LISTITEM, + "DD": controlTypes.Role.LISTITEM, + "DT": controlTypes.Role.LISTITEM, + "TR": controlTypes.Role.TABLEROW, + "THEAD": controlTypes.Role.TABLEHEADER, + "TBODY": controlTypes.Role.TABLEBODY, + "HR": controlTypes.Role.SEPARATOR, + "OBJECT": controlTypes.Role.EMBEDDEDOBJECT, + "APPLET": controlTypes.Role.EMBEDDEDOBJECT, + "EMBED": controlTypes.Role.EMBEDDEDOBJECT, "FIELDSET": controlTypes.Role.GROUPING, - "OPTION":controlTypes.Role.LISTITEM, - "BLOCKQUOTE":controlTypes.Role.BLOCKQUOTE, - "MATH":controlTypes.Role.MATH, + "OPTION": controlTypes.Role.LISTITEM, + "BLOCKQUOTE": controlTypes.Role.BLOCKQUOTE, + "MATH": controlTypes.Role.MATH, "NAV": controlTypes.Role.LANDMARK, "HEADER": controlTypes.Role.LANDMARK, "MAIN": controlTypes.Role.LANDMARK, @@ -153,31 +160,33 @@ def __contains__(self,item): def getZoomFactorsFromHTMLDocument(HTMLDocument): try: - scr=HTMLDocument.parentWindow.screen - except (COMError,NameError,AttributeError): + scr = HTMLDocument.parentWindow.screen + except (COMError, NameError, AttributeError): log.debugWarning("no screen object for MSHTML document") - return (1,1) + return (1, 1) try: - devX=float(scr.deviceXDPI) - devY=float(scr.deviceYDPI) - logX=float(scr.logicalXDPI) - logY=float(scr.logicalYDPI) - except (COMError,NameError,AttributeError,TypeError): + devX = float(scr.deviceXDPI) + devY = float(scr.deviceYDPI) + logX = float(scr.logicalXDPI) + logY = float(scr.logicalYDPI) + except (COMError, NameError, AttributeError, TypeError): log.debugWarning("unable to fetch DPI factors") - return (1,1) + return (1, 1) return (devX // logX, devY // logY) + def IAccessibleFromHTMLNode(HTMLNode): try: - s=HTMLNode.QueryInterface(IServiceProvider) - return s.QueryService(oleacc.IAccessible._iid_,oleacc.IAccessible) + s = HTMLNode.QueryInterface(IServiceProvider) + return s.QueryService(oleacc.IAccessible._iid_, oleacc.IAccessible) except COMError: raise NotImplementedError + def HTMLNodeFromIAccessible(IAccessibleObject): try: - s=IAccessibleObject.QueryInterface(IServiceProvider) - i=s.QueryService(IID_IHTMLElement,comtypes.automation.IDispatch) + s = IAccessibleObject.QueryInterface(IServiceProvider) + i = s.QueryService(IID_IHTMLElement, comtypes.automation.IDispatch) if not i: # QueryService should fail if IHTMLElement is not supported, but some applications misbehave and return a null COM pointer. raise NotImplementedError @@ -185,219 +194,229 @@ def HTMLNodeFromIAccessible(IAccessibleObject): except COMError: raise NotImplementedError -def locateHTMLElementByID(document,ID): + +def locateHTMLElementByID(document, ID): try: - elements=document.getElementsByName(ID) + elements = document.getElementsByName(ID) if elements is not None: - element=elements.item(0) - else: #probably IE 10 in standards mode (#3151) + element = elements.item(0) + else: # probably IE 10 in standards mode (#3151) try: - element=document.all.item(ID) + element = document.all.item(ID) except: # noqa: E722 - element=None - if element is None: #getElementsByName doesn't return element with specified ID in IE11 (#5784) + element = None + if element is None: # getElementsByName doesn't return element with specified ID in IE11 (#5784) try: - element=document.getElementByID(ID) + element = document.getElementByID(ID) except COMError as e: - log.debugWarning("document.getElementByID failed with COMError %s"%e) - element=None + log.debugWarning("document.getElementByID failed with COMError %s" % e) + element = None except COMError as e: - log.debugWarning("document.getElementsByName failed with COMError %s"%e) - element=None + log.debugWarning("document.getElementsByName failed with COMError %s" % e) + element = None if element: return element try: - nodeName=document.body.nodeName + nodeName = document.body.nodeName except COMError as e: - log.debugWarning("document.body.nodeName failed with COMError %s"%e) + log.debugWarning("document.body.nodeName failed with COMError %s" % e) return None if nodeName: - nodeName=nodeName.upper() - if nodeName=="FRAMESET": - tag="frame" + nodeName = nodeName.upper() + if nodeName == "FRAMESET": + tag = "frame" else: - tag="iframe" + tag = "iframe" try: - frames=document.getElementsByTagName(tag) + frames = document.getElementsByTagName(tag) except COMError as e: - log.debugWarning("document.getElementsByTagName failed with COMError %s"%e) + log.debugWarning("document.getElementsByTagName failed with COMError %s" % e) return None - if not frames: #frames can be None in IE 10 + if not frames: # frames can be None in IE 10 return None for frame in frames: - childElement=getChildHTMLNodeFromFrame(frame) + childElement = getChildHTMLNodeFromFrame(frame) if not childElement: continue - childElement=locateHTMLElementByID(childElement.document,ID) - if not childElement: continue # noqa: E701 + childElement = locateHTMLElementByID(childElement.document, ID) + if not childElement: + continue # noqa: E701 return childElement + def getChildHTMLNodeFromFrame(frame): try: - pacc=IAccessibleFromHTMLNode(frame) + pacc = IAccessibleFromHTMLNode(frame) except NotImplementedError: # #1569: It's not possible to get an IAccessible from frames marked with an ARIA role of presentation. # In this case, just skip this frame. return - res=IAccessibleHandler.accChild(pacc,1) - if not res: return # noqa: E701 + res = IAccessibleHandler.accChild(pacc, 1) + if not res: + return # noqa: E701 return HTMLNodeFromIAccessible(res[0]) -class MSHTMLTextInfo(textInfos.TextInfo): - def _expandToLine(self,textRange): - #Try to calculate the line range by finding screen coordinates and using moveToPoint - parent=textRange.parentElement() - if not parent.isMultiline: #fastest solution for single line edits () +class MSHTMLTextInfo(textInfos.TextInfo): + def _expandToLine(self, textRange): + # Try to calculate the line range by finding screen coordinates and using moveToPoint + parent = textRange.parentElement() + if not parent.isMultiline: # fastest solution for single line edits () textRange.expand("textEdit") return - parentRect=parent.getBoundingClientRect() - #This can be simplified when comtypes is fixed - lineTop=comtypes.client.dynamic._Dispatch(textRange._comobj).offsetTop - lineLeft=parentRect.left+parent.clientLeft - #editable documents have a different right most boundary to