14
14
"""
15
15
16
16
import itertools
17
+ import typing
17
18
from collections import namedtuple , defaultdict , OrderedDict
18
19
import threading
19
20
from typing import Iterable
@@ -110,55 +111,98 @@ def addBluetoothDevices(driver, matchFunc):
110
111
devs = _getDriver (driver )
111
112
devs [KEY_BLUETOOTH ] = matchFunc
112
113
113
- def getDriversForConnectedUsbDevices ():
114
+
115
+ def getDriversForConnectedUsbDevices () -> typing .Iterator [typing .Tuple [str , DeviceMatch ]]:
114
116
"""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.
117
120
"""
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
125
124
)
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 ):
127
141
for driver , devs in _driverDevices .items ():
128
142
for type , ids in devs .items ():
129
143
if match .type == type and match .id in ids :
130
144
yield driver , match
145
+
146
+ for match in usbHidDeviceMatches :
131
147
# Check for the Braille HID protocol after any other device matching.
132
148
# This ensures that a vendor specific driver is preferred over the braille HID protocol.
133
149
# 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
+ )
136
155
137
156
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 ]]:
139
169
"""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.
142
173
"""
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
149
178
)
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 ):
151
190
for driver , devs in _driverDevices .items ():
152
191
matchFunc = devs [KEY_BLUETOOTH ]
153
192
if not callable (matchFunc ):
154
193
continue
155
194
if matchFunc (match ):
156
195
yield driver , match
196
+
197
+ for match in btHidDevMatchesForHid :
157
198
# Check for the Braille HID protocol after any other device matching.
158
199
# This ensures that a vendor specific driver is preferred over the braille HID protocol.
159
200
# 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
+ )
162
206
163
207
164
208
class _DeviceInfoFetcher (AutoPropertyObject ):
@@ -360,8 +404,8 @@ def getConnectedUsbDevicesForDriver(driver) -> typing.Iterator[DeviceMatch]:
360
404
for port in deviceInfoFetcher .comPorts if "usbID" in port )
361
405
)
362
406
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 ) :
365
409
yield match
366
410
else :
367
411
devs = _driverDevices [driver ]
@@ -377,9 +421,8 @@ def getPossibleBluetoothDevicesForDriver(driver) -> typing.Iterator[DeviceMatch]
377
421
@return: Port information for each port.
378
422
@raise LookupError: If there is no detection data for this driver.
379
423
"""
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
383
426
else :
384
427
matchFunc = _driverDevices [driver ][KEY_BLUETOOTH ]
385
428
if not callable (matchFunc ):
0 commit comments