-
-
Notifications
You must be signed in to change notification settings - Fork 634
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Use modern DPI awareness settings (#13254)
Fixes #13370 Fixes #6722 Fixes #3875 Fixes #12070 Fixes #7083 Fixes #7915 Likely fixes #9531, otherwise close as stale/can't reproduce ### Summary of the issue: When DPI for a monitor is not set to 100%, or when using multiple monitors with different DPI settings, NVDA would: - misplace highlight frames - have inaccurate mouse tracking - have inaccurate touch screen interaction NVDA currently sets the DPI awareness via a Windows API call introduced in Windows Vista. It is recommended to set DPI through the app manifest, rather than Windows API calls where possible. Newer settings for DPI awareness have been introduced since Windows Vista. Windows 8 introduced multiple monitor DPI awareness. Windows 10 introduced a richer version of multiple monitor DPI awareness. ### Description of how this pull request fixes the issue: Background docs: https://docs.microsoft.com/en-us/windows/win32/hidpi/high-dpi-desktop-application-development-on-windows#per-monitor-and-per-monitor-v2-dpi-awareness When running as an executable, NVDA sets DPI awareness via the app manifest. When running through source, NVDA sets DPI awareness via Windows API calls. The most modern method available is used to set DPI awareness. - For Windows 7, DPI awareness is unlikely to improve. There may be fixes from setting it via the app manifest instead of via Windows API calls. - For Windows 8 and newer, NVDA has per monitor DPI awareness - For Windows 10 1703 and newer, NVDA has [advanced per monitor DPI awareness](https://docs.microsoft.com/en-us/windows/win32/hidpi/dpi-awareness-context), including: - Child window DPI change notifications - Scaling of non-client area - All windows will automatically have their non-client area drawn in a DPI sensitive fashion. Calls to [EnableNonClientDpiScaling](https://docs.microsoft.com/en-us/windows/desktop/api/Winuser/nf-winuser-enablenonclientdpiscaling) are unnecessary. - Scaling of Win32 menus - All NTUSER menus created in Per Monitor v2 contexts will be scaling in a per-monitor fashion. - Dialog Scaling - Win32 dialogs created in Per Monitor v2 contexts will automatically respond to DPI changes. - Improved scaling of comctl32 controls - Various comctl32 controls have improved DPI scaling behavior in Per Monitor v2 contexts. - Improved theming behavior - UxTheme handles opened in the context of a Per Monitor v2 window will operate in terms of the DPI associated with that window.
- Loading branch information
1 parent
4721336
commit 28b4756
Showing
6 changed files
with
194 additions
and
50 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
<assembly | ||
xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0" | ||
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3" | ||
> | ||
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
<security> | ||
<requestedPrivileges> | ||
<requestedExecutionLevel | ||
level="asInvoker" | ||
uiAccess="%(uiAccess)s" | ||
/> | ||
</requestedPrivileges> | ||
</security> | ||
</trustInfo> | ||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1"> | ||
<application> | ||
<!-- Windows 7 --> | ||
<supportedOS | ||
Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" | ||
/> | ||
<!-- Windows 8 --> | ||
<supportedOS | ||
Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" | ||
/> | ||
<!-- Windows 8.1 --> | ||
<supportedOS | ||
Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" | ||
/> | ||
<!-- Windows 10/11 --> | ||
<supportedOS | ||
Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" | ||
/> | ||
</application> | ||
</compatibility> | ||
<asmv3:application> | ||
<asmv3:windowsSettings> | ||
<dpiAware | ||
xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings" | ||
> | ||
true/pm | ||
</dpiAware> | ||
<dpiAwareness | ||
xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings" | ||
> | ||
PerMonitorV2, PerMonitor | ||
</dpiAwareness> | ||
</asmv3:windowsSettings> | ||
</asmv3:application> | ||
</assembly> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
# A part of NonVisual Desktop Access (NVDA) | ||
# Copyright (C) 2022 NV Access Limited | ||
# This file is covered by the GNU General Public License. | ||
# See the file COPYING for more details. | ||
|
||
import enum | ||
|
||
|
||
class HResult(enum.IntEnum): | ||
# https://docs.microsoft.com/en-us/windows/win32/seccrypto/common-hresult-values | ||
S_OK = 0x00000000 | ||
E_ACCESS_DENIED = 0x80070005 # E_ACCESSDENIED | ||
E_INVALID_ARG = 0x80070057 # E_INVALIDARG | ||
|
||
|
||
class SystemErrorCodes(enum.IntEnum): | ||
# https://docs.microsoft.com/en-us/windows/win32/debug/system-error-codes--0-499- | ||
ACCESS_DENIED = 0x5 | ||
INVALID_PARAMETER = 0x57 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
# A part of NonVisual Desktop Access (NVDA) | ||
# Copyright (C) 2022 NV Access Limited | ||
# This file is covered by the GNU General Public License. | ||
# See the file COPYING for more details. | ||
|
||
import ctypes | ||
|
||
from logHandler import log | ||
|
||
from .constants import ( | ||
HResult, | ||
SystemErrorCodes, | ||
) | ||
|
||
|
||
def setDPIAwareness() -> None: | ||
""" | ||
Different versions of Windows inconsistently support different styles of DPI Awareness. | ||
This function attempts to set process DPI awareness using the most modern Windows API method available. | ||
Only call this function once per instance of NVDA. | ||
Only call this function when running from source. | ||
It is recommended that you set the process-default DPI awareness via application manifest. | ||
Setting the process-default DPI awareness via these API calls can lead to unexpected application behavior. | ||
""" | ||
# Support is inconsistent across versions of Windows, so try/excepts are used rather than explicit | ||
# version checks. | ||
# https://docs.microsoft.com/en-us/windows/win32/hidpi/setting-the-default-dpi-awareness-for-a-process | ||
try: | ||
# An advancement over the original per-monitor DPI awareness mode, | ||
# which enables applications to access new DPI-related scaling behaviors on a per top-level window basis. | ||
# For more information on behaviours, refer to: | ||
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiawarenesscontext | ||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = -4 | ||
# Method introduced in Windows 10 | ||
# https://docs.microsoft.com/en-us/windows/win32/hidpi/dpi-awareness-context | ||
success = ctypes.windll.user32.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2) | ||
except AttributeError: | ||
log.debug("Cannot set DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2") | ||
else: | ||
if success: | ||
return | ||
else: | ||
errorCode = ctypes.GetLastError() | ||
if errorCode == SystemErrorCodes.ACCESS_DENIED: | ||
# The DPI awareness is already set, | ||
# either by calling this API previously or through the application (.exe) manifest. | ||
# This is unexpected as we should only set DPI awareness once. | ||
# NVDA sets DPI awareness from the manifest, | ||
# however this function should only be called when running from source. | ||
log.error("DPI Awareness already set.") | ||
return | ||
elif errorCode == SystemErrorCodes.INVALID_PARAMETER: | ||
log.error("DPI Awareness function provided invalid argument.") | ||
else: | ||
log.error(f"Unknown error setting DPI Awareness. Error code: {errorCode}") | ||
|
||
log.debug("Falling back to older method of setting DPI Awareness") | ||
|
||
try: | ||
# https://docs.microsoft.com/en-us/windows/win32/api/shellscalingapi/nf-shellscalingapi-setprocessdpiawareness | ||
# This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes. | ||
# These processes are not automatically scaled by the system. | ||
PROCESS_PER_MONITOR_DPI_AWARE = 2 | ||
# Method introduced in Windows 8 | ||
hResult = ctypes.windll.shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE) | ||
except AttributeError: | ||
log.debug("Cannot set PROCESS_PER_MONITOR_DPI_AWARE") | ||
else: | ||
if hResult == HResult.S_OK: | ||
return | ||
elif hResult == HResult.E_ACCESS_DENIED: | ||
# The DPI awareness is already set, | ||
# either by calling this API previously or through the application (.exe) manifest. | ||
# This is unexpected as we should only set DPI awareness once. | ||
# NVDA sets DPI awareness from the manifest, | ||
# however this function should only be called when running from source. | ||
log.error("DPI Awareness already set.") | ||
return | ||
elif hResult == HResult.E_INVALID_ARG: | ||
log.error("DPI Awareness function provided invalid argument.") | ||
else: | ||
log.error(f"Unknown error setting DPI Awareness. HRESULT: {hResult}") | ||
|
||
log.debug("Falling back to legacy method of setting DPI Awareness") | ||
|
||
# Method introduced in Windows Vista | ||
# https://docs.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setprocessdpiaware | ||
result = ctypes.windll.user32.SetProcessDPIAware() | ||
if result == 0: | ||
errorCode = ctypes.GetLastError() | ||
log.error(f"Unknown error setting DPI Awareness. Error code: {errorCode}") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters