| Trees | Indices | Help |
|---|
|
|
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>
6
7 import time
8 import wx
9 import serial
10 import hwPortUtils
11 import braille
12 import inputCore
13 from logHandler import log
14
15 TIMEOUT = 0.2
16 BAUD_RATE = 19200
17 READ_INTERVAL = 50
18
19 ESCAPE = "\x1b"
20
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"
32
33 BAUM_RSP_LENGTHS = {
34 BAUM_CELL_COUNT: 1,
35 BAUM_POWERDOWN: 1,
36 BAUM_COMMUNICATION_CHANNEL: 1,
37 BAUM_DISPLAY_KEYS: 1,
38 BAUM_BRAILLE_KEYS: 2,
39 BAUM_JOYSTICK_KEYS: 1,
40 BAUM_DEVICE_ID: 16,
41 BAUM_SERIAL_NUMBER: 8,
42 }
43
44 KEY_NAMES = {
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"),
50 }
51
52 USB_IDS = frozenset((
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
73 ))
74
75 BLUETOOTH_NAMES = (
76 "Baum SuperVario",
77 "Baum PocketVario",
78 "Baum SVario",
79 "HWG Brailliant",
80 "Refreshabraille",
81 "VarioConnect",
82 "BrailleConnect",
83 )
86 name = "baum"
87 description = _("Baum/HumanWare/APH braille displays")
88
89 @classmethod
92
94 super(BrailleDisplayDriver, self).__init__()
95 self.numCells = 0
96 self._deviceID = None
97
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"):
104 # USB.
105 portType = "USB"
106 try:
107 usbID = hwID.split("&", 1)[1]
108 except IndexError:
109 continue
110 if usbID not in USB_IDS:
111 continue
112 elif "bluetoothName" in portInfo:
113 # Bluetooth.
114 portType = "bluetooth"
115 btName = portInfo["bluetoothName"]
116 if not any(btName.startswith(prefix) for prefix in BLUETOOTH_NAMES):
117 continue
118 else:
119 continue
120
121 # At this point, a port bound to this display has been found.
122 # Try talking to the display.
123 try:
124 self._ser = serial.Serial(port, baudrate=BAUD_RATE, timeout=TIMEOUT, writeTimeout=TIMEOUT)
125 except serial.SerialException:
126 continue
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)
135 if self.numCells:
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))
142 break
143
144 else:
145 raise RuntimeError("No Baum display found")
146
147 self._readTimer = wx.PyTimer(self._handleResponses)
148 self._readTimer.Start(READ_INTERVAL)
149 self._keysDown = {}
150 self._ignoreKeyReleases = False
151
153 try:
154 super(BrailleDisplayDriver, self).terminate()
155 self._readTimer.Stop()
156 self._readTimer = None
157 self._sendRequest(BAUM_PROTOCOL_ONOFF, False)
158 finally:
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.
161 self._ser.close()
162
164 if isinstance(arg, (int, bool)):
165 arg = chr(arg)
166 self._ser.write("\x1b{command}{arg}".format(command=command,
167 arg=arg.replace(ESCAPE, ESCAPE * 2)))
168
170 while wait or self._ser.inWaiting():
171 command, arg = self._readPacket()
172 if command:
173 self._handleResponse(command, arg)
174 wait = False
175
177 # Find the escape.
178 chars = []
179 escapeFound = False
180 while True:
181 char = self._ser.read(1)
182 if char == ESCAPE:
183 escapeFound = True
184 break
185 else:
186 chars.append(char)
187 if not self._ser.inWaiting():
188 break
189 if chars:
190 log.debugWarning("Ignoring data before escape: %r" % "".join(chars))
191 if not escapeFound:
192 return None, None
193
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)
199 return command, arg
200
202 if command == BAUM_CELL_COUNT:
203 self.numCells = ord(arg)
204 elif command == BAUM_DEVICE_ID:
205 self._deviceID = arg
206
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):
210 # Release.
211 if not self._ignoreKeyReleases:
212 # The first key released executes the key combination.
213 try:
214 inputCore.manager.executeGesture(InputGesture(self._keysDown))
215 except inputCore.NoInputGestureAction:
216 pass
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
220 else:
221 # Press.
222 # This begins a new key combination.
223 self._ignoreKeyReleases = False
224 self._keysDown[command] = arg
225
226 elif command == BAUM_POWERDOWN:
227 log.debug("Power down")
228 elif command in (BAUM_COMMUNICATION_CHANNEL, BAUM_SERIAL_NUMBER):
229 pass
230
231 else:
232 log.debugWarning("Unknown command {command!r}, arg {arg!r}".format(command=command, arg=arg))
233
235 # cells will already be padded up to numCells.
236 self._sendRequest(BAUM_DISPLAY_DATA, "".join(chr(cell) for cell in cells))
237
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",),
250 },
251 })
252
254
255 source = BrailleDisplayDriver.name
256
258 super(InputGesture, self).__init__()
259 self.keysDown = dict(keysDown)
260
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
267 names.add("routing")
268 break
269 else:
270 for index, name in enumerate(KEY_NAMES[group]):
271 if groupKeysDown & (1 << index):
272 names.add(name)
273
274 self.id = "+".join(names)
275
| Trees | Indices | Help |
|---|
| Generated by Epydoc 3.0.1 on Fri Nov 18 17:46:02 2011 | http://epydoc.sourceforge.net |