Skip to content

Commit aff0999

Browse files
authored
Merge 2ba7a4c into c29fa56
2 parents c29fa56 + 2ba7a4c commit aff0999

3 files changed

Lines changed: 33 additions & 5 deletions

File tree

source/brailleDisplayDrivers/freedomScientific.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ def __init__(self, port="auto"):
208208
epIn=1,
209209
epOut=0,
210210
onReceive=self._onReceive,
211-
onReceiveSize=56
211+
onReceiveSize=56,
212+
onReadError=self._handleReadError
212213
)
213214
else:
214215
self._dev = hwIo.Serial(
@@ -245,6 +246,7 @@ def __init__(self, port="auto"):
245246
"globalCommands", "GlobalCommands", "braille_scrollBack")
246247
self.gestureMap.add("br(freedomScientific):topRouting%d" % self.numCells,
247248
"globalCommands", "GlobalCommands", "braille_scrollForward")
249+
self._restarting = False
248250

249251
def terminate(self):
250252
try:
@@ -317,6 +319,17 @@ def _onReceive(self, data: bytes):
317319

318320
self._handlePacket(packetType, arg1, arg2, arg3, payload)
319321

322+
def _handleReadError(self, error: int) -> bool:
323+
if error == 995: # Broken I/O pipe, terminate and allow restart
324+
if not self._restarting:
325+
# Will not cause a data race since this driver runs on one thread
326+
self._restarting = True
327+
log.info("Freedom Scientific display implicitly disconnected by suspend, reinitializing")
328+
self.terminate()
329+
self.__init__()
330+
return True
331+
return False
332+
320333
def _handlePacket(
321334
self, packetType: bytes, arg1: bytes, arg2: bytes, arg3: bytes, payload: bytes
322335
):

source/hwIo/base.py

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ def __init__(
3939
fileHandle: Union[ctypes.wintypes.HANDLE],
4040
onReceive: Callable[[bytes], None],
4141
writeFileHandle: Optional[ctypes.wintypes.HANDLE] = None,
42-
onReceiveSize: int = 1
42+
onReceiveSize: int = 1,
43+
onReadError: Optional[Callable[[int], bool]] = None
4344
):
4445
"""Constructor.
4546
@param fileHandle: A handle to an open I/O device opened for overlapped I/O.
@@ -48,11 +49,15 @@ def __init__(
4849
@param onReceive: A callable taking the received data as its only argument.
4950
@param writeFileHandle: A handle to an open output device opened for overlapped I/O.
5051
@param onReceiveSize: The size (in bytes) of the data with which to call C{onReceive}.
52+
@param onReadError: If provided, a callback that takes the error code for a failed read
53+
and returns True if the I/O loop should exit cleanly or False if an
54+
exception should be thrown
5155
"""
5256
self._file = fileHandle
5357
self._onReceive = onReceive
5458
self._writeFile = writeFileHandle if writeFileHandle is not None else fileHandle
5559
self._readSize = onReceiveSize
60+
self._onReadError = onReadError
5661
self._readBuf = ctypes.create_string_buffer(onReceiveSize)
5762
self._readOl = OVERLAPPED()
5863
self._recvEvt = winKernel.createEvent()
@@ -141,7 +146,11 @@ def _ioDone(self, error, numberOfBytes: int, overlapped):
141146
self._ioDone = None
142147
return
143148
elif error != 0:
144-
raise ctypes.WinError(error)
149+
if not self._onReadError or not self._onReadError(error):
150+
raise ctypes.WinError(error)
151+
else:
152+
self._ioDone = None
153+
return
145154
self._notifyReceive(self._readBuf[:numberOfBytes])
146155
winKernel.kernel32.SetEvent(self._recvEvt)
147156
self._asyncRead()
@@ -244,13 +253,17 @@ class Bulk(IoBase):
244253
def __init__(
245254
self, path: str, epIn: int, epOut: int,
246255
onReceive: Callable[[bytes], None],
247-
onReceiveSize: int = 1
256+
onReceiveSize: int = 1,
257+
onReadError: Optional[Callable[[int], bool]] = None
248258
):
249259
"""Constructor.
250260
@param path: The device path.
251261
@param epIn: The endpoint to read data from.
252262
@param epOut: The endpoint to write data to.
253263
@param onReceive: A callable taking a received input report as its only argument.
264+
@param onReadError: An optional callable that handles read errors.
265+
It takes an error code and returns True if the error has been handled,
266+
allowing the read loop to exit cleanly, or False if an exception should be thrown.
254267
"""
255268
if _isDebug():
256269
log.debug("Opening device %s" % path)
@@ -269,7 +282,8 @@ def __init__(
269282
log.debug("Open write handle failed: %s" % ctypes.WinError())
270283
raise ctypes.WinError()
271284
super(Bulk, self).__init__(readHandle, onReceive,
272-
writeFileHandle=writeHandle, onReceiveSize=onReceiveSize)
285+
writeFileHandle=writeHandle, onReceiveSize=onReceiveSize,
286+
onReadError=onReadError)
273287

274288
def close(self):
275289
super(Bulk, self).close()

user_docs/en/changes.t2t

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ What's New in NVDA
5555
- Windows 11 Mail: After switching focus between apps, while reading a long email, NVDA would get stuck on a line of the email. (#13050)
5656
- HID braille: chorded gestures (E.g. space+dot4) can be successfully performed from the Braille display. (#13326)
5757
- Fixed an issue where multiple settings dialogs could be opened at the same time. (#12818)
58+
- Fixed a problem where some Focus Blue Braille displays would stop working after waking the computer from sleep. (#9830)
5859
-
5960

6061
== Changes for Developers ==

0 commit comments

Comments
 (0)