Skip to content

Commit 82d6a27

Browse files
authored
Merge 2048b83 into f843cbe
2 parents f843cbe + 2048b83 commit 82d6a27

23 files changed

Lines changed: 978 additions & 207 deletions

source/NVDAObjects/behaviors.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,12 @@ def event_valueChange(self):
6363
self.progressValueCache["beep,%d,%d"%(x,y)]=percentage
6464
lastSpeechProgressValue=self.progressValueCache.get("speech,%d,%d"%(x,y),None)
6565
if pbConf["progressBarOutputMode"] in ("speak","both") and (lastSpeechProgressValue is None or abs(percentage-lastSpeechProgressValue)>=pbConf["speechPercentageInterval"]):
66-
queueHandler.queueFunction(queueHandler.eventQueue,speech.speakMessage,_("%d percent")%percentage)
66+
queueHandler.queueFunction(
67+
queueHandler.eventQueue,
68+
speech.speakMessage,
69+
# Translators: This is presented to inform the user of a progress bar percentage.
70+
_("%d percent") % percentage,
71+
)
6772
self.progressValueCache["speech,%d,%d"%(x,y)]=percentage
6873

6974
class Dialog(NVDAObject):

source/bdDetect.py

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
#bdDetect.py
2-
#A part of NonVisual Desktop Access (NVDA)
3-
#This file is covered by the GNU General Public License.
4-
#See the file COPYING for more details.
5-
#Copyright (C) 2013-2017 NV Access Limited
1+
# A part of NonVisual Desktop Access (NVDA)
2+
# This file is covered by the GNU General Public License.
3+
# See the file COPYING for more details.
4+
# Copyright (C) 2013-2022 NV Access Limited
65

76
"""Support for braille display detection.
87
This allows devices to be automatically detected and used when they become available,
@@ -18,19 +17,16 @@
1817
import threading
1918

2019
import typing
21-
import wx
2220
import hwPortUtils
2321
import braille
2422
import winKernel
2523
import winUser
26-
import core
27-
import ctypes
2824
from logHandler import log
2925
import config
30-
import time
3126
import appModuleHandler
3227
from baseObject import AutoPropertyObject
3328
import re
29+
from winAPI import messageWindow
3430

3531

3632
HID_USAGE_PAGE_BRAILLE = 0x41
@@ -256,7 +252,7 @@ def __init__(self, usb=True, bluetooth=True, limitToDevices=None):
256252
self._BgScanApc = winKernel.PAPCFUNC(self._bgScan)
257253
self._btDevsLock = threading.Lock()
258254
self._btDevs = None
259-
core.post_windowMessageReceipt.register(self.handleWindowMessage)
255+
messageWindow.pre_handleWindowMessage.register(self.handleWindowMessage)
260256
appModuleHandler.post_appSwitch.register(self.pollBluetoothDevices)
261257
self._stopEvent = threading.Event()
262258
self._queuedScanLock = threading.Lock()
@@ -390,7 +386,7 @@ def pollBluetoothDevices(self):
390386

391387
def terminate(self):
392388
appModuleHandler.post_appSwitch.unregister(self.pollBluetoothDevices)
393-
core.post_windowMessageReceipt.unregister(self.handleWindowMessage)
389+
messageWindow.pre_handleWindowMessage.unregister(self.handleWindowMessage)
394390
self._stopBgScan()
395391

396392

source/core.py

Lines changed: 17 additions & 132 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from dataclasses import dataclass
1111
from typing import (
12+
Any,
1213
List,
1314
Optional,
1415
)
@@ -30,6 +31,20 @@
3031
import NVDAState
3132

3233

34+
def __getattr__(attrName: str) -> Any:
35+
"""Module level `__getattr__` used to preserve backward compatibility.
36+
"""
37+
if attrName == "post_windowMessageReceipt" and NVDAState._allowDeprecatedAPI():
38+
from winAPI.messageWindow import pre_handleWindowMessage
39+
log.warning(
40+
"core.post_windowMessageReceipt is deprecated, "
41+
"use winAPI.messageWindow.pre_handleWindowMessage instead."
42+
)
43+
return pre_handleWindowMessage
44+
raise AttributeError(f"module {repr(__name__)} has no attribute {repr(attrName)}")
45+
46+
47+
3348
# inform those who want to know that NVDA has finished starting up.
3449
postNvdaStartup = extensionPoints.Action()
3550

@@ -38,19 +53,6 @@
3853
#: The thread identifier of the main thread.
3954
mainThreadId = threading.get_ident()
4055

41-
#: Notifies when a window message has been received by NVDA.
42-
#: This allows components to perform an action when several system events occur,
43-
#: such as power, screen orientation and hardware changes.
44-
#: Handlers are called with three arguments.
45-
#: @param msg: The window message.
46-
#: @type msg: int
47-
#: @param wParam: Additional message information.
48-
#: @type wParam: int
49-
#: @param lParam: Additional message information.
50-
#: @type lParam: int
51-
# TODO: move to winAPI.messageWindow
52-
post_windowMessageReceipt = extensionPoints.Action()
53-
5456
_pump = None
5557
_isPumpPending = False
5658

@@ -553,126 +555,9 @@ def onEndSession(evt):
553555
# the GUI mainloop must be running for this to work so delay it
554556
wx.CallAfter(audioDucking.initialize)
555557

556-
from winAPI.messageWindow import WindowMessage
557-
from winAPI import sessionTracking
558-
import winUser
559-
# #3763: In wxPython 3, the class name of frame windows changed from wxWindowClassNR to wxWindowNR.
560-
# NVDA uses the main frame to check for and quit another instance of NVDA.
561-
# To remain compatible with older versions of NVDA, create our own wxWindowClassNR.
562-
# We don't need to do anything else because wx handles WM_QUIT for all windows.
563-
import windowUtils
564-
# TODO: move to winAPI.messageWindow
565-
class MessageWindow(windowUtils.CustomWindow):
566-
className = u"wxWindowClassNR"
567-
# Windows constants for power / display changes
568-
# TODO: move to winAPI
569-
PBT_APMPOWERSTATUSCHANGE = 0xA
570-
UNKNOWN_BATTERY_STATUS = 0xFF
571-
AC_ONLINE = 0X1
572-
NO_SYSTEM_BATTERY = 0X80
573-
#States for screen orientation
574-
# TODO: move to winAPI, turn to Enum
575-
ORIENTATION_NOT_INITIALIZED = 0
576-
ORIENTATION_PORTRAIT = 1
577-
ORIENTATION_LANDSCAPE = 2
578-
579-
def __init__(self, windowName=None):
580-
super(MessageWindow, self).__init__(windowName)
581-
self.oldBatteryStatus = None
582-
self.orientationStateCache = self.ORIENTATION_NOT_INITIALIZED
583-
self.orientationCoordsCache = (0,0)
584-
self.handlePowerStatusChange()
585-
586-
# Call must be paired with a call to sessionTracking.unregister
587-
self._isSessionTrackingRegistered = sessionTracking.register(self.handle)
588-
589-
def warnIfSessionTrackingNotRegistered(self) -> None:
590-
if self._isSessionTrackingRegistered:
591-
return
592-
failedToRegisterMsg = _(
593-
# Translators: This is a warning to users, shown if NVDA cannot determine if
594-
# Windows is locked.
595-
"NVDA failed to register session tracking. "
596-
"While this instance of NVDA is running, "
597-
"your desktop will not be secure when Windows is locked. "
598-
"Restart NVDA? "
599-
)
600-
if wx.YES == gui.messageBox(
601-
failedToRegisterMsg,
602-
# Translators: This is a warning to users, shown if NVDA cannot determine if
603-
# Windows is locked.
604-
caption=_("NVDA could not start securely."),
605-
style=wx.ICON_ERROR | wx.YES_NO,
606-
):
607-
restart()
608-
609-
def destroy(self):
610-
"""
611-
NVDA must unregister session tracking before destroying the message window.
612-
"""
613-
if self._isSessionTrackingRegistered:
614-
# Requires an active message window and a handle to unregister.
615-
sessionTracking.unregister(self.handle)
616-
super().destroy()
617-
618-
def windowProc(self, hwnd, msg, wParam, lParam):
619-
post_windowMessageReceipt.notify(msg=msg, wParam=wParam, lParam=lParam)
620-
if msg == WindowMessage.POWER_BROADCAST and wParam == self.PBT_APMPOWERSTATUSCHANGE:
621-
self.handlePowerStatusChange()
622-
elif msg == winUser.WM_DISPLAYCHANGE:
623-
self.handleScreenOrientationChange(lParam)
624-
elif msg == WindowMessage.WTS_SESSION_CHANGE:
625-
# If we are receiving WTS_SESSION_CHANGE events, _isSessionTrackingRegistered should be True
626-
sessionTracking.handleSessionChange(sessionTracking.WindowsTrackedSession(wParam), lParam)
627-
628-
def handleScreenOrientationChange(self, lParam):
629-
# TODO: move to winAPI
630-
import ui
631-
# Resolution detection comes from an article found at https://msdn.microsoft.com/en-us/library/ms812142.aspx.
632-
#The low word is the width and hiword is height.
633-
width = winUser.LOWORD(lParam)
634-
height = winUser.HIWORD(lParam)
635-
self.orientationCoordsCache = (width,height)
636-
if width > height:
637-
# If the height and width are the same, it's actually a screen flip, and we do want to alert of those!
638-
if self.orientationStateCache == self.ORIENTATION_LANDSCAPE and self.orientationCoordsCache != (width,height):
639-
return
640-
#Translators: The screen is oriented so that it is wider than it is tall.
641-
ui.message(_("Landscape" ))
642-
self.orientationStateCache = self.ORIENTATION_LANDSCAPE
643-
else:
644-
if self.orientationStateCache == self.ORIENTATION_PORTRAIT and self.orientationCoordsCache != (width,height):
645-
return
646-
#Translators: The screen is oriented in such a way that the height is taller than it is wide.
647-
ui.message(_("Portrait"))
648-
self.orientationStateCache = self.ORIENTATION_PORTRAIT
649-
650-
def handlePowerStatusChange(self):
651-
# TODO: move to winAPI
652-
#Mostly taken from script_say_battery_status, but modified.
653-
import ui
654-
import winKernel
655-
sps = winKernel.SYSTEM_POWER_STATUS()
656-
if not winKernel.GetSystemPowerStatus(sps) or sps.BatteryFlag is self.UNKNOWN_BATTERY_STATUS:
657-
return
658-
if sps.BatteryFlag & self.NO_SYSTEM_BATTERY:
659-
return
660-
if self.oldBatteryStatus is None:
661-
#Just initializing the cache, do not report anything.
662-
self.oldBatteryStatus = sps.ACLineStatus
663-
return
664-
if sps.ACLineStatus == self.oldBatteryStatus:
665-
#Sometimes, this double fires. This also fires when the battery level decreases by 3%.
666-
return
667-
self.oldBatteryStatus = sps.ACLineStatus
668-
if sps.ACLineStatus & self.AC_ONLINE:
669-
#Translators: Reported when the battery is plugged in, and now is charging.
670-
ui.message(_("Charging battery. %d percent") % sps.BatteryLifePercent)
671-
else:
672-
#Translators: Reported when the battery is no longer plugged in, and now is not charging.
673-
ui.message(_("Not charging battery. %d percent") %sps.BatteryLifePercent)
558+
from winAPI.messageWindow import _MessageWindow
674559
import versionInfo
675-
messageWindow = MessageWindow(versionInfo.name)
560+
messageWindow = _MessageWindow(versionInfo.name)
676561

677562
# initialize wxpython localization support
678563
wxLocaleObj = wx.Locale()

source/globalCommands.py

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@
4646
import characterProcessing
4747
from baseObject import ScriptableObject
4848
import core
49+
from winAPI._powerTracking import reportCurrentBatteryStatus
4950
import winVersion
5051
from base64 import b16encode
5152
import vision
@@ -2515,26 +2516,8 @@ def script_toggleAutoFocusFocusableElements(self,gesture):
25152516
category=SCRCAT_SYSTEM,
25162517
gesture="kb:NVDA+shift+b"
25172518
)
2518-
def script_say_battery_status(self,gesture):
2519-
UNKNOWN_BATTERY_STATUS = 0xFF
2520-
AC_ONLINE = 0X1
2521-
NO_SYSTEM_BATTERY = 0X80
2522-
sps = winKernel.SYSTEM_POWER_STATUS()
2523-
if not winKernel.GetSystemPowerStatus(sps) or sps.BatteryFlag is UNKNOWN_BATTERY_STATUS:
2524-
log.error("error accessing system power status")
2525-
return
2526-
if sps.BatteryFlag & NO_SYSTEM_BATTERY:
2527-
# Translators: This is presented when there is no battery such as desktop computers and laptops with battery pack removed.
2528-
ui.message(_("No system battery"))
2529-
return
2530-
# Translators: This is presented to inform the user of the current battery percentage.
2531-
text = _("%d percent") % sps.BatteryLifePercent + " "
2532-
# Translators: This is presented when AC power is connected such as when recharging a laptop battery.
2533-
if sps.ACLineStatus & AC_ONLINE: text += _("AC power on")
2534-
elif sps.BatteryLifeTime!=0xffffffff:
2535-
# Translators: This is the estimated remaining runtime of the laptop battery.
2536-
text += _("{hours:d} hours and {minutes:d} minutes remaining") .format(hours=sps.BatteryLifeTime // 3600, minutes=(sps.BatteryLifeTime % 3600) // 60)
2537-
ui.message(text)
2519+
def script_say_battery_status(self, gesture: inputCore.InputGesture) -> None:
2520+
reportCurrentBatteryStatus()
25382521

25392522
@script(
25402523
description=_(

source/mouseHandler.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from math import floor
2525
from contextlib import contextmanager
2626
import threading
27+
from winAPI.winUser.constants import SystemMetrics
28+
2729

2830
WM_MOUSEMOVE=0x0200
2931
WM_LBUTTONDOWN=0x0201
@@ -288,7 +290,7 @@ def getLogicalButtonFlags() -> LogicalButtonFlags:
288290
taking into account the Windows user setting
289291
for which button (left or right) is primary and which is secondary.
290292
"""
291-
swappedButtons = ctypes.windll.user32.GetSystemMetrics(winUser.SM_SWAPBUTTON)
293+
swappedButtons = ctypes.windll.user32.GetSystemMetrics(SystemMetrics.SWAP_BUTTON)
292294
if not swappedButtons:
293295
return LogicalButtonFlags(
294296
primaryDown=winUser.MOUSEEVENTF_LEFTDOWN,

source/visionEnhancementProviders/NVDAHighlighter.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# A part of NonVisual Desktop Access (NVDA)
22
# This file is covered by the GNU General Public License.
33
# See the file COPYING for more details.
4-
# Copyright (C) 2018-2019 NV Access Limited, Babbage B.V., Takuya Nishimoto
4+
# Copyright (C) 2018-2022 NV Access Limited, Babbage B.V., Takuya Nishimoto
55

66
"""Default highlighter based on GDI Plus."""
77
from typing import Optional, Tuple
@@ -25,6 +25,7 @@
2525
from locationHelper import RectLTWH
2626
from collections import namedtuple
2727
import threading
28+
from winAPI.messageWindow import WindowMessage
2829
import winGDI
2930
import weakref
3031
from colors import RGB
@@ -143,7 +144,7 @@ def windowProc(self, hwnd, msg, wParam, lParam):
143144
winUser.user32.PostQuitMessage(0)
144145
elif msg == winUser.WM_TIMER:
145146
self.refresh()
146-
elif msg == winUser.WM_DISPLAYCHANGE:
147+
elif msg == WindowMessage.DISPLAY_CHANGE:
147148
# wx might not be aware of the display change at this point
148149
core.callLater(100, self.updateLocationForDisplays)
149150

0 commit comments

Comments
 (0)