Skip to content

Commit d46f6c5

Browse files
committed
Merge pull request nvaccess#13168 from nvaccess/fixHidAfterCustomDriver
2 parents f6d47c5 + dd24001 commit d46f6c5

File tree

3 files changed

+87
-36
lines changed

3 files changed

+87
-36
lines changed

source/bdDetect.py

+73-30
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
"""
1515

1616
import itertools
17+
import typing
1718
from collections import namedtuple, defaultdict, OrderedDict
1819
import threading
1920
from typing import Iterable
@@ -110,55 +111,98 @@ def addBluetoothDevices(driver, matchFunc):
110111
devs = _getDriver(driver)
111112
devs[KEY_BLUETOOTH] = matchFunc
112113

113-
def getDriversForConnectedUsbDevices():
114+
115+
def getDriversForConnectedUsbDevices() -> typing.Iterator[typing.Tuple[str, DeviceMatch]]:
114116
"""Get any matching drivers for connected USB devices.
115-
@return: Pairs of drivers and device information.
116-
@rtype: generator of (str, L{DeviceMatch}) tuples
117+
Looks for (and yields) custom drivers first, then considers if the device is may be compatible with the
118+
Standard HID Braille spec.
119+
@return: Generator of pairs of drivers and device information.
117120
"""
118-
usbDevs = itertools.chain(
119-
(DeviceMatch(KEY_CUSTOM, port["usbID"], port["devicePath"], port)
120-
for port in deviceInfoFetcher.usbDevices),
121-
(DeviceMatch(KEY_HID, port["usbID"], port["devicePath"], port)
122-
for port in deviceInfoFetcher.hidDevices if port["provider"]=="usb"),
123-
(DeviceMatch(KEY_SERIAL, port["usbID"], port["port"], port)
124-
for port in deviceInfoFetcher.comPorts if "usbID" in port)
121+
usbCustomDeviceMatches = (
122+
DeviceMatch(KEY_CUSTOM, port["usbID"], port["devicePath"], port)
123+
for port in deviceInfoFetcher.usbDevices
125124
)
126-
for match in usbDevs:
125+
usbComDeviceMatches = (
126+
DeviceMatch(KEY_SERIAL, port["usbID"], port["port"], port)
127+
for port in deviceInfoFetcher.comPorts
128+
if "usbID" in port
129+
)
130+
# Tee is used to ensure that the DeviceMatches aren't created multiple times.
131+
# The processing of these HID device matches, looking for a custom driver, means that all
132+
# HID device matches are created, and by teeing the output the matches don't need to be created again.
133+
# The corollary is that clients of this method don't have to process all devices (and create all
134+
# device matches), if one is found early the iteration can stop.
135+
usbHidDeviceMatches, usbHidDeviceMatchesForCustom = itertools.tee((
136+
DeviceMatch(KEY_HID, port["usbID"], port["devicePath"], port)
137+
for port in deviceInfoFetcher.hidDevices
138+
if port["provider"] == "usb"
139+
))
140+
for match in itertools.chain(usbCustomDeviceMatches, usbHidDeviceMatchesForCustom, usbComDeviceMatches):
127141
for driver, devs in _driverDevices.items():
128142
for type, ids in devs.items():
129143
if match.type==type and match.id in ids:
130144
yield driver, match
145+
146+
for match in usbHidDeviceMatches:
131147
# Check for the Braille HID protocol after any other device matching.
132148
# This ensures that a vendor specific driver is preferred over the braille HID protocol.
133149
# This preference may change in the future.
134-
if match.type == KEY_HID and match.deviceInfo.get('HIDUsagePage') == HID_USAGE_PAGE_BRAILLE:
135-
yield ("hid", match)
150+
if _isHIDBrailleMatch(match):
151+
yield (
152+
_getStandardHidDriverName(),
153+
match
154+
)
136155

137156

138-
def getDriversForPossibleBluetoothDevices():
157+
def _getStandardHidDriverName() -> str:
158+
"""Return the name of the standard HID Braille device driver
159+
"""
160+
import brailleDisplayDrivers.hidBrailleStandard
161+
return brailleDisplayDrivers.hidBrailleStandard.HidBrailleDriver.name
162+
163+
164+
def _isHIDBrailleMatch(match: DeviceMatch) -> bool:
165+
return match.type == KEY_HID and match.deviceInfo.get('HIDUsagePage') == HID_USAGE_PAGE_BRAILLE
166+
167+
168+
def getDriversForPossibleBluetoothDevices() -> typing.Iterator[typing.Tuple[str, DeviceMatch]]:
139169
"""Get any matching drivers for possible Bluetooth devices.
140-
@return: Pairs of drivers and port information.
141-
@rtype: generator of (str, L{DeviceMatch}) tuples
170+
Looks for (and yields) custom drivers first, then considers if the device is may be compatible with the
171+
Standard HID Braille spec.
172+
@return: Generator of pairs of drivers and port information.
142173
"""
143-
btDevs = itertools.chain(
144-
(DeviceMatch(KEY_SERIAL, port["bluetoothName"], port["port"], port)
145-
for port in deviceInfoFetcher.comPorts
146-
if "bluetoothName" in port),
147-
(DeviceMatch(KEY_HID, port["hardwareID"], port["devicePath"], port)
148-
for port in deviceInfoFetcher.hidDevices if port["provider"]=="bluetooth"),
174+
btSerialMatchesForCustom = (
175+
DeviceMatch(KEY_SERIAL, port["bluetoothName"], port["port"], port)
176+
for port in deviceInfoFetcher.comPorts
177+
if "bluetoothName" in port
149178
)
150-
for match in btDevs:
179+
# Tee is used to ensure that the DeviceMatches aren't created multiple times.
180+
# The processing of these HID device matches, looking for a custom driver, means that all
181+
# HID device matches are created, and by teeing the output the matches don't need to be created again.
182+
# The corollary is that clients of this method don't have to process all devices (and create all
183+
# device matches), if one is found early the iteration can stop.
184+
btHidDevMatchesForHid, btHidDevMatchesForCustom = itertools.tee((
185+
DeviceMatch(KEY_HID, port["hardwareID"], port["devicePath"], port)
186+
for port in deviceInfoFetcher.hidDevices
187+
if port["provider"] == "bluetooth"
188+
))
189+
for match in itertools.chain(btSerialMatchesForCustom, btHidDevMatchesForCustom):
151190
for driver, devs in _driverDevices.items():
152191
matchFunc = devs[KEY_BLUETOOTH]
153192
if not callable(matchFunc):
154193
continue
155194
if matchFunc(match):
156195
yield driver, match
196+
197+
for match in btHidDevMatchesForHid:
157198
# Check for the Braille HID protocol after any other device matching.
158199
# This ensures that a vendor specific driver is preferred over the braille HID protocol.
159200
# This preference may change in the future.
160-
if match.type == KEY_HID and match.deviceInfo.get('HIDUsagePage') == HID_USAGE_PAGE_BRAILLE:
161-
yield ("hid", match)
201+
if _isHIDBrailleMatch(match):
202+
yield (
203+
_getStandardHidDriverName(),
204+
match
205+
)
162206

163207

164208
class _DeviceInfoFetcher(AutoPropertyObject):
@@ -360,8 +404,8 @@ def getConnectedUsbDevicesForDriver(driver) -> typing.Iterator[DeviceMatch]:
360404
for port in deviceInfoFetcher.comPorts if "usbID" in port)
361405
)
362406
for match in usbDevs:
363-
if driver == "hid":
364-
if match.type == KEY_HID and match.deviceInfo.get('HIDUsagePage') == HID_USAGE_PAGE_BRAILLE:
407+
if driver == _getStandardHidDriverName():
408+
if _isHIDBrailleMatch(match):
365409
yield match
366410
else:
367411
devs = _driverDevices[driver]
@@ -377,9 +421,8 @@ def getPossibleBluetoothDevicesForDriver(driver) -> typing.Iterator[DeviceMatch]
377421
@return: Port information for each port.
378422
@raise LookupError: If there is no detection data for this driver.
379423
"""
380-
if driver == "hid":
381-
def matchFunc(match):
382-
return match.type == KEY_HID and match.deviceInfo.get('HIDUsagePage') == HID_USAGE_PAGE_BRAILLE
424+
if driver == _getStandardHidDriverName():
425+
matchFunc = _isHIDBrailleMatch
383426
else:
384427
matchFunc = _driverDevices[driver][KEY_BLUETOOTH]
385428
if not callable(matchFunc):

source/braille.py

+8-3
Original file line numberDiff line numberDiff line change
@@ -2258,10 +2258,15 @@ def func(cls):
22582258
if cls.exit:
22592259
break
22602260

2261-
#: Maps old braille display driver names to new drivers that supersede old drivers.
2261+
2262+
# Maps old braille display driver names to new drivers that supersede old drivers.
2263+
# Ensure that if a user has set a preferred driver which has changed name, the new
2264+
# user preference is retained.
22622265
RENAMED_DRIVERS = {
2263-
"syncBraille":"hims",
2264-
"alvaBC6":"alva"
2266+
# "oldDriverName": "newDriverName"
2267+
"syncBraille": "hims",
2268+
"alvaBC6": "alva",
2269+
"hid": "hidBrailleStandard",
22652270
}
22662271

22672272
handler: BrailleHandler

source/brailleDisplayDrivers/hid.py source/brailleDisplayDrivers/hidBrailleStandard.py

+6-3
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ class ButtonCapsInfo:
7171
relativeIndexInCollection: int = 0
7272

7373

74-
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
74+
class HidBrailleDriver(braille.BrailleDisplayDriver):
7575
_dev: hwIo.hid.Hid
76-
name = "hid"
76+
name = "hidBrailleStandard"
7777
# Translators: The name of a series of braille displays.
7878
description = _("Standard HID Braille Display")
7979
isThreadSafe = True
@@ -235,7 +235,7 @@ def display(self, cells: List[int]):
235235

236236
class InputGesture(braille.BrailleDisplayGesture, brailleInput.BrailleInputGesture):
237237

238-
source = BrailleDisplayDriver.name
238+
source = HidBrailleDriver.name
239239

240240
def __init__(self, driver, dataIndices):
241241
super().__init__()
@@ -307,3 +307,6 @@ def _usageIDToGestureName(self, usagePage: int, usageID: int):
307307
# Join the words together as camelcase.
308308
name = "".join(wordList)
309309
return name
310+
311+
312+
BrailleDisplayDriver = HidBrailleDriver

0 commit comments

Comments
 (0)