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