1+ #A part of NonVisual Desktop Access (NVDA)
2+ #This file is covered by the GNU General Public License.
3+ #See the file COPYING for more details.
4+ #Copyright (C) 2012/20 Ulf Beckmann <beckmann@flusoft.de>
5+ #20/02/10 8:57
6+
7+ # This file represents the braille display driver for
8+ # Seika Mini, a product from Nippon Telesoft
9+ # see www.seika-braille.com for more details
10+ # 09.06.2012
11+ # Dec14/Jan15 add BrilleInput
12+
13+ from typing import Optional , List
14+ from ctypes import *
15+ import time
16+ import wx
17+ import braille
18+ import brailleInput
19+ import inputCore
20+ import hwPortUtils
21+ import winUser
22+ import os
23+ from logHandler import log
24+
25+ READ_INTERVAL = 50
26+
27+ DOT_1 = 0x1
28+ DOT_2 = 0x2
29+ DOT_3 = 0x4
30+ DOT_4 = 0x8
31+ DOT_5 = 0x10
32+ DOT_6 = 0x20
33+ DOT_7 = 0x40
34+ DOT_8 = 0x80
35+
36+
37+ _keyNames = {
38+ 0x000001 : "BACKSPACE" ,
39+ 0x000002 : "SPACE" ,
40+ 0x000004 : "LB" ,
41+ 0x000008 : "RB" ,
42+ 0x000010 : "LJ_CENTER" ,
43+ 0x000020 : "LJ_LEFT" ,
44+ 0x000040 : "LJ_RIGHT" ,
45+ 0x000080 : "LJ_UP" ,
46+ 0x000100 : "LJ_DOWN" ,
47+ 0x000200 : "RJ_CENTER" ,
48+ 0x000400 : "RJ_LEFT" ,
49+ 0x000800 : "RJ_RIGHT" ,
50+ 0x001000 : "RJ_UP" ,
51+ 0x002000 : "RJ_DOWN"
52+ }
53+
54+ _dotNames = {}
55+ for i in range (1 ,9 ):
56+ key = globals ()["DOT_%d" % i ]
57+ _dotNames [key ] = "d%d" % i
58+
59+
60+ # try to load the SeikaDevice.dll
61+ # first get the path of the SeikaMini.py
62+ # get the current work directory
63+ # change to this directory
64+ # load the .dll, the SeikaDevice.dll can also load by Pathname + dllname
65+ # but not the SLABxxx.dll's
66+ # after loading, set the path back to the work path
67+
68+ BASE_PATH = os .path .dirname (__file__ )
69+ DLLNAME = "Seika\\ SeikaDevice.dll"
70+ WORK_PATH = os .getcwd ()
71+
72+ if not os .path .isfile (BASE_PATH + "\\ " + DLLNAME ):
73+ BASE_PATH = "brailleDisplayDrivers"
74+ os .chdir (BASE_PATH )
75+ try :
76+ seikaDll = cdll .LoadLibrary (DLLNAME )
77+ except :
78+ seikaDll = None
79+ log .info ("LoadLibrary failed " + DLLNAME )
80+ os .chdir (WORK_PATH )
81+
82+ class BrailleDisplayDriver (braille .BrailleDisplayDriver ):
83+ name = "seikamini"
84+ description = "Seika Notetaker"
85+
86+ numCells = 0
87+ # numBtns = 0
88+
89+ @classmethod
90+ def check (cls ):
91+ return bool (seikaDll )
92+
93+ def seika_errcheck (res , func , args ):
94+ if res != 0 :
95+ raise RuntimeError ("seikamini: %s: code %d" % (func .__name__ , res ))
96+ return res
97+
98+ def __init__ (self ):
99+ super (BrailleDisplayDriver , self ).__init__ ()
100+ pint = c_int * 1
101+ nCells = pint (0 )
102+ nBut = pint (0 )
103+ pN = ""
104+
105+ # seikaDll.BrailleOpen.errcheck=self.seika_errcheck
106+ seikaDll .BrailleOpen .restype = c_int
107+ seikaDll .BrailleOpen .argtype = (c_int ,c_int )
108+
109+ # seikaDll.GetBrailleDisplayInfo.errcheck=self.seika_errcheck
110+ seikaDll .GetBrailleDisplayInfo .restype = c_int
111+ seikaDll .GetBrailleDisplayInfo .argtype = (c_void_p ,c_void_p )
112+
113+ # seikaDll.UpdateBrailleDisplay.errcheck=self.seika_errcheck
114+ seikaDll .UpdateBrailleDisplay .restype = c_int
115+ seikaDll .UpdateBrailleDisplay .argtype = (POINTER (c_ubyte ),c_int )
116+
117+ # seikaDll.GetBrailleKey.errcheck=self.seika_errcheck
118+ seikaDll .GetBrailleKey .restype = c_int
119+ seikaDll .GetBrailleKey .argtype = (c_void_p ,c_void_p )
120+
121+ seikaDll .BrailleClose .restype = c_int
122+
123+ if seikaDll .BrailleOpen (2 ,0 ): # test USB
124+ seikaDll .GetBrailleDisplayInfo (nCells ,nBut )
125+ log .info ("seikamini an USB-HID, Cells {c} Buttons {b}" .format (c = nCells [0 ], b = nBut [0 ]))
126+ self .numCells = nCells [0 ]
127+ #
128+ else : # search the blutooth ports
129+ for portInfo in sorted (hwPortUtils .listComPorts (onlyAvailable = True ), key = lambda item : "bluetoothName" in item ):
130+ port = portInfo ["port" ]
131+ hwID = portInfo ["hardwareID" ]
132+ if not hwID .startswith (r"BTHENUM" ): # Bluetooth Ports
133+ continue
134+ bName = ""
135+ try :
136+ bName = portInfo ["bluetoothName" ]
137+ except KeyError :
138+ continue
139+ if not bName .startswith (r"TSM" ): # seikamini and then the 4-Digits
140+ continue
141+
142+ try :
143+ pN = port .split ("COM" )[1 ]
144+ except IndexError :
145+ pN = "0"
146+ portNum = int (pN ,10 )
147+ log .info ("seikamini test {c}, {b}" .format (c = port , b = bName ))
148+ if seikaDll .BrailleOpen (0 ,portNum ):
149+ seikaDll .GetBrailleDisplayInfo (nCells ,nBut )
150+ log .info ("seikamini via Bluetooth {p} Cells {c} Buttons {b}" .format (p = port ,c = nCells [0 ], b = nBut [0 ]))
151+ self .numCells = nCells [0 ]
152+ # self.numBtns=nBut[0]
153+ break
154+ else :
155+ raise RuntimeError ("No MINI-SEIKA display found" )
156+ self ._readTimer = wx .PyTimer (self .handleResponses )
157+ self ._readTimer .Start (READ_INTERVAL )
158+
159+ def terminate (self ):
160+ try :
161+ super (BrailleDisplayDriver , self ).terminate ()
162+ self ._readTimer .Stop ()
163+ self ._readTimer = None
164+ finally :
165+ seikaDll .BrailleClose ()
166+
167+ def display (self , cells : List [int ]):
168+ # cells will already be padded up to numCells.
169+ cellBytes = bytes (cells )
170+ seikaDll .UpdateBrailleDisplay (cellBytes ,self .numCells )
171+
172+ def handleResponses (self ):
173+ pint = c_int * 1
174+ nKey = pint (0 )
175+ nRou = pint (0 )
176+ Key = 0
177+ Brl = 0
178+ Rou = 0
179+ Btn = 0
180+ keys = set ()
181+ if seikaDll .GetBrailleKey (nKey ,nRou ):
182+ Rou = nRou [0 ]
183+ Btn = (nKey [0 ] & 0xff ) << 16
184+ Brl = (nKey [0 ] >> 8 ) & 0xff
185+ Key = (nKey [0 ] >> 16 ) & 0xffff
186+ space = (nKey [0 ] >> 16 ) & 0x2
187+ log .info ("GBK Key{c}-Brl{a}-Routing{b}" .format (c = Key , b = Rou , a = Brl ))
188+ # log.info("Seika Brl {brl} Key {c} Buttons {b} Route {r}".format(brl=Brl, c=Key, b=Btn, r=Rou))
189+ if not (Rou or Key or Btn or Brl ):
190+ pass
191+ if Rou : # Routing key is pressed
192+ gesture = InputGestureRouting (Rou - 1 )
193+ try :
194+ inputCore .manager .executeGesture (gesture )
195+ except inputCore .NoInputGestureAction :
196+ log .debug ("No Action for routing command" )
197+ pass
198+
199+ if Key : # Mini Seika has 2 Top and 4 Front ....
200+ gesture = InputGesture (keys = Key )
201+ if Btn : # Mini Seika has no Btn ....
202+ gesture = InputGesture (keys = Btn )
203+ if Brl : # or how to handle Brailleinput?
204+ gesture = InputGesture (dots = Brl )
205+ if Key or Btn or Brl :
206+ try :
207+ inputCore .manager .executeGesture (gesture )
208+ except inputCore .NoInputGestureAction :
209+ log .debug ("No Action for keys " )
210+ pass
211+
212+
213+
214+ gestureMap = inputCore .GlobalGestureMap ({
215+ "globalCommands.GlobalCommands" : {
216+ "braille_routeTo" : ("br(seikamini):routing" ,),
217+ "braille_scrollBack" : ("br(seikamini):LB" ,),
218+ "braille_scrollForward" : ("br(seikamini):RB" ,),
219+ "braille_previousLine" : ("br(seikamini):LJ_UP" ,),
220+ "braille_nextLine" : ("br(seikamini):LJ_DOWN" ,),
221+ "braille_toggleTether" : ("br(seikamini):LJ_CENTER" ,),
222+ "sayAll" : ("br(seikamini):SPACE+BACKSPACE" ,),
223+ "showGui" : ("br(seikamini):RB+LB" ,),
224+ "kb:tab" : ("br(seikamini):LJ_RIGHT" ,),
225+ "kb:shift+tab" : ("br(seikamini):LJ_LEFT" ,),
226+ "kb:upArrow" : ("br(seikamini):RJ_UP" ,),
227+ "kb:downArrow" : ("br(seikamini):RJ_DOWN" ,),
228+ "kb:leftArrow" : ("br(seikamini):RJ_LEFT" ,),
229+ "kb:rightArrow" : ("br(seikamini):RJ_RIGHT" ,),
230+ "kb:shift+upArrow" : ("br(seikamini):SPACE+RJ_UP" ,),
231+ "kb:shift+downArrow" : ("br(seikamini):SPACE+RJ_DOWN" ,),
232+ "kb:shift+leftArrow" : ("br(seikamini):SPACE+RJ_LEFT" ,),
233+ "kb:shift+rightArrow" : ("br(seikamini):SPACE+RJ_RIGHT" ,),
234+ "kb:escape" : ("br(seikamini):SPACE+RJ_CENTER" ,),
235+ "kb:shift+upArrow" : ("br(seikamini):BACKSPACE+RJ_UP" ,),
236+ "kb:shift+downArrow" : ("br(seikamini):BACKSPACE+RJ_DOWN" ,),
237+ "kb:shift+leftArrow" : ("br(seikamini):BACKSPACE+RJ_LEFT" ,),
238+ "kb:shift+rightArrow" : ("br(seikamini):BACKSPACE+RJ_RIGHT" ,),
239+ "kb:windows" : ("br(seikamini):BACKSPACE+RJ_CENTER" ,),
240+ "kb:space" : ("br(seikamini):BACKSPACE" ,"br(seikamini):SPACE" ,),
241+ "kb:backspace" : ("br(seikamini):d7" ,),
242+ "kb:pageup" : ("br(seikamini):SPACE+LJ_RIGHT" ,),
243+ "kb:pagedown" : ("br(seikamini):SPACE+LJ_LEFT" ,),
244+ "kb:home" : ("br(seikamini):SPACE+LJ_UP" ,),
245+ "kb:end" : ("br(seikamini):SPACE+LJ_DOWN" ,),
246+ "kb:control+home" : ("br(seikamini):BACKSPACE+LJ_UP" ,),
247+ "kb:control+end" : ("br(seikamini):BACKSPACE+LJ_DOWN" ,),
248+ "kb:enter" : ("br(seikamini):RJ_CENTER" ,"br(seikamini):d8" ),
249+ },
250+ })
251+
252+
253+ class InputGestureRouting (braille .BrailleDisplayGesture ):
254+
255+ source = BrailleDisplayDriver .name
256+ def __init__ (self , index ):
257+ super (InputGestureRouting , self ).__init__ ()
258+ self .id = "routing"
259+ self .routingIndex = index
260+
261+ class InputGesture (braille .BrailleDisplayGesture , brailleInput .BrailleInputGesture ):
262+ source = BrailleDisplayDriver .name
263+
264+ def __init__ (self , keys = None , dots = None , space = False , routing = None ):
265+ super (braille .BrailleDisplayGesture , self ).__init__ ()
266+ # see what thumb keys are pressed:
267+ names = set ()
268+ if keys is not None :
269+ names .update (_keyNames [1 << i ] for i in range (22 )
270+ if (1 << i ) & keys )
271+ elif dots is not None :
272+ # now the dots
273+ self .dots = dots
274+ if space :
275+ self .space = space
276+ names .add (_keyNames [0 ])
277+ names .update (_dotNames [1 << i ] for i in range (8 )
278+ if (1 << i ) & dots )
279+ elif routing is not None :
280+ self .routingIndex = routing
281+ names .add ('routing' )
282+ self .id = "+" .join (names )
283+ # log.info("keys {keys}".format(keys=names))
0 commit comments