33#A part of NonVisual Desktop Access (NVDA)
44#This file is covered by the GNU General Public License.
55#See the file COPYING for more details.
6- #Copyright (C) 2017-2019 NV Access Limited, Babbage B.V., Eurobraille
6+ #Copyright (C) 2017-2023 NV Access Limited, Babbage B.V., Eurobraille
77
88from collections import OrderedDict , defaultdict
9- from typing import Dict , Any , List , Union
9+ from typing import Dict , Any , List , Union , Iterator
10+ import re
1011
1112from io import BytesIO
1213import serial
2324from globalCommands import SCRCAT_BRAILLE
2425import ui
2526import time
27+ import hwPortUtils
2628
2729BAUD_RATE = 9600
2830
5557EB_IRIS_TEST_sub = b'L' # 0x4c
5658EB_VISU = b'V' # 0x56
5759EB_VISU_DOT = b'D' # 0x44
60+ EB_CONNECTION_NAME = b'n'
5861
5962# The eurobraille protocol uses real number characters as boolean values, so 0 (0x30) and 1 (0x31)
6063EB_FALSE = b'0' # 0x30
6568 0x20000 : "joystick1Down" ,
6669 0x40000 : "joystick1Right" ,
6770 0x80000 : "joystick1Left" ,
68- 0x100000 : "joystick1Center" ,
71+ 0x100000 : "joystick1Center" ,
6972 0x1000000 : "joystick2Up" ,
7073 0x2000000 : "joystick2Down" ,
7174 0x4000000 : "joystick2Right" ,
114117})
115118KEYS_ESITIME .update (KEYS_STICK )
116119
120+ KEYS_BNOTE = KEYS_ESYS
121+ KEYS_BBOOK = KEYS_ESITIME
122+
117123DEVICE_TYPES = {
118124 0x01 :"Iris 20" ,
119125 0x02 :"Iris 40" ,
132138 0x0f :"Esytime 32 standard" ,
133139 0x10 :"Esytime evo 32" ,
134140 0x11 :"Esytime evo 32 standard" ,
141+ 0x12 :"b.note" ,
142+ 0x13 :"b.note 2" ,
143+ 0x14 :"b.book" ,
144+ 0x15 :"b.book 2"
135145}
136146
147+ defaultGestureMapEntries = {
148+ "globalCommands.GlobalCommands" : {
149+ "braille_routeTo" : ("br(eurobraille):routing" ,),
150+ "braille_reportFormatting" : ("br(eurobraille):doubleRouting" ,),
151+ "braille_scrollBack" : (
152+ "br(eurobraille):switch1Left" ,
153+ "br(eurobraille):l1" ,
154+ ),
155+ "braille_scrollForward" : (
156+ "br(eurobraille):switch1Right" ,
157+ "br(eurobraille):l8" ,
158+ ),
159+ "braille_toFocus" : (
160+ "br(eurobraille):switch1Left+switch1Right" , "br(eurobraille):switch2Left+switch2Right" ,
161+ "br(eurobraille):switch3Left+switch3Right" , "br(eurobraille):switch4Left+switch4Right" ,
162+ "br(eurobraille):switch5Left+switch5Right" , "br(eurobraille):switch6Left+switch6Right" ,
163+ "br(eurobraille):l1+l8" ,
164+ ),
165+ "review_previousLine" : ("br(eurobraille):joystick1Up" ,),
166+ "review_nextLine" : ("br(eurobraille):joystick1Down" ,),
167+ "review_previousCharacter" : ("br(eurobraille):joystick1Left" ,),
168+ "review_nextCharacter" : ("br(eurobraille):joystick1Right" ,),
169+ "reviewMode_previous" : ("br(eurobraille):joystick1Left+joystick1Up" ,),
170+ "reviewMode_next" : ("br(eurobraille):joystick1Right+joystick1Down" ,),
171+ # Esys and esytime have a dedicated key for backspace and combines backspace and space to perform a return.
172+ "braille_eraseLastCell" : ("br(eurobraille):backSpace" ,),
173+ "braille_enter" : ("br(eurobraille):backSpace+space" ,),
174+ "kb:insert" : (
175+ "br(eurobraille):dot1+dot3+dot5+space" ,
176+ "br(eurobraille):dot3+dot4+dot5+space" ,
177+ ),
178+ "kb:delete" : ("br(eurobraille):dot3+dot6+space" ,),
179+ "kb:home" : ("br(eurobraille):dot1+dot2+dot3+space" ),
180+ "kb:end" : ("br(eurobraille):dot4+dot5+dot6+space" ,),
181+ "kb:leftArrow" : (
182+ "br(eurobraille):dot2+space" ,
183+ "br(eurobraille):joystick2Left" ,
184+ "br(eurobraille):leftArrow" ,
185+ ),
186+ "kb:rightArrow" : (
187+ "br(eurobraille):dot5+space" ,
188+ "br(eurobraille):joystick2Right" ,
189+ "br(eurobraille):rightArrow" ,
190+ ),
191+ "kb:upArrow" : (
192+ "br(eurobraille):dot1+space" ,
193+ "br(eurobraille):joystick2Up" ,
194+ "br(eurobraille):upArrow" ,
195+ ),
196+ "kb:downArrow" : (
197+ "br(eurobraille):dot6+space" ,
198+ "br(eurobraille):joystick2Down" ,
199+ "br(eurobraille):downArrow" ,
200+ ),
201+ "kb:enter" : ("br(eurobraille):joystick2Center" ,),
202+ "kb:pageUp" : ("br(eurobraille):dot1+dot3+space" ,),
203+ "kb:pageDown" : ("br(eurobraille):dot4+dot6+space" ,),
204+ "kb:numpad1" : ("br(eurobraille):dot1+dot6+backspace" ,),
205+ "kb:numpad2" : ("br(eurobraille):dot1+dot2+dot6+backspace" ,),
206+ "kb:numpad3" : ("br(eurobraille):dot1+dot4+dot6+backspace" ,),
207+ "kb:numpad4" : ("br(eurobraille):dot1+dot4+dot5+dot6+backspace" ,),
208+ "kb:numpad5" : ("br(eurobraille):dot1+dot5+dot6+backspace" ,),
209+ "kb:numpad6" : ("br(eurobraille):dot1+dot2+dot4+dot6+backspace" ,),
210+ "kb:numpad7" : ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+backspace" ,),
211+ "kb:numpad8" : ("br(eurobraille):dot1+dot2+dot5+dot6+backspace" ,),
212+ "kb:numpad9" : ("br(eurobraille):dot2+dot4+dot6+backspace" ,),
213+ "kb:numpadInsert" : ("br(eurobraille):dot3+dot4+dot5+dot6+backspace" ,),
214+ "kb:numpadDecimal" : ("br(eurobraille):dot2+backspace" ,),
215+ "kb:numpadDivide" : ("br(eurobraille):dot3+dot4+backspace" ,),
216+ "kb:numpadMultiply" : ("br(eurobraille):dot3+dot5+backspace" ,),
217+ "kb:numpadMinus" : ("br(eurobraille):dot3+dot6+backspace" ,),
218+ "kb:numpadPlus" : ("br(eurobraille):dot2+dot3+dot5+backspace" ,),
219+ "kb:numpadEnter" : ("br(eurobraille):dot3+dot4+dot5+backspace" ,),
220+ "kb:escape" : (
221+ "br(eurobraille):dot1+dot2+dot4+dot5+space" ,
222+ "br(eurobraille):l2" ,
223+ ),
224+ "kb:tab" : (
225+ "br(eurobraille):dot2+dot5+dot6+space" ,
226+ "br(eurobraille):l3" ,
227+ ),
228+ "kb:shift+tab" : ("br(eurobraille):dot2+dot3+dot5+space" ,),
229+ "kb:printScreen" : ("br(eurobraille):dot1+dot3+dot4+dot6+space" ,),
230+ "kb:pause" : ("br(eurobraille):dot1+dot4+space" ,),
231+ "kb:applications" : ("br(eurobraille):dot5+dot6+backspace" ,),
232+ "kb:f1" : ("br(eurobraille):dot1+backspace" ,),
233+ "kb:f2" : ("br(eurobraille):dot1+dot2+backspace" ,),
234+ "kb:f3" : ("br(eurobraille):dot1+dot4+backspace" ,),
235+ "kb:f4" : ("br(eurobraille):dot1+dot4+dot5+backspace" ,),
236+ "kb:f5" : ("br(eurobraille):dot1+dot5+backspace" ,),
237+ "kb:f6" : ("br(eurobraille):dot1+dot2+dot4+backspace" ,),
238+ "kb:f7" : ("br(eurobraille):dot1+dot2+dot4+dot5+backspace" ,),
239+ "kb:f8" : ("br(eurobraille):dot1+dot2+dot5+backspace" ,),
240+ "kb:f9" : ("br(eurobraille):dot2+dot4+backspace" ,),
241+ "kb:f10" : ("br(eurobraille):dot2+dot4+dot5+backspace" ,),
242+ "kb:f11" : ("br(eurobraille):dot1+dot3+backspace" ,),
243+ "kb:f12" : ("br(eurobraille):dot1+dot2+dot3+backspace" ,),
244+ "kb:windows" : ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+space" ,),
245+ "kb:capsLock" : ("br(eurobraille):dot7+backspace" , "br(eurobraille):dot8+backspace" ,),
246+ "kb:numLock" : ("br(eurobraille):dot3+backspace" , "br(eurobraille):dot6+backspace" ,),
247+ "kb:shift" : ("br(eurobraille):dot1+dot7+space" , "br(eurobraille):dot4+dot7+space" ,),
248+ "braille_toggleShift" : ("br(eurobraille):dot7+space" ,"br(eurobraille):l4" ,),
249+ "kb:control" : ("br(eurobraille):dot1+dot7+dot8+space" , "br(eurobraille):dot4+dot7+dot8+space" ,),
250+ "braille_toggleControl" : ("br(eurobraille):dot7+dot8+space" ,"br(eurobraille):l5" ,),
251+ "kb:alt" : ("br(eurobraille):dot1+dot8+space" , "br(eurobraille):dot4+dot8+space" ),
252+ "braille_toggleAlt" : ("br(eurobraille):dot8+space" , "br(eurobraille):l6" ),
253+ "braille_toggleNVDAKey" : ("br(eurobraille):l7" , "br(eurobraille):dot3+dot5+space" ),
254+ "kb:control+home" : ("br(eurobraille):joystick2left+joystick2up" , "br(eurobraille):l1+l2+l3" , "br(eurobraille):l2+l3+l4" ,),
255+ "kb:control+end" : ("br(eurobraille):joystick2right+joystick2up" , "br(eurobraille):l6+l7+l8" , "br(eurobraille):l5+l6+l7" ,),
256+ "braille_toggleWindows" : ("br(eurobraille):backspace+dot1+dot2+dot3+dot4" , "br(eurobraille):dot2+dot4+dot5+dot6+space" ,),
257+ "kb:control+shift+e" : ("br(eurobraille):dot1+dot5+space" ,),
258+ },
259+ }
260+ default_GestureMap = inputCore .GlobalGestureMap (defaultGestureMapEntries )
261+ bnote_GestureMap = inputCore .GlobalGestureMap (defaultGestureMapEntries )
262+ bnote_GestureMap .add ("br(eurobraille):joystick1Left" , "globalCommands" , "GlobalCommands" , "braille_scrollBack" )
263+ bnote_GestureMap .add ("br(eurobraille):joystick1Right" , "globalCommands" , "GlobalCommands" , "braille_scrollForward" )
264+ bnote_GestureMap .remove ("br(eurobraille):joystick1Left" , "globalCommands" , "GlobalCommands" , "review_previousCharacter" )
265+ bnote_GestureMap .remove ("br(eurobraille):joystick1Right" , "globalCommands" , "GlobalCommands" , "review_nextCharacter" )
137266
138267def bytesToInt (byteData : bytes ):
139268 """Converts bytes to its integral equivalent."""
@@ -146,7 +275,7 @@ class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
146275 _awaitingFrameReceipts : Dict [int , Any ]
147276 name = "eurobraille"
148277 # Translators: Names of braille displays.
149- description = _ ("Eurobraille Esys/Esytime/Iris displays" )
278+ description = _ ("Eurobraille displays" )
150279 isThreadSafe = True
151280 timeout = 0.2
152281 supportedSettings = (
@@ -211,6 +340,12 @@ def __init__(self, port="Auto"):
211340 # A display responded.
212341 log .info ("Found {device} connected via {type} ({port})" .format (
213342 device = self .deviceType , type = portType , port = port ))
343+ if self .deviceType .startswith (("b.note" , "b.book" )):
344+ # send identifier to bnote / bbook with current COM port
345+ comportNumber = f'{ int (re .match (".*?([0-9]+)$" , port ).group (1 )):02d} '
346+ identifier = f"NVDA/{ comportNumber } " .encode ()
347+ log .debugWarning (f"sending { identifier } to eurobraille display" )
348+ self ._sendPacket (EB_SYSTEM , EB_CONNECTION_NAME , identifier )
214349 break
215350 self ._dev .close ()
216351
@@ -222,6 +357,9 @@ def __init__(self, port="Auto"):
222357
223358 def terminate (self ):
224359 try :
360+ if self .deviceType .startswith (("b.note" , "b.book" )):
361+ # reset identifier to bnote / bbook with current COM port
362+ self ._sendPacket (EB_SYSTEM , EB_CONNECTION_NAME , b'' )
225363 super (BrailleDisplayDriver , self ).terminate ()
226364 finally :
227365 # We must sleep before closing the port as not doing this can leave the display in a bad state where it can not be re-initialized.
@@ -313,6 +451,11 @@ def _handleSystemPacket(self, packetType: bytes, data: bytes):
313451 self .keys = KEYS_ESYS
314452 elif 0x0e <= deviceType <= 0x11 : # Esitime
315453 self .keys = KEYS_ESITIME
454+ elif 0x12 <= deviceType <= 0x13 :
455+ self .gestureMap = bnote_GestureMap
456+ self .keys = KEYS_BNOTE
457+ elif 0x14 <= deviceType <= 0x15 :
458+ self .keys = KEYS_BBOOK
316459 else :
317460 log .debugWarning ("Unknown device identifier %r" % data )
318461 elif packetType == EB_SYSTEM_DISPLAY_LENGTH :
@@ -453,125 +596,11 @@ def announceUnavailableMessage():
453596 "br(eurobraille):switch1Left+joystick1Down" : "toggleHidKeyboardInput" ,
454597 "br(eurobraille.esytime):l8+joystick1Down" : "toggleHidKeyboardInput" ,
455598 "br(eurobraille):switch1Right+joystick1Down" : "toggleHidKeyboardInput" ,
456- }
457-
458- gestureMap = inputCore .GlobalGestureMap ({
459- "globalCommands.GlobalCommands" : {
460- "braille_routeTo" : ("br(eurobraille):routing" ,),
461- "braille_reportFormatting" : ("br(eurobraille):doubleRouting" ,),
462- "braille_scrollBack" : (
463- "br(eurobraille):switch1Left" ,
464- "br(eurobraille):l1" ,
465- ),
466- "braille_scrollForward" : (
467- "br(eurobraille):switch1Right" ,
468- "br(eurobraille):l8" ,
469- ),
470- "braille_toFocus" : (
471- "br(eurobraille):switch1Left+switch1Right" , "br(eurobraille):switch2Left+switch2Right" ,
472- "br(eurobraille):switch3Left+switch3Right" , "br(eurobraille):switch4Left+switch4Right" ,
473- "br(eurobraille):switch5Left+switch5Right" , "br(eurobraille):switch6Left+switch6Right" ,
474- "br(eurobraille):l1+l8" ,
475- ),
476- "review_previousLine" : ("br(eurobraille):joystick1Up" ,),
477- "review_nextLine" : ("br(eurobraille):joystick1Down" ,),
478- "review_previousCharacter" : ("br(eurobraille):joystick1Left" ,),
479- "review_nextCharacter" : ("br(eurobraille):joystick1Right" ,),
480- "reviewMode_previous" : ("br(eurobraille):joystick1Left+joystick1Up" ,),
481- "reviewMode_next" : ("br(eurobraille):joystick1Right+joystick1Down" ,),
482- # Esys and esytime have a dedicated key for backspace and combines backspace and space to perform a return.
483- "braille_eraseLastCell" : ("br(eurobraille):backSpace" ,),
484- "braille_enter" : ("br(eurobraille):backSpace+space" ,),
485- "kb:insert" : (
486- "br(eurobraille):dot3+dot5+space" ,
487- "br(eurobraille):l7" ,
488- ),
489- "kb:delete" : ("br(eurobraille):dot3+dot6+space" ,),
490- "kb:home" : ("br(eurobraille):dot1+dot2+dot3+space" , "br(eurobraille):joystick2Left+joystick2Up" ,),
491- "kb:end" : ("br(eurobraille):dot4+dot5+dot6+space" , "br(eurobraille):joystick2Right+joystick2Down" ,),
492- "kb:leftArrow" : (
493- "br(eurobraille):dot2+space" ,
494- "br(eurobraille):joystick2Left" ,
495- "br(eurobraille):leftArrow" ,
496- ),
497- "kb:rightArrow" : (
498- "br(eurobraille):dot5+space" ,
499- "br(eurobraille):joystick2Right" ,
500- "br(eurobraille):rightArrow" ,
501- ),
502- "kb:upArrow" : (
503- "br(eurobraille):dot1+space" ,
504- "br(eurobraille):joystick2Up" ,
505- "br(eurobraille):upArrow" ,
506- ),
507- "kb:downArrow" : (
508- "br(eurobraille):dot6+space" ,
509- "br(eurobraille):joystick2Down" ,
510- "br(eurobraille):downArrow" ,
511- ),
512- "kb:enter" : ("br(eurobraille):joystick2Center" ,),
513- "kb:pageUp" : ("br(eurobraille):dot1+dot3+space" ,),
514- "kb:pageDown" : ("br(eurobraille):dot4+dot6+space" ,),
515- "kb:numpad1" : ("br(eurobraille):dot1+dot6+backspace" ,),
516- "kb:numpad2" : ("br(eurobraille):dot1+dot2+dot6+backspace" ,),
517- "kb:numpad3" : ("br(eurobraille):dot1+dot4+dot6+backspace" ,),
518- "kb:numpad4" : ("br(eurobraille):dot1+dot4+dot5+dot6+backspace" ,),
519- "kb:numpad5" : ("br(eurobraille):dot1+dot5+dot6+backspace" ,),
520- "kb:numpad6" : ("br(eurobraille):dot1+dot2+dot4+dot6+backspace" ,),
521- "kb:numpad7" : ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+backspace" ,),
522- "kb:numpad8" : ("br(eurobraille):dot1+dot2+dot5+dot6+backspace" ,),
523- "kb:numpad9" : ("br(eurobraille):dot2+dot4+dot6+backspace" ,),
524- "kb:numpadInsert" : ("br(eurobraille):dot3+dot4+dot5+dot6+backspace" ,),
525- "kb:numpadDecimal" : ("br(eurobraille):dot2+backspace" ,),
526- "kb:numpadDivide" : ("br(eurobraille):dot3+dot4+backspace" ,),
527- "kb:numpadMultiply" : ("br(eurobraille):dot3+dot5+backspace" ,),
528- "kb:numpadMinus" : ("br(eurobraille):dot3+dot6+backspace" ,),
529- "kb:numpadPlus" : ("br(eurobraille):dot2+dot3+dot5+backspace" ,),
530- "kb:numpadEnter" : ("br(eurobraille):dot3+dot4+dot5+backspace" ,),
531- "kb:escape" : (
532- "br(eurobraille):dot1+dot2+dot4+dot5+space" ,
533- "br(eurobraille):l2" ,
534- ),
535- "kb:tab" : (
536- "br(eurobraille):dot2+dot5+dot6+space" ,
537- "br(eurobraille):l3" ,
538- ),
539- "kb:shift+tab" : ("br(eurobraille):dot2+dot3+dot5+space" ,),
540- "kb:printScreen" : ("br(eurobraille):dot1+dot3+dot4+dot6+space" ,),
541- "kb:pause" : ("br(eurobraille):dot1+dot4+space" ,),
542- "kb:applications" : ("br(eurobraille):dot5+dot6+backspace" ,),
543- "kb:f1" : ("br(eurobraille):dot1+backspace" ,),
544- "kb:f2" : ("br(eurobraille):dot1+dot2+backspace" ,),
545- "kb:f3" : ("br(eurobraille):dot1+dot4+backspace" ,),
546- "kb:f4" : ("br(eurobraille):dot1+dot4+dot5+backspace" ,),
547- "kb:f5" : ("br(eurobraille):dot1+dot5+backspace" ,),
548- "kb:f6" : ("br(eurobraille):dot1+dot2+dot4+backspace" ,),
549- "kb:f7" : ("br(eurobraille):dot1+dot2+dot4+dot5+backspace" ,),
550- "kb:f8" : ("br(eurobraille):dot1+dot2+dot5+backspace" ,),
551- "kb:f9" : ("br(eurobraille):dot2+dot4+backspace" ,),
552- "kb:f10" : ("br(eurobraille):dot2+dot4+dot5+backspace" ,),
553- "kb:f11" : ("br(eurobraille):dot1+dot3+backspace" ,),
554- "kb:f12" : ("br(eurobraille):dot1+dot2+dot3+backspace" ,),
555- "kb:windows" : ("br(eurobraille):dot1+dot2+dot3+dot4+backspace" ,),
556- "kb:capsLock" : ("br(eurobraille):dot7+backspace" , "br(eurobraille):dot8+backspace" ,),
557- "kb:numLock" : ("br(eurobraille):dot3+backspace" , "br(eurobraille):dot6+backspace" ,),
558- "kb:shift" : (
559- "br(eurobraille):dot7+space" ,
560- "br(eurobraille):l4" ,
561- ),
562- "braille_toggleShift" : ("br(eurobraille):dot1+dot7+space" , "br(eurobraille):dot4+dot7+space" ,),
563- "kb:control" : (
564- "br(eurobraille):dot7+dot8+space" ,
565- "br(eurobraille):l5" ,
566- ),
567- "braille_toggleControl" : ("br(eurobraille):dot1+dot7+dot8+space" , "br(eurobraille):dot4+dot7+dot8+space" ,),
568- "kb:alt" : (
569- "br(eurobraille):dot8+space" ,
570- "br(eurobraille):l6" ,
571- ),
572- "braille_toggleAlt" : ("br(eurobraille):dot1+dot8+space" , "br(eurobraille):dot4+dot8+space" ,),
573- },
574- })
599+ }\
600+
601+ gestureMap = default_GestureMap
602+
603+
575604
576605class InputGesture (braille .BrailleDisplayGesture , brailleInput .BrailleInputGesture ):
577606
@@ -581,6 +610,7 @@ def __init__(self, display):
581610 super (InputGesture , self ).__init__ ()
582611 self .model = display .deviceType .lower ().split (" " )[0 ]
583612 keysDown = dict (display .keysDown )
613+
584614 self .keyNames = names = []
585615 for group , groupKeysDown in keysDown .items ():
586616 if group == EB_KEY_BRAILLE :
0 commit comments