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+
83+ class BrailleDisplayDriver (braille .BrailleDisplayDriver ):
84+ name = "seikamini"
85+ description = "Seika Notetaker"
86+
87+ numCells = 0
88+ # numBtns = 0
89+
90+ @classmethod
91+ def check (cls ):
92+ return bool (seikaDll )
93+
94+ def seika_errcheck (res , func , args ):
95+ if res != 0 :
96+ raise RuntimeError ("seikamini: %s: code %d" % (func .__name__ , res ))
97+ return res
98+
99+ def __init__ (self ):
100+ super (BrailleDisplayDriver , self ).__init__ ()
101+ pint = c_int * 1
102+ nCells = pint (0 )
103+ nBut = pint (0 )
104+ pN = ""
105+
106+ # seikaDll.BrailleOpen.errcheck=self.seika_errcheck
107+ seikaDll .BrailleOpen .restype = c_int
108+ seikaDll .BrailleOpen .argtype = (c_int , c_int )
109+
110+ # seikaDll.GetBrailleDisplayInfo.errcheck=self.seika_errcheck
111+ seikaDll .GetBrailleDisplayInfo .restype = c_int
112+ seikaDll .GetBrailleDisplayInfo .argtype = (c_void_p , c_void_p )
113+
114+ # seikaDll.UpdateBrailleDisplay.errcheck=self.seika_errcheck
115+ seikaDll .UpdateBrailleDisplay .restype = c_int
116+ seikaDll .UpdateBrailleDisplay .argtype = (POINTER (c_ubyte ), c_int )
117+
118+ # seikaDll.GetBrailleKey.errcheck=self.seika_errcheck
119+ seikaDll .GetBrailleKey .restype = c_int
120+ seikaDll .GetBrailleKey .argtype = (c_void_p , c_void_p )
121+
122+ seikaDll .BrailleClose .restype = c_int
123+
124+ if seikaDll .BrailleOpen (2 ,0 ): # test USB
125+ seikaDll .GetBrailleDisplayInfo (nCells , nBut )
126+ log .info ("seikamini an USB-HID, Cells {c} Buttons {b}" .format (c = nCells [0 ], b = nBut [0 ]))
127+ self .numCells = nCells [0 ]
128+ #
129+ else : # search the blutooth ports
130+ for portInfo in sorted (hwPortUtils .listComPorts (onlyAvailable = True ), key = lambda item : "bluetoothName" in item ):
131+ port = portInfo ["port" ]
132+ hwID = portInfo ["hardwareID" ]
133+ if not hwID .startswith (r"BTHENUM" ): # Bluetooth Ports
134+ continue
135+ bName = ""
136+ try :
137+ bName = portInfo ["bluetoothName" ]
138+ except KeyError :
139+ continue
140+ if not bName .startswith (r"TSM" ): # seikamini and then the 4-Digits
141+ continue
142+
143+ try :
144+ pN = port .split ("COM" )[1 ]
145+ except IndexError :
146+ pN = "0"
147+ portNum = int (pN , 10 )
148+ log .info ("seikamini test {c}, {b}" .format (c = port , b = bName ))
149+ if seikaDll .BrailleOpen (0 , portNum ):
150+ seikaDll .GetBrailleDisplayInfo (nCells , nBut )
151+ log .info ("seikamini via Bluetooth {p} Cells {c} Buttons {b}" .format (p = port , c = nCells [0 ], b = nBut [0 ]))
152+ self .numCells = nCells [0 ]
153+ # self.numBtns=nBut[0]
154+ break
155+ else :
156+ raise RuntimeError ("No MINI-SEIKA display found" )
157+ self ._readTimer = wx .PyTimer (self .handleResponses )
158+ self ._readTimer .Start (READ_INTERVAL )
159+
160+ def terminate (self ):
161+ try :
162+ super (BrailleDisplayDriver , self ).terminate ()
163+ self ._readTimer .Stop ()
164+ self ._readTimer = None
165+ finally :
166+ seikaDll .BrailleClose ()
167+
168+ def display (self , cells : List [int ]):
169+ # cells will already be padded up to numCells.
170+ cellBytes = bytes (cells )
171+ seikaDll .UpdateBrailleDisplay (cellBytes ,self .numCells )
172+
173+ def handleResponses (self ):
174+ pint = c_int * 1
175+ nKey = pint (0 )
176+ nRou = pint (0 )
177+ Key = 0
178+ Brl = 0
179+ Rou = 0
180+ Btn = 0
181+ keys = set ()
182+ if seikaDll .GetBrailleKey (nKey ,nRou ):
183+ Rou = nRou [0 ]
184+ Btn = (nKey [0 ] & 0xff ) << 16
185+ Brl = (nKey [0 ] >> 8 ) & 0xff
186+ Key = (nKey [0 ] >> 16 ) & 0xffff
187+ space = (nKey [0 ] >> 16 ) & 0x2
188+ log .info ("GBK Key{c}-Brl{a}-Routing{b}" .format (c = Key , b = Rou , a = Brl ))
189+ # log.info("Seika Brl {brl} Key {c} Buttons {b} Route {r}".format(brl=Brl, c=Key, b=Btn, r=Rou))
190+ if not (Rou or Key or Btn or Brl ):
191+ pass
192+ if Rou : # Routing key is pressed
193+ gesture = InputGestureRouting (Rou - 1 )
194+ try :
195+ inputCore .manager .executeGesture (gesture )
196+ except inputCore .NoInputGestureAction :
197+ log .debug ("No Action for routing command" )
198+ pass
199+
200+ if Key : # Mini Seika has 2 Top and 4 Front ....
201+ gesture = InputGesture (keys = Key )
202+ if Btn : # Mini Seika has no Btn ....
203+ gesture = InputGesture (keys = Btn )
204+ if Brl : # or how to handle Brailleinput?
205+ gesture = InputGesture (dots = Brl )
206+ if Key or Btn or Brl :
207+ try :
208+ inputCore .manager .executeGesture (gesture )
209+ except inputCore .NoInputGestureAction :
210+ log .debug ("No Action for keys " )
211+ pass
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+
257+ def __init__ (self , index ):
258+ super (InputGestureRouting , self ).__init__ ()
259+ self .id = "routing"
260+ self .routingIndex = index
261+
262+
263+ class InputGesture (braille .BrailleDisplayGesture , brailleInput .BrailleInputGesture ):
264+ source = BrailleDisplayDriver .name
265+
266+ def __init__ (self , keys = None , dots = None , space = False , routing = None ):
267+ super (braille .BrailleDisplayGesture , self ).__init__ ()
268+ # see what thumb keys are pressed:
269+ names = set ()
270+ if keys is not None :
271+ names .update (_keyNames [1 << i ] for i in range (22 )
272+ if (1 << i ) & keys )
273+ elif dots is not None :
274+ # now the dots
275+ self .dots = dots
276+ if space :
277+ self .space = space
278+ names .add (_keyNames [0 ])
279+ names .update (_dotNames [1 << i ] for i in range (8 )
280+ if (1 << i ) & dots )
281+ elif routing is not None :
282+ self .routingIndex = routing
283+ names .add ('routing' )
284+ self .id = "+" .join (names )
285+ # log.info("keys {keys}".format(keys=names))
0 commit comments