Skip to content

Commit b95da15

Browse files
authored
Merge ce7880f into c4ca763
2 parents c4ca763 + ce7880f commit b95da15

1 file changed

Lines changed: 81 additions & 2 deletions

File tree

source/braille.py

Lines changed: 81 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Tuple,
2020
Union,
2121
Type,
22+
Callable,
2223
)
2324
from locale import strxfrm
2425

@@ -53,6 +54,7 @@
5354
import re
5455
import scriptHandler
5556
import collections
57+
from collections import deque
5658
import extensionPoints
5759
import hwPortUtils
5860
import bdDetect
@@ -1569,6 +1571,7 @@ def bufferPosToRegionPos(self, bufferPos):
15691571
raise LookupError("No such position")
15701572

15711573
def regionPosToBufferPos(self, region, pos, allowNearest=False):
1574+
start: int = 0
15721575
for testRegion, start, end in self.regionsWithPositions:
15731576
if region == testRegion:
15741577
if pos < end - start:
@@ -2003,6 +2006,46 @@ def formatCellsForLog(cells: List[int]) -> str:
20032006
"""
20042007

20052008

2009+
class RepeatedTimer:
2010+
"""Repeating timer.
2011+
Timer is used to try to ensure that display content is always up to date.
2012+
"""
2013+
2014+
def __init__(
2015+
self,
2016+
interval: float,
2017+
updateFunction: Callable[[], None]
2018+
):
2019+
"""Constructor.
2020+
@param interval: Checking frequency
2021+
@param updateFunction: Update display
2022+
"""
2023+
self._interval = interval
2024+
self._timer = threading.Timer(self._interval, self._run)
2025+
self._updateFunction = updateFunction
2026+
self.is_running = False
2027+
self.start()
2028+
2029+
def _run(self):
2030+
self.is_running = False
2031+
self.start()
2032+
self._updateFunction()
2033+
2034+
def start(self):
2035+
if not self.is_running:
2036+
self._timer = threading.Timer(self._interval, self._run)
2037+
self._timer.start()
2038+
self.is_running = True
2039+
2040+
def stop(self):
2041+
self._timer.cancel()
2042+
self.is_running = False
2043+
2044+
2045+
UPDATE_DISPLAY_PERIODICALLY_INTERVAL: float = 0.5
2046+
"""Timer interval for L{BrailleHandler._updateDisplayPeriodically}."""
2047+
2048+
20062049
class BrailleHandler(baseObject.AutoPropertyObject):
20072050
# TETHER_AUTO, TETHER_FOCUS, TETHER_REVIEW and tetherValues
20082051
# are deprecated, but remain to retain API backwards compatibility
@@ -2042,6 +2085,13 @@ def __init__(self):
20422085
self._cursorPos = None
20432086
self._cursorBlinkUp = True
20442087
self._cells = []
2088+
self._oldCells: List[int] = []
2089+
self._handleUpdateQueue = deque(maxlen=1)
2090+
self._updateTimer = RepeatedTimer(
2091+
UPDATE_DISPLAY_PERIODICALLY_INTERVAL,
2092+
self._updateDisplayPeriodically
2093+
)
2094+
self._updateTimer.start()
20452095
self._cursorBlinkTimer = None
20462096
config.post_configProfileSwitch.register(self.handlePostConfigProfileSwitch)
20472097
if config.conf["braille"]["tetherTo"] == TetherTo.AUTO.value:
@@ -2064,6 +2114,9 @@ def terminate(self):
20642114
if self._cursorBlinkTimer:
20652115
self._cursorBlinkTimer.Stop()
20662116
self._cursorBlinkTimer = None
2117+
if self._updateTimer:
2118+
self._updateTimer.stop()
2119+
self._updateTimer = None
20672120
config.post_configProfileSwitch.unregister(self.handlePostConfigProfileSwitch)
20682121
if self.display:
20692122
self.display.terminate()
@@ -2468,6 +2521,12 @@ def handleCaretMove(
24682521
if shouldAutoTether:
24692522
self.setTether(TetherTo.FOCUS.value, auto=True)
24702523
if self._tether != TetherTo.FOCUS.value:
2524+
# Braille display content is updated in case where:
2525+
# braille is tethered to review, review cursor does not follow system caret,
2526+
# and focus object is navigator object.
2527+
if not config.conf["reviewCursor"]["followCaret"]:
2528+
if obj == api.getNavigatorObject():
2529+
self.handleUpdate(obj)
24712530
return
24722531
region = self.mainBuffer.regions[-1] if self.mainBuffer.regions else None
24732532
if region and region.obj==obj:
@@ -2526,7 +2585,7 @@ def _handleProgressBarUpdate(
25262585
self.handleUpdate(obj)
25272586
return
25282587

2529-
def handleUpdate(self, obj: "NVDAObject") -> None:
2588+
def handleUpdate(self, obj: "NVDAObject", fromTimer: bool = False) -> None:
25302589
if not self.enabled:
25312590
return
25322591
if objectBelowLockScreenAndWindowsIsLocked(obj):
@@ -2549,11 +2608,15 @@ def handleUpdate(self, obj: "NVDAObject") -> None:
25492608
self._handleProgressBarUpdate(obj)
25502609
return
25512610
self.mainBuffer.saveWindow()
2611+
log.debug("saadaanko debug-varoitus")
25522612
region.update()
25532613
self.mainBuffer.update()
25542614
self.mainBuffer.restoreWindow()
25552615
if self.buffer is self.mainBuffer:
2616+
if fromTimer and self._oldCells == self.buffer.windowBrailleCells:
2617+
return
25562618
self.update()
2619+
self._oldCells = self.buffer.windowBrailleCells.copy()
25572620
elif self.buffer is self.messageBuffer and keyboardHandler.keyCounter>self._keyCountForLastMessage:
25582621
self._dismissMessage()
25592622

@@ -2686,6 +2749,19 @@ def _ackTimeoutResetter(self, param: int):
26862749
self.display._awaitingAck = False
26872750
self._writeCellsInBackground()
26882751

2752+
def _updateDisplayPeriodically(self):
2753+
"""Timer runs this function periodically to ensure content is up to date."""
2754+
if self.buffer is not self.mainBuffer:
2755+
return
2756+
obj: NVDAObject
2757+
if api.isObjectInActiveTreeInterceptor(api.getNavigatorObject()):
2758+
obj = api.getCaretObject()
2759+
elif handler.getTether() == TetherTo.FOCUS.value:
2760+
obj = api.getFocusObject()
2761+
else:
2762+
obj = api.getNavigatorObject()
2763+
self._handleUpdateQueue.append(obj)
2764+
26892765

26902766
# Maps old braille display driver names to new drivers that supersede old drivers.
26912767
# Ensure that if a user has set a preferred driver which has changed name, the new
@@ -2720,14 +2796,17 @@ def initialize():
27202796
handler.setDisplayByName(config.conf["braille"]["display"])
27212797

27222798
def pumpAll():
2723-
"""Runs tasks at the end of each core cycle. For now just caret updates."""
2799+
"""Runs tasks at the end of each core cycle."""
2800+
if len(handler._handleUpdateQueue):
2801+
handler.handleUpdate(handler._handleUpdateQueue.popleft(), True)
27242802
handler.handlePendingCaretUpdate()
27252803

27262804
def terminate():
27272805
global handler
27282806
handler.terminate()
27292807
handler = None
27302808

2809+
27312810
class BrailleDisplayDriver(driverHandler.Driver):
27322811
"""Abstract base braille display driver.
27332812
Each braille display driver should be a separate Python module in the root brailleDisplayDrivers directory

0 commit comments

Comments
 (0)