Skip to content
Open
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 21 additions & 11 deletions nvdaHelper/archBuild_sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -67,21 +67,23 @@ def COMProxyDllBuilder(env, target, source, proxyClsid):

env.AddMethod(COMProxyDllBuilder, "COMProxyDll")

# We only support compiling with MSVC 14.2 (2019) or newer
if not env.get("MSVC_VERSION") or tuple(map(int, env.get("MSVC_VERSION").split("."))) < (14, 2):
raise RuntimeError("Visual C++ 14.2 (Visual Studio 2019) or newer not found")
# We only support compiling with MSVC 14.3 (2022) or newer
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please note this in changes for developers

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like this was already noted in the changes for 2024.1. I think this was an oversight or something.

if not env.get("MSVC_VERSION") or tuple(map(int, env.get("MSVC_VERSION").split("."))) < (14, 3):
raise RuntimeError("Visual C++ 14.3 (Visual Studio 2022) or newer not found")

PYTHON_PLATFORM = sysconfig.get_platform()
TARGET_ARCH = env["TARGET_ARCH"]
isArm64EC = env.get("isArm64EC", False)


isNVDACoreArch = (
(PYTHON_PLATFORM == "win32" and TARGET_ARCH == "x86")
or (PYTHON_PLATFORM == "win-amd64" and TARGET_ARCH == "x86_64")
or (PYTHON_PLATFORM == "win-arm64" and TARGET_ARCH == "arm64")
)


isNVDAHelperLocalArch = isNVDACoreArch or (
PYTHON_PLATFORM == "win-amd64" and TARGET_ARCH == "arm64" and isArm64EC
)
debug = env["nvdaHelperDebugFlags"]
release = env["release"]
signExec = env["signExec"] if (bool(env["certFile"]) ^ bool(env["apiSigningToken"])) else None
Expand Down Expand Up @@ -123,7 +125,7 @@ env.Append(
if TARGET_ARCH == "x86_64":
env.Append(MIDLFLAGS="/x64")
elif TARGET_ARCH == "arm64":
env.Append(MIDLFLAGS="/arm64")
env.Append(MIDLFLAGS="/x64" if isArm64EC else "/arm64")
else:
env.Append(MIDLFLAGS="/win32")

Expand Down Expand Up @@ -159,6 +161,11 @@ env.Append(
]
)

if isArm64EC:
env.Append(CCFLAGS="/arm64EC")
env.Append(LINKFLAGS="/machine:arm64ec")
env.Append(ARFLAGS="/machine:arm64ec")

if "debugCRT" in debug:
env.Append(CCFLAGS=["/MTd"])
else:
Expand Down Expand Up @@ -220,7 +227,7 @@ Export("detoursLib")
apiHookObj = env.Object("apiHook", "common/apiHook.cpp")
Export("apiHookObj")

if isNVDACoreArch:
if isNVDAHelperLocalArch:
localLib = env.SConscript("local/sconscript")
Export("localLib")
if signExec:
Expand Down Expand Up @@ -249,11 +256,14 @@ if signExec:
env.AddPostAction(remoteLib[0], [signExec])
env.Install(libInstallDir, remoteLib)

remoteLoaderProgram = env.SConscript("remoteLoader/sconscript")
if signExec:
env.AddPostAction(remoteLoaderProgram, [signExec])
env.Install(libInstallDir, remoteLoaderProgram)
if not isNVDAHelperLocalArch:
remoteLoaderProgram = env.SConscript("remoteLoader/sconscript")
if signExec:
env.AddPostAction(remoteLoaderProgram, [signExec])
env.Install(libInstallDir, remoteLoaderProgram)

if isNVDACoreArch:
thirdPartyEnv.SConscript("espeak/sconscript")
thirdPartyEnv.SConscript("liblouis/sconscript")
thirdPartyEnv.SConscript("javaAccessBridge/sconscript")
thirdPartyEnv.SConscript("uwp/sconscript")
28 changes: 28 additions & 0 deletions nvdaHelper/javaAccessBridge/sconscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2019-2025 NV Access Limited, Leonard de Ruijter
# This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license.
# For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt

Import(
[
"thirdPartyEnv",
"sourceDir",
]
)

env: Environment = thirdPartyEnv
TARGET_ARCH = env["TARGET_ARCH"]
# Copy in the Java Access Bridge dependency.
match TARGET_ARCH:
case "x86":
jabDllName = "windowsaccessbridge-32.dll"
case "x86_64":
jabDllName = "windowsaccessbridge-64.dll"
case _:
print(f"No Java Access Bridge support for NVDA core architecture {TARGET_ARCH!r}")
jabDllName = None
if jabDllName:
env.Command(
sourceDir.File("windowsaccessbridge.dll"), env.Dir("#include/javaAccessBridge32").File(jabDllName), Copy("$TARGET", "$SOURCE")
)

33 changes: 0 additions & 33 deletions nvdaHelper/localWin10/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,6 @@
# http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
###

import os
import glob

from SCons.Tool.MSCommon.vc import find_vc_pdir

Import(
"env",
"sourceDir",
Expand All @@ -41,32 +36,4 @@ localWin10Lib = env.SharedLibrary(
], # noqa: F821
)

# UWP dlls can only be dynamically linked with the CRT,
# but some systems might not have this version of the CRT.
# Therefore, we must include it.
# VS keeps changing the path to reflect the latest major.minor.build version which we canot easily find out.
# Therefore Search these versioned directories from newest to oldest to collect all the files we need.
msvc = env.get("MSVC_VERSION")
vcRedistDirs = glob.glob(
os.path.join(
find_vc_pdir(msvc, env), rf"Redist\MSVC\{msvc[:2]}*\x86\Microsoft.VC{msvc.replace('.', '')}.CRT"
)
)
if len(vcRedistDirs) == 0:
raise RuntimeError(
"Could not locate vc redistributables. Perhaps the Universal Windows Platform component in visual Studio is not installed"
)
vcRedistDirs.sort(reverse=True)
for fn in ("msvcp140.dll", "vccorlib140.dll", "vcruntime140.dll"):
for vcRedistDir in vcRedistDirs:
path = os.path.join(vcRedistDir, fn)
if os.path.isfile(path):
env.Install(sourceDir, path)
break
else:
raise RuntimeError(
"Could not locate %s. Perhaps the Universal Windows Platform component in visual Studio is not installed"
% fn
)

Return(["localWin10Lib"])
46 changes: 46 additions & 0 deletions nvdaHelper/uwp/sconscript
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# A part of NonVisual Desktop Access (NVDA)
# Copyright (C) 2017-2025 NV Access Limited, Leonard de Ruijter
# This file may be used under the terms of the GNU General Public License, version 2 or later, as modified by the NVDA license.
# For full terms and any additional permissions, see the NVDA license file: https://github.com/nvaccess/nvda/blob/master/copying.txt

import glob
import os

from SCons.Tool.MSCommon.vc import find_vc_pdir

Import(
[
"thirdPartyEnv",
"sourceDir",
]
)

env: Environment = thirdPartyEnv

# UWP dlls can only be dynamically linked with the CRT,
# but some systems might not have this version of the CRT.
# Therefore, we must include it.
# VS keeps changing the path to reflect the latest major.minor.build version which we canot easily find out.
# Therefore Search these versioned directories from newest to oldest to collect all the files we need.
msvc = env.get("MSVC_VERSION")
vcRedistDirs = glob.glob(
os.path.join(
find_vc_pdir(msvc, env), rf"Redist\MSVC\{msvc[:2]}*\x86\Microsoft.VC{msvc.replace('.', '')}.CRT"
)
)
if len(vcRedistDirs) == 0:
raise RuntimeError(
"Could not locate vc redistributables. Perhaps the Universal Windows Platform component in visual Studio is not installed"
)
vcRedistDirs.sort(reverse=True)
for fn in ("msvcp140.dll", "vccorlib140.dll", "vcruntime140.dll"):
for vcRedistDir in vcRedistDirs:
path = os.path.join(vcRedistDir, fn)
if os.path.isfile(path):
env.Install(sourceDir, path)
break
else:
raise RuntimeError(
"Could not locate %s. Perhaps the Universal Windows Platform component in visual Studio is not installed"
% fn
)
12 changes: 12 additions & 0 deletions sconstruct
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,8 @@ sourceLibDir64 = sourceDir.Dir("lib/x64")
Export("sourceLibDir64")
sourceLibDirArm64 = sourceDir.Dir("lib/arm64")
Export("sourceLibDirArm64")
sourceLibDirArm64EC = sourceDir.Dir("lib/arm64ec")
Export("sourceLibDirArm64EC")
buildDir = Dir("build")
outFilePrefix = "nvda{type}_{version}".format(type="" if release else "_snapshot", version=version)
Export("outFilePrefix")
Expand Down Expand Up @@ -236,10 +238,13 @@ archTools = ["default", "midl", "msrpc"]
env32 = env.Clone(TARGET_ARCH="x86", tools=archTools)
env64 = env.Clone(TARGET_ARCH="x86_64", tools=archTools)
envArm64 = env.Clone(TARGET_ARCH="arm64", tools=archTools)
envArm64EC = envArm64.Clone()
envArm64EC["isArm64EC"] = True
# Hack around odd bug where some tool [after] msvc states that static and shared objects are different
env32["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
env64["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
envArm64["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1
envArm64EC["STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME"] = 1

if platform.architecture()[0].startswith("64"):
env = env64
Expand All @@ -265,9 +270,11 @@ resFile = env.RES(
env32["projectResFile"] = resFile
env64["projectResFile"] = resFile
envArm64["projectResFile"] = resFile
envArm64EC["projectResFile"] = resFile

# Fill sourceDir with anything provided for it by miscDeps
env.recursiveCopy(sourceDir, Dir("miscdeps/source"))

# Copy in some other dependencies.
for targetDir, jabDll in (
(sourceLibDir, "windowsaccessbridge-32.dll"),
Expand Down Expand Up @@ -297,6 +304,11 @@ envArm64.SConscript(
exports={"env": envArm64, "clientInstallDir": clientDir.Dir("arm64"), "libInstallDir": sourceLibDirArm64},
variant_dir="build/arm64",
)
envArm64EC.SConscript(
"nvdaHelper/archBuild_sconscript",
exports={"env": envArm64EC, "clientInstallDir": clientDir.Dir("arm64ec"), "libInstallDir": sourceLibDirArm64EC},
variant_dir="build/arm64ec",
)

# Allow all NVDA's gettext po files to be compiled in source/locale
for po in env.Glob(sourceDir.path + "/locale/*/lc_messages/*.po"):
Expand Down
5 changes: 3 additions & 2 deletions source/JABHandler.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@
import NVDAObjects.JAB
import core
import textUtils
import NVDAState
import config
from utils.security import isRunningOnSecureDesktop

Expand Down Expand Up @@ -1136,7 +1135,9 @@ def enableBridge():
def initialize():
global bridgeDll, isRunning
try:
bridgeDll = cdll.LoadLibrary(NVDAState.ReadPaths.javaAccessBridgeDLL)
bridgeDll = cdll.LoadLibrary(
os.path.join("windowsaccessbridge.dll"),
)
except WindowsError:
raise NotImplementedError("dll not available")
_fixBridgeFuncs()
Expand Down
2 changes: 1 addition & 1 deletion source/NVDAHelper/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -876,7 +876,7 @@ def initialize() -> None:
# Windows on ARM from Windows 11 supports running AMD64 apps.
# Thus we also need to be able to inject into these.
if winVersion.getWinVer() >= winVersion.WIN11:
_remoteLoaderAMD64 = _RemoteLoader(ReadPaths.versionedLibARM64Path)
_remoteLoaderAMD64 = _RemoteLoader(ReadPaths.versionedLibAMD64Path)


def terminate():
Expand Down
17 changes: 14 additions & 3 deletions source/NVDAState.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
import buildVersion
import globalVars

from functools import cached_property


class _WritePaths:
@property
Expand Down Expand Up @@ -109,15 +111,24 @@ def versionedLibPath(self) -> str:
def versionedLibX86Path(self) -> str:
return os.path.join(self.versionedLibPath, "x86")

@property
@cached_property
def versionedLibAMD64Path(self) -> str:
return os.path.join(self.versionedLibPath, "x64")
import winVersion

arch = winVersion.getWinVer().processorArchitecture
return os.path.join(
self.versionedLibPath,
(
# On ARM64 Windows, we use arm64ec libraries for interop with x64 code.
"arm64ec" if arch == "ARM64" else "x64"
),
)

@property
def versionedLibARM64Path(self) -> str:
return os.path.join(self.versionedLibPath, "arm64")

@property
@cached_property
def coreArchLibPath(self) -> str:
match sysconfig.get_platform():
case "win-amd64":
Expand Down
1 change: 1 addition & 0 deletions source/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ def _genManifestTemplate(shouldHaveUIAccess: bool) -> tuple[int, int, bytes]:
("lib/%s/x86" % version, glob("lib/x86/*.dll") + glob("lib/x86/*.exe")),
("lib/%s/x64" % version, glob("lib/x64/*.dll") + glob("lib/x64/*.exe")),
("lib/%s/arm64" % version, glob("lib/arm64/*.dll") + glob("lib/arm64/*.exe")),
("lib/%s/arm64ec" % version, glob("lib/arm64ec/*.dll") + glob("lib/arm64ec/*.exe")),
("waves", glob("waves/*.wav")),
("images", glob("images/*.ico")),
("fonts", glob("fonts/*.ttf")),
Expand Down
3 changes: 3 additions & 0 deletions user_docs/en/changes.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ We recommend updating to Windows 11, or when that's not possible, to the latest
* Added the possibility to report when multiple items can be selected in a list control.
This can be enabled using the "Report when lists support multiple selection" setting in NVDA's object presentation settings. (#18365 @LeonarddeR)
* In Visual Studio Code, the status bar is now reported when using the standard `NVDA+end` (desktop) / `NVDA+shift+end` (laptop) gesture. (#11064, @codeofdusk)
* Performance improvements on ARM64 systems, such as with Qualcomm processors. (#18570, @leonarddeR)

### Changes

Expand All @@ -37,6 +38,8 @@ Add-ons will need to be re-tested and have their manifest updated.
* The changelog should document changes between previous and latest add-on versions.
* Updated components
* Licensecheck has been updated to 2025.1 (#18728, @bramd)
* X64 NVDAHelper libraries are now also build for the [ARM64EC architecture](https://learn.microsoft.com/en-us/windows/arm/arm64ec).
On ARm64 machines with Windows 11, these ARM64EC libraries are loaded instead of their X64 equivalents. (#18570, @leonarddeR)

#### API Breaking Changes

Expand Down