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.b.note):joystick1Left" ,
153+ "br(eurobraille):switch1Left" ,
154+ "br(eurobraille):l1" ,
155+ ),
156+ "braille_scrollForward" : (
157+ "br(eurobraille.b.note):joystick1Right" ,
158+ "br(eurobraille):switch1Right" ,
159+ "br(eurobraille):l8" ,
160+ ),
161+ "braille_toFocus" : (
162+ "br(eurobraille):switch1Left+switch1Right" , "br(eurobraille):switch2Left+switch2Right" ,
163+ "br(eurobraille):switch3Left+switch3Right" , "br(eurobraille):switch4Left+switch4Right" ,
164+ "br(eurobraille):switch5Left+switch5Right" , "br(eurobraille):switch6Left+switch6Right" ,
165+ "br(eurobraille):l1+l8" ,
166+ ),
167+ "review_previousLine" : ("br(eurobraille):joystick1Up" ,),
168+ "review_nextLine" : ("br(eurobraille):joystick1Down" ,),
169+ "review_previousCharacter" : ("br(eurobraille):joystick1Left" ,),
170+ "review_nextCharacter" : ("br(eurobraille):joystick1Right" ,),
171+ "reviewMode_previous" : ("br(eurobraille):joystick1Left+joystick1Up" ,),
172+ "reviewMode_next" : ("br(eurobraille):joystick1Right+joystick1Down" ,),
173+ # Esys and esytime have a dedicated key for backspace and combines backspace and space to perform a return.
174+ "braille_eraseLastCell" : ("br(eurobraille):backSpace" ,),
175+ "braille_enter" : ("br(eurobraille):backSpace+space" ,),
176+ "kb:insert" : (
177+ "br(eurobraille):dot1+dot3+dot5+space" ,
178+ "br(eurobraille):dot3+dot4+dot5+space" ,
179+ ),
180+ "kb:delete" : ("br(eurobraille):dot3+dot6+space" ,),
181+ "kb:home" : ("br(eurobraille):dot1+dot2+dot3+space" ),
182+ "kb:end" : ("br(eurobraille):dot4+dot5+dot6+space" ,),
183+ "kb:leftArrow" : (
184+ "br(eurobraille):dot2+space" ,
185+ "br(eurobraille):joystick2Left" ,
186+ "br(eurobraille):leftArrow" ,
187+ ),
188+ "kb:rightArrow" : (
189+ "br(eurobraille):dot5+space" ,
190+ "br(eurobraille):joystick2Right" ,
191+ "br(eurobraille):rightArrow" ,
192+ ),
193+ "kb:upArrow" : (
194+ "br(eurobraille):dot4+space" ,
195+ "br(eurobraille):joystick2Up" ,
196+ "br(eurobraille):upArrow" ,
197+ ),
198+ "kb:downArrow" : (
199+ "br(eurobraille):dot6+space" ,
200+ "br(eurobraille):joystick2Down" ,
201+ "br(eurobraille):downArrow" ,
202+ ),
203+ "kb:enter" : ("br(eurobraille):joystick2Center" ,),
204+ "kb:pageUp" : ("br(eurobraille):dot1+dot3+space" ,),
205+ "kb:pageDown" : ("br(eurobraille):dot4+dot6+space" ,),
206+ "kb:numpad1" : ("br(eurobraille):dot1+dot6+backspace" ,),
207+ "kb:numpad2" : ("br(eurobraille):dot1+dot2+dot6+backspace" ,),
208+ "kb:numpad3" : ("br(eurobraille):dot1+dot4+dot6+backspace" ,),
209+ "kb:numpad4" : ("br(eurobraille):dot1+dot4+dot5+dot6+backspace" ,),
210+ "kb:numpad5" : ("br(eurobraille):dot1+dot5+dot6+backspace" ,),
211+ "kb:numpad6" : ("br(eurobraille):dot1+dot2+dot4+dot6+backspace" ,),
212+ "kb:numpad7" : ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+backspace" ,),
213+ "kb:numpad8" : ("br(eurobraille):dot1+dot2+dot5+dot6+backspace" ,),
214+ "kb:numpad9" : ("br(eurobraille):dot2+dot4+dot6+backspace" ,),
215+ "kb:numpadInsert" : ("br(eurobraille):dot3+dot4+dot5+dot6+backspace" ,),
216+ "kb:numpadDecimal" : ("br(eurobraille):dot2+backspace" ,),
217+ "kb:numpadDivide" : ("br(eurobraille):dot3+dot4+backspace" ,),
218+ "kb:numpadMultiply" : ("br(eurobraille):dot3+dot5+backspace" ,),
219+ "kb:numpadMinus" : ("br(eurobraille):dot3+dot6+backspace" ,),
220+ "kb:numpadPlus" : ("br(eurobraille):dot2+dot3+dot5+backspace" ,),
221+ "kb:numpadEnter" : ("br(eurobraille):dot3+dot4+dot5+backspace" ,),
222+ "kb:escape" : (
223+ "br(eurobraille):dot1+dot2+dot4+dot5+space" ,
224+ "br(eurobraille):l2" ,
225+ ),
226+ "kb:tab" : (
227+ "br(eurobraille):dot2+dot5+dot6+space" ,
228+ "br(eurobraille):l3" ,
229+ ),
230+ "kb:shift+tab" : ("br(eurobraille):dot2+dot3+dot5+space" ,),
231+ "kb:printScreen" : ("br(eurobraille):dot1+dot3+dot4+dot6+space" ,),
232+ "kb:pause" : ("br(eurobraille):dot1+dot4+space" ,),
233+ "kb:applications" : ("br(eurobraille):dot5+dot6+backspace" ,),
234+ "kb:f1" : ("br(eurobraille):dot1+backspace" ,),
235+ "kb:f2" : ("br(eurobraille):dot1+dot2+backspace" ,),
236+ "kb:f3" : ("br(eurobraille):dot1+dot4+backspace" ,),
237+ "kb:f4" : ("br(eurobraille):dot1+dot4+dot5+backspace" ,),
238+ "kb:f5" : ("br(eurobraille):dot1+dot5+backspace" ,),
239+ "kb:f6" : ("br(eurobraille):dot1+dot2+dot4+backspace" ,),
240+ "kb:f7" : ("br(eurobraille):dot1+dot2+dot4+dot5+backspace" ,),
241+ "kb:f8" : ("br(eurobraille):dot1+dot2+dot5+backspace" ,),
242+ "kb:f9" : ("br(eurobraille):dot2+dot4+backspace" ,),
243+ "kb:f10" : ("br(eurobraille):dot2+dot4+dot5+backspace" ,),
244+ "kb:f11" : ("br(eurobraille):dot1+dot3+backspace" ,),
245+ "kb:f12" : ("br(eurobraille):dot1+dot2+dot3+backspace" ,),
246+ "kb:windows" : ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+space" ,),
247+ "kb:capsLock" : ("br(eurobraille):dot7+backspace" , "br(eurobraille):dot8+backspace" ,),
248+ "kb:numLock" : ("br(eurobraille):dot3+backspace" , "br(eurobraille):dot6+backspace" ,),
249+ "kb:shift" : ("br(eurobraille):dot1+dot7+space" , "br(eurobraille):dot4+dot7+space" ,),
250+ "braille_toggleShift" : ("br(eurobraille):dot7+space" ,"br(eurobraille):l4" ,),
251+ "kb:control" : ("br(eurobraille):dot1+dot7+dot8+space" , "br(eurobraille):dot4+dot7+dot8+space" ,),
252+ "braille_toggleControl" : ("br(eurobraille):dot7+dot8+space" ,"br(eurobraille):l5" ,),
253+ "kb:alt" : ("br(eurobraille):dot1+dot8+space" , "br(eurobraille):dot4+dot8+space" ),
254+ "braille_toggleAlt" : ("br(eurobraille):dot8+space" , "br(eurobraille):l6" ),
255+ "braille_toggleNVDAKey" : ("br(eurobraille):l7" , "br(eurobraille):dot3+dot5+space" ),
256+ "kb:control+home" : ("br(eurobraille):joystick2left+joystick2up" , "br(eurobraille):l1+l2+l3" , "br(eurobraille):l2+l3+l4" ,),
257+ "kb:control+end" : ("br(eurobraille):joystick2right+joystick2up" , "br(eurobraille):l6+l7+l8" , "br(eurobraille):l5+l6+l7" ,),
258+ "braille_toggleWindows" : ("br(eurobraille):backspace+dot1+dot2+dot3+dot4" , "br(eurobraille):dot2+dot4+dot5+dot6+space" ,),
259+ "kb:control+shift+e" : ("br(eurobraille):dot1+dot5+space" ,),
260+ },
261+ }
262+ default_GestureMap = inputCore .GlobalGestureMap (defaultGestureMapEntries )
137263
138264def bytesToInt (byteData : bytes ):
139265 """Converts bytes to its integral equivalent."""
@@ -146,7 +272,7 @@ class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
146272 _awaitingFrameReceipts : Dict [int , Any ]
147273 name = "eurobraille"
148274 # Translators: Names of braille displays.
149- description = _ ("Eurobraille Esys/Esytime/Iris displays" )
275+ description = _ ("Eurobraille displays" )
150276 isThreadSafe = True
151277 timeout = 0.2
152278 supportedSettings = (
@@ -211,6 +337,12 @@ def __init__(self, port="Auto"):
211337 # A display responded.
212338 log .info ("Found {device} connected via {type} ({port})" .format (
213339 device = self .deviceType , type = portType , port = port ))
340+ if self .deviceType .startswith (("b.note" , "b.book" )):
341+ # send identifier to bnote / bbook with current COM port
342+ comportNumber = f'{ int (re .match (".*?([0-9]+)$" , port ).group (1 )):02d} '
343+ identifier = f"NVDA/{ comportNumber } " .encode ()
344+ log .debugWarning (f"sending { identifier } to eurobraille display" )
345+ self ._sendPacket (EB_SYSTEM , EB_CONNECTION_NAME , identifier )
214346 break
215347 self ._dev .close ()
216348
@@ -222,6 +354,9 @@ def __init__(self, port="Auto"):
222354
223355 def terminate (self ):
224356 try :
357+ if self .deviceType .startswith (("b.note" , "b.book" )):
358+ # reset identifier to bnote / bbook with current COM port
359+ self ._sendPacket (EB_SYSTEM , EB_CONNECTION_NAME , b'' )
225360 super (BrailleDisplayDriver , self ).terminate ()
226361 finally :
227362 # 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 +448,10 @@ def _handleSystemPacket(self, packetType: bytes, data: bytes):
313448 self .keys = KEYS_ESYS
314449 elif 0x0e <= deviceType <= 0x11 : # Esitime
315450 self .keys = KEYS_ESITIME
451+ elif 0x12 <= deviceType <= 0x13 :
452+ self .keys = KEYS_BNOTE
453+ elif 0x14 <= deviceType <= 0x15 :
454+ self .keys = KEYS_BBOOK
316455 else :
317456 log .debugWarning ("Unknown device identifier %r" % data )
318457 elif packetType == EB_SYSTEM_DISPLAY_LENGTH :
@@ -453,125 +592,11 @@ def announceUnavailableMessage():
453592 "br(eurobraille):switch1Left+joystick1Down" : "toggleHidKeyboardInput" ,
454593 "br(eurobraille.esytime):l8+joystick1Down" : "toggleHidKeyboardInput" ,
455594 "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- })
595+ }\
596+
597+ gestureMap = default_GestureMap
598+
599+
575600
576601class InputGesture (braille .BrailleDisplayGesture , brailleInput .BrailleInputGesture ):
577602
@@ -581,6 +606,7 @@ def __init__(self, display):
581606 super (InputGesture , self ).__init__ ()
582607 self .model = display .deviceType .lower ().split (" " )[0 ]
583608 keysDown = dict (display .keysDown )
609+
584610 self .keyNames = names = []
585611 for group , groupKeysDown in keysDown .items ():
586612 if group == EB_KEY_BRAILLE :
0 commit comments