Skip to content

Commit f62982f

Browse files
authored
Merge 192585b into 1ef4dfb
2 parents 1ef4dfb + 192585b commit f62982f

1 file changed

Lines changed: 279 additions & 0 deletions

File tree

Lines changed: 279 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,279 @@
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+
#
6+
# This file represents the braille display driver for
7+
# Seika Notetaker, a product from Nippon Telesoft
8+
# see www.seika-braille.com for more details
9+
# 29.06.2020 / 12:36
10+
11+
from io import BytesIO
12+
from typing import Union, List, Optional
13+
from ctypes import *
14+
import time
15+
import wx
16+
import braille
17+
import brailleInput
18+
import inputCore
19+
import hwPortUtils
20+
import bdDetect
21+
import hwIo
22+
from serial.win32 import INVALID_HANDLE_VALUE
23+
from logHandler import log
24+
import config
25+
26+
TIMEOUT = 0.2
27+
28+
DOT_1 = 0x1
29+
DOT_2 = 0x2
30+
DOT_3 = 0x4
31+
DOT_4 = 0x8
32+
DOT_5 = 0x10
33+
DOT_6 = 0x20
34+
DOT_7 = 0x40
35+
DOT_8 = 0x80
36+
37+
38+
_keyNames = {
39+
0x000001 : "BACKSPACE",
40+
0x000002 : "SPACE",
41+
0x000004 : "LB",
42+
0x000008 : "RB",
43+
0x000010 : "LJ_CENTER",
44+
0x000020 : "LJ_LEFT",
45+
0x000040 : "LJ_RIGHT",
46+
0x000080 : "LJ_UP",
47+
0x000100 : "LJ_DOWN",
48+
0x000200 : "RJ_CENTER",
49+
0x000400 : "RJ_LEFT",
50+
0x000800 : "RJ_RIGHT",
51+
0x001000 : "RJ_UP",
52+
0x002000 : "RJ_DOWN"
53+
}
54+
55+
SEIKA_REQUEST_INFO = b"\x03\xff\xff\xa1"
56+
SEIKA_INFO = b"\xff\xff\xa2"
57+
SEIKA_SEND_TEXT = b"\x2c\xff\xff\xa3"
58+
SEIKA_ROUTING = b"\xff\xff\xa4"
59+
SEIKA_KEYS = b"\xff\xff\xa6"
60+
SEIKA_KEYS_ROU = b"\xff\xff\xa8"
61+
62+
SEIKA_CONFIG = b"\x50\x00\x00\x25\x80\x00\x00\x03\x00"
63+
SEIKA_CMD_ON = b"\x41\x01"
64+
65+
vidpid ="VID_10C4&PID_EA80"
66+
hidvidpid ="HID\\VID_10C4&PID_EA80"
67+
SEIKA_NAME = "seikantk"
68+
69+
_dotNames = {}
70+
for i in range(1,9):
71+
key = globals()["DOT_%d" % i]
72+
_dotNames[key] = "d%d" % i
73+
bdDetect.addUsbDevices(SEIKA_NAME, bdDetect.KEY_HID, {
74+
vidpid, # USB-HID adapter
75+
})
76+
77+
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
78+
_dev: hwIo.IoBase
79+
name = SEIKA_NAME
80+
description = "Seika Notetaker"
81+
path = ""
82+
isThreadSafe = True
83+
for d in hwPortUtils.listHidDevices():
84+
if d["hardwareID"].startswith(hidvidpid):
85+
path = d["devicePath"]
86+
#log.info("Devicepath: "+path)
87+
88+
@classmethod
89+
def check(cls):
90+
return True
91+
92+
@classmethod
93+
def getManualPorts(cls):
94+
return path
95+
96+
97+
def __init__(self, port="hid"):
98+
super(BrailleDisplayDriver, self).__init__()
99+
self.numCells = 0
100+
self.numBtns = 0
101+
self.status = 0
102+
self.cmdlen = 0
103+
self.handle = None
104+
self._hidBuffer = b""
105+
log.info("Versuch: "+self.path)
106+
# for portType, portId, port, portInfo in self._getTryPorts(port):
107+
# log.info("port:"+port)
108+
if self.path == "":
109+
raise RuntimeError("No MINI-SEIKA display found, no path found")
110+
self._dev = hwIo.Hid(path=self.path, onReceive=self._onReceive)
111+
if self._dev._file == INVALID_HANDLE_VALUE:
112+
raise RuntimeError("No MINI-SEIKA display found, open error")
113+
self._dev.setFeature(SEIKA_CONFIG) # baudrate, stopbit usw
114+
self._dev.setFeature(SEIKA_CMD_ON) # device on
115+
self._dev.write(SEIKA_REQUEST_INFO) # Request the Info from the device
116+
for i in range(30): # the info-block is about
117+
self._dev.waitForRead(TIMEOUT)
118+
if self.numCells:
119+
log.info("Seikanotetaker an USB-HID, Cells {c} Buttons {b}".format(c=self.numCells,b=self.numBtns))
120+
config.conf["braille"]["display"] = SEIKA_NAME
121+
break
122+
123+
if self.numCells==0:
124+
self._dev.close()
125+
raise RuntimeError("No MINI-SEIKA display found, no response")
126+
127+
def terminate(self):
128+
try:
129+
super(BrailleDisplayDriver, self).terminate()
130+
finally:
131+
self._dev.close()
132+
133+
def display(self, cells: List[int]):
134+
# cells will already be padded up to numCells.
135+
cellBytes = SEIKA_SEND_TEXT +self.numCells.to_bytes(1,'little') +bytes(cells)
136+
self._dev.write(cellBytes)
137+
138+
def _onReceive(self, data: bytes):
139+
stream = BytesIO(data)
140+
cmd = stream.read(3)
141+
self._hidBuffer += cmd[1:2]
142+
if len(self._hidBuffer) == 3:
143+
self.status = 1
144+
elif len(self._hidBuffer) == 4:
145+
self.cmdlen = cmd[1]
146+
self.status = 2
147+
elif self.status == 2 and len(self._hidBuffer) == self.cmdlen+4:
148+
command = self._hidBuffer[0:3]
149+
arg = self._hidBuffer[3:self.cmdlen+4]
150+
self.status = 0
151+
self._hidBuffer=b""
152+
# log.info("auswerten nach cmd"+"".join(format(x, ' 02X') for x in command)+" ... "+"".join(format(x, ' 02X') for x in arg));
153+
if command == SEIKA_INFO:
154+
self._handInfo(arg)
155+
elif command == SEIKA_ROUTING:
156+
self._handRouting(arg)
157+
elif command == SEIKA_KEYS:
158+
self._handKeys(arg)
159+
elif command == SEIKA_KEYS_ROU:
160+
self._handKeysRouting(arg)
161+
else:
162+
log.debug("hier haette ich nicht ankommen sollen.")
163+
164+
def _handInfo(self, arg: bytes):
165+
self.numCells = arg[2]
166+
self.numBtns = arg[1]
167+
168+
def _handRouting(self, arg: bytes):
169+
Rou = 0
170+
#log.info("Rou: "+""+"".join(format(x, ' 02X') for x in arg));
171+
for i in range(arg[0]):
172+
for j in range(8):
173+
if arg[i+1] & (1<<j):
174+
Rou = i*8+j
175+
# log.info("Routing: {c}".format(c=Rou))
176+
gesture = InputGestureRouting(Rou)
177+
try:
178+
inputCore.manager.executeGesture(gesture)
179+
except inputCore.NoInputGestureAction:
180+
log.debug("No Action for routing command")
181+
pass
182+
183+
def _handKeys(self, arg: bytes):
184+
#log.info("Keys: "+""+"".join(format(x, ' 02X') for x in arg));
185+
Brl = arg[1]
186+
Key = arg[2] | (arg[3] << 8)
187+
space = arg[2] & 2
188+
Btn = 0 # Seika has no button
189+
if not (Key or Btn or Brl):
190+
pass
191+
if Key: # Mini Seika has 2 Top and 4 Front ....
192+
gesture = InputGesture(keys=Key)
193+
if Btn: # Mini Seika has no Btn ....
194+
gesture = InputGesture(keys=Btn)
195+
if Brl: # or how to handle Brailleinput?
196+
gesture = InputGesture(dots=Brl)
197+
if Key or Btn or Brl:
198+
try:
199+
inputCore.manager.executeGesture(gesture)
200+
except inputCore.NoInputGestureAction:
201+
log.debug("No Action for keys ")
202+
pass
203+
204+
def _handKeysRouting(self, arg: bytes):
205+
argk = b"\x03" + arg[1:]
206+
argr = (arg[0]-3).to_bytes(1,'little')+ arg[4:]
207+
self._handRouting(argr)
208+
self._handKeys(argk)
209+
210+
gestureMap = inputCore.GlobalGestureMap({
211+
"globalCommands.GlobalCommands": {
212+
"braille_routeTo": ("br(seikantk):routing",),
213+
"braille_scrollBack": ("br(seikantk):LB",),
214+
"braille_scrollForward": ("br(seikantk):RB",),
215+
"braille_previousLine": ("br(seikantk):LJ_UP",),
216+
"braille_nextLine": ("br(seikantk):LJ_DOWN",),
217+
"braille_toggleTether": ("br(seikantk):LJ_CENTER",),
218+
"sayAll": ("br(seikantk):SPACE+BACKSPACE",),
219+
"showGui": ("br(seikantk):RB+LB",),
220+
"kb:tab": ("br(seikantk):LJ_RIGHT",),
221+
"kb:shift+tab": ("br(seikantk):LJ_LEFT",),
222+
"kb:upArrow": ("br(seikantk):RJ_UP",),
223+
"kb:downArrow": ("br(seikantk):RJ_DOWN",),
224+
"kb:leftArrow": ("br(seikantk):RJ_LEFT",),
225+
"kb:rightArrow": ("br(seikantk):RJ_RIGHT",),
226+
"kb:shift+upArrow": ("br(seikantk):SPACE+RJ_UP",),
227+
"kb:shift+downArrow": ("br(seikantk):SPACE+RJ_DOWN",),
228+
"kb:shift+leftArrow": ("br(seikantk):SPACE+RJ_LEFT",),
229+
"kb:shift+rightArrow": ("br(seikantk):SPACE+RJ_RIGHT",),
230+
"kb:escape": ("br(seikantk):SPACE+RJ_CENTER",),
231+
"kb:shift+upArrow": ("br(seikantk):BACKSPACE+RJ_UP",),
232+
"kb:shift+downArrow": ("br(seikantk):BACKSPACE+RJ_DOWN",),
233+
"kb:shift+leftArrow": ("br(seikantk):BACKSPACE+RJ_LEFT",),
234+
"kb:shift+rightArrow": ("br(seikantk):BACKSPACE+RJ_RIGHT",),
235+
"kb:windows": ("br(seikantk):BACKSPACE+RJ_CENTER",),
236+
"kb:space": ("br(seikantk):BACKSPACE","br(seikantk):SPACE",),
237+
"kb:backspace": ("br(seikantk):d7",),
238+
"kb:pageup": ("br(seikantk):SPACE+LJ_RIGHT",),
239+
"kb:pagedown": ("br(seikantk):SPACE+LJ_LEFT",),
240+
"kb:home": ("br(seikantk):SPACE+LJ_UP",),
241+
"kb:end": ("br(seikantk):SPACE+LJ_DOWN",),
242+
"kb:control+home": ("br(seikantk):BACKSPACE+LJ_UP",),
243+
"kb:control+end": ("br(seikantk):BACKSPACE+LJ_DOWN",),
244+
"kb:enter": ("br(seikantk):RJ_CENTER","br(seikantk):d8"),
245+
},
246+
})
247+
248+
class InputGestureRouting(braille.BrailleDisplayGesture):
249+
source = BrailleDisplayDriver.name
250+
251+
def __init__(self, index):
252+
super(InputGestureRouting, self).__init__()
253+
self.id = "routing"
254+
self.routingIndex = index
255+
256+
class InputGesture(braille.BrailleDisplayGesture, brailleInput.BrailleInputGesture):
257+
source = BrailleDisplayDriver.name
258+
259+
def __init__(self, keys=None, dots=None, space=False, routing=None):
260+
super(braille.BrailleDisplayGesture, self).__init__()
261+
# see what thumb keys are pressed:
262+
names = set()
263+
if keys is not None:
264+
names.update(_keyNames[1 << i] for i in range(22)
265+
if (1 << i) & keys)
266+
elif dots is not None:
267+
# now the dots
268+
self.dots = dots
269+
if space:
270+
self.space = space
271+
names.add(_keyNames[1])
272+
names.update(_dotNames[1 << i] for i in range(8)
273+
if (1 << i) & dots)
274+
elif routing is not None:
275+
self.routingIndex = routing
276+
names.add('routing')
277+
self.id = "+".join(names)
278+
#log.info("SNT: keys {keys}".format(keys=names))
279+

0 commit comments

Comments
 (0)