1
#brailleDisplayDrivers/baum.py
2
#A part of NonVisual Desktop Access (NVDA)
3
#This file is covered by the GNU General Public License.
4
#See the file COPYING for more details.
5
#Copyright (C) 2010-2011 James Teh <jamie@jantrid.net>
13
from logHandler import log
21
BAUM_DISPLAY_DATA = "\x01"
22
BAUM_CELL_COUNT = "\x01"
23
BAUM_PROTOCOL_ONOFF = "\x15"
24
BAUM_COMMUNICATION_CHANNEL = "\x16"
25
BAUM_POWERDOWN = "\x17"
26
BAUM_ROUTING_KEYS = "\x22"
27
BAUM_DISPLAY_KEYS = "\x24"
28
BAUM_BRAILLE_KEYS = "\x33"
29
BAUM_JOYSTICK_KEYS = "\x34"
30
BAUM_DEVICE_ID = "\x84"
31
BAUM_SERIAL_NUMBER = "\x8A"
36
BAUM_COMMUNICATION_CHANNEL: 1,
39
BAUM_JOYSTICK_KEYS: 1,
41
BAUM_SERIAL_NUMBER: 8,
45
BAUM_ROUTING_KEYS: None,
46
BAUM_DISPLAY_KEYS: ("d1", "d2", "d3", "d4", "d5", "d6"),
47
BAUM_BRAILLE_KEYS: ("b9", "b10", "b11", None, "c1", "c2", "c3", "c4", # byte 1
48
"b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8"), # byte 2
49
BAUM_JOYSTICK_KEYS: ("up", "left", "down", "right", "select"),
53
"VID_0403&PID_FE70", # Vario 40
54
"VID_0403&PID_FE71", # PocketVario
55
"VID_0403&PID_FE72", # SuperVario/Brailliant 40
56
"VID_0403&PID_FE73", # SuperVario/Brailliant 32
57
"VID_0403&PID_FE74", # SuperVario/Brailliant 64
58
"VID_0403&PID_FE75", # SuperVario/Brailliant 80
59
"VID_0403&PID_FE76", # VarioPro 80
60
"VID_0403&PID_FE77", # VarioPro 64
61
"VID_0904&PID_2000", # VarioPro 40
62
"VID_0904&PID_2001", # EcoVario 24
63
"VID_0904&PID_2002", # EcoVario 40
64
"VID_0904&PID_2007", # VarioConnect/BrailleConnect 40
65
"VID_0904&PID_2008", # VarioConnect/BrailleConnect 32
66
"VID_0904&PID_2009", # VarioConnect/BrailleConnect 24
67
"VID_0904&PID_2010", # VarioConnect/BrailleConnect 64
68
"VID_0904&PID_2011", # VarioConnect/BrailleConnect 80
69
"VID_0904&PID_2014", # EcoVario 32
70
"VID_0904&PID_2015", # EcoVario 64
71
"VID_0904&PID_2016", # EcoVario 80
72
"VID_0904&PID_3000", # RefreshaBraille 18
85
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
87
description = _("Baum/HumanWare/APH braille displays")
94
super(BrailleDisplayDriver, self).__init__()
98
# Scan all available com ports.
99
# Try bluetooth ports last.
100
for portInfo in sorted(hwPortUtils.listComPorts(onlyAvailable=True), key=lambda item: "bluetoothName" in item):
101
port = portInfo["port"]
102
hwID = portInfo["hardwareID"]
103
if hwID.startswith(r"FTDIBUS\COMPORT"):
107
usbID = hwID.split("&", 1)[1]
110
if usbID not in USB_IDS:
112
elif "bluetoothName" in portInfo:
114
portType = "bluetooth"
115
btName = portInfo["bluetoothName"]
116
if not any(btName.startswith(prefix) for prefix in BLUETOOTH_NAMES):
121
# At this point, a port bound to this display has been found.
122
# Try talking to the display.
124
self._ser = serial.Serial(port, baudrate=BAUD_RATE, timeout=TIMEOUT, writeTimeout=TIMEOUT)
125
except serial.SerialException:
127
# This will cause the number of cells to be returned.
128
self._sendRequest(BAUM_DISPLAY_DATA)
129
# Send again in case the display misses the first one.
130
self._sendRequest(BAUM_DISPLAY_DATA)
131
# We just sent less bytes than we should,
132
# so we need to send another request in order for the display to know the previous request is finished.
133
self._sendRequest(BAUM_DEVICE_ID)
134
self._handleResponses(wait=True)
136
# A display responded.
137
if not self._deviceID:
138
# Bah. The response to our device ID query hasn't arrived yet, so wait for it.
139
self._handleResponses(wait=True)
140
log.info("Found {device} connected via {type} ({port})".format(
141
device=self._deviceID, type=portType, port=port))
145
raise RuntimeError("No Baum display found")
147
self._readTimer = wx.PyTimer(self._handleResponses)
148
self._readTimer.Start(READ_INTERVAL)
150
self._ignoreKeyReleases = False
154
super(BrailleDisplayDriver, self).terminate()
155
self._readTimer.Stop()
156
self._readTimer = None
157
self._sendRequest(BAUM_PROTOCOL_ONOFF, False)
159
# We absolutely must close the Serial object, as it does not have a destructor.
160
# If we don't, we won't be able to re-open it later.
163
def _sendRequest(self, command, arg=""):
164
if isinstance(arg, (int, bool)):
166
self._ser.write("\x1b{command}{arg}".format(command=command,
167
arg=arg.replace(ESCAPE, ESCAPE * 2)))
169
def _handleResponses(self, wait=False):
170
while wait or self._ser.inWaiting():
171
command, arg = self._readPacket()
173
self._handleResponse(command, arg)
176
def _readPacket(self):
181
char = self._ser.read(1)
187
if not self._ser.inWaiting():
190
log.debugWarning("Ignoring data before escape: %r" % "".join(chars))
194
command = self._ser.read(1)
195
length = BAUM_RSP_LENGTHS.get(command, 0)
196
if command == BAUM_ROUTING_KEYS:
197
length = self.numCells / 8
198
arg = self._ser.read(length)
201
def _handleResponse(self, command, arg):
202
if command == BAUM_CELL_COUNT:
203
self.numCells = ord(arg)
204
elif command == BAUM_DEVICE_ID:
207
elif command in KEY_NAMES:
208
arg = sum(ord(byte) << offset * 8 for offset, byte in enumerate(arg))
209
if arg < self._keysDown.get(command, 0):
211
if not self._ignoreKeyReleases:
212
# The first key released executes the key combination.
214
inputCore.manager.executeGesture(InputGesture(self._keysDown))
215
except inputCore.NoInputGestureAction:
217
# Any further releases are just the rest of the keys in the combination being released,
218
# so they should be ignored.
219
self._ignoreKeyReleases = True
222
# This begins a new key combination.
223
self._ignoreKeyReleases = False
224
self._keysDown[command] = arg
226
elif command == BAUM_POWERDOWN:
227
log.debug("Power down")
228
elif command in (BAUM_COMMUNICATION_CHANNEL, BAUM_SERIAL_NUMBER):
232
log.debugWarning("Unknown command {command!r}, arg {arg!r}".format(command=command, arg=arg))
234
def display(self, cells):
235
# cells will already be padded up to numCells.
236
self._sendRequest(BAUM_DISPLAY_DATA, "".join(chr(cell) for cell in cells))
238
gestureMap = inputCore.GlobalGestureMap({
239
"globalCommands.GlobalCommands": {
240
"braille_scrollBack": ("br(baum):d2",),
241
"braille_scrollForward": ("br(baum):d5",),
242
"braille_previousLine": ("br(baum):d1",),
243
"braille_nextLine": ("br(baum):d3",),
244
"braille_routeTo": ("br(baum):routing",),
245
"kb:upArrow": ("br(baum):up",),
246
"kb:downArrow": ("br(baum):down",),
247
"kb:leftArrow": ("br(baum):left",),
248
"kb:rightArrow": ("br(baum):right",),
249
"kb:enter": ("br(baum):select",),
253
class InputGesture(braille.BrailleDisplayGesture):
255
source = BrailleDisplayDriver.name
257
def __init__(self, keysDown):
258
super(InputGesture, self).__init__()
259
self.keysDown = dict(keysDown)
261
self.keyNames = names = set()
262
for group, groupKeysDown in keysDown.iteritems():
263
if group == BAUM_ROUTING_KEYS:
264
for index in xrange(braille.handler.display.numCells):
265
if groupKeysDown & (1 << index):
266
self.routingIndex = index
270
for index, name in enumerate(KEY_NAMES[group]):
271
if groupKeysDown & (1 << index):
274
self.id = "+".join(names)