From b67bf335d361dc1320fbc4eb76621da5849bdcc9 Mon Sep 17 00:00:00 2001 From: James Teh Date: Mon, 6 Dec 2010 17:18:02 +1000 Subject: [PATCH] Updated alvaBC6 driver: * Use a callback instead of a polling timer to retrieve key presses. This requires an updated alvaw32.dll (version 3.0.4.1 or later). * Rewrote key handling code to use InputGestures. It is now possible to map any of the display's keys. * Map braille navigation scripts to appropriate gestures. Note that display keys that previously emulated system keyboard keys haven't yet been mapped, nor have display keys which changed the braille cursor. --- readme.txt | 2 +- source/brailleDisplayDrivers/alvaBC6.py | 245 ++++++++++---------------------- 2 files changed, 75 insertions(+), 172 deletions(-) diff --git a/readme.txt b/readme.txt index 7f9957905..b2b701259 100644 --- a/readme.txt +++ b/readme.txt @@ -70,7 +70,7 @@ To use the brltty braille display driver: * The brlapi Python bindings can be found in the BRLTTY installation directory and are named brlapi-x.y.z.exe To use the Alva BC640/680 braille display driver: - * ALVA BC6 generic dll, version 2.0.3.0 or later: http://www.nvda-project.org/3rdParty/alvaw32.dll + * ALVA BC6 generic dll, version 3.0.4.1 or later: http://www.nvda-project.org/3rdParty/alvaw32.dll * Copy alvaw32.dll into the source\brailleDisplayDrivers directory. To use the MDV Lilli braille display driver: diff --git a/source/brailleDisplayDrivers/alvaBC6.py b/source/brailleDisplayDrivers/alvaBC6.py index 200469575..a9c15713a 100644 --- a/source/brailleDisplayDrivers/alvaBC6.py +++ b/source/brailleDisplayDrivers/alvaBC6.py @@ -1,67 +1,38 @@ #brailleDisplayDrivers/alvaBC6.py #A part of NonVisual Desktop Access (NVDA) -#Copyright (C) 2006-2009 NVDA Contributors #This file is covered by the GNU General Public License. #See the file COPYING for more details. +#Copyright (C) 2009-2010 Optelec B.V. , James Teh import braille import queueHandler from logHandler import log from ctypes import * +from ctypes.wintypes import * import time -import wx import config from keyboardHandler import KeyboardInputGesture +import inputCore -ALVA_KEY_CHECK_INTERVAL = 50 -ALVA_NO_KEY = 0xFFFF +ALVA_RELEASE_MASK = 0x8000 -ALVA_FRONT_GROUP = 0x71 -ALVA_ET_GROUP = 0x72 -ALVA_SP_GROUP = 0x73 +ALVA_KEYS = { + # Thumb keys (FRONT_GROUP) + 0x71: ("t1", "t2", "t3", "t4", "t5", + # Only for BC680 + "t1", "t2", "t3", "t4", "t5"), + # eTouch keys (ETOUCH_GROUP) + 0x72: ("etouch1", "etouch2", "etouch3", "etouch4"), + # Smartpad keys (PDA_GROUP) + 0x73: ("sp1", "sp2", "spLeft", "spEnter", "spUp", "spDown", "spRight", "sp3", "sp4", + # Only for BC680 + "sp1", "sp2", "spLeft", "spEnter", "spUp", "spDown", "spRight", "sp3", "sp4") +} ALVA_CR_GROUP = 0x74 ALVA_MODIFIER_GROUP = 0x75 ALVA_ASCII_GROUP = 0x76 -ALVA_RELEASE_MASK = 0x8000 -ALVA_SP1 = (1 << 0) -ALVA_SP2 = (1 << 1) -ALVA_SP_LEFT = (1 << 2) -ALVA_SP_ENTER = (1 << 3) -ALVA_SP_UP = (1 << 4) -ALVA_SP_DOWN = (1 << 5) -ALVA_SP_RIGHT = (1 << 6) -ALVA_SP3 = (1 << 7) -ALVA_SP4 = (1 << 8) -# Only for BC680 -ALVA_SPR1 = (1 << 9) -ALVA_SPR2 = (1 << 10) -ALVA_SPR_LEFT = (1 << 11) -ALVA_SPR_ENTER = (1 << 12) -ALVA_SPR_UP = (1 << 13) -ALVA_SPR_DOWN = (1 << 14) -ALVA_SPR_RIGHT = ( 1 << 15) -ALVA_SPR3 = (1 << 16) -ALVA_SPR4 = (1 << 17) - -# FRONT_KEY BITMAPS -ALVA_T1 = (1 << 18) -ALVA_T2 = (1 << 19) -ALVA_T3 = (1 << 20) -ALVA_T4 = 1 << 21 -ALVA_T5 = 1 << 22 -# Only for BC680 -ALVA_R_T1 = 1 << 23 -ALVA_R_T2 = 1 << 24 -ALVA_R_T3 = 1 << 25 -ALVA_R_T4 = 1 << 26 -ALVA_R_T5 = 1 << 27 - -# eTouch keys -ALVA_ETOUCH1 = 1 << 28 -ALVA_ETOUCH2 = 1 << 29 -ALVA_ETOUCH3 = 1 << 30 -ALVA_ETOUCH4 = 1 << 31 +ALVA_PKEYCALLBACK = CFUNCTYPE(BOOL, c_int, USHORT, c_void_p) #Try to load alvaw32.dll try: @@ -89,21 +60,15 @@ class BrailleDisplayDriver(braille.BrailleDisplayDriverWithCursor): AlvaLib.AlvaScanDevices(byref(_AlvaNumDevices)) if _AlvaNumDevices.value==0: raise RuntimeError("No ALVA display found") - else: - log.info("%d devices found" %_AlvaNumDevices.value) - AlvaLib.AlvaOpen(0) - self._alva_NumCells = 0 - self._alva_KeyCheckTimer = wx.PyTimer(self._alva_CheckKeyPresses) - self._alva_KeyMask = 0 - self._alva_KeyCheckTimer.Start(ALVA_KEY_CHECK_INTERVAL) + log.info("%d devices found" %_AlvaNumDevices.value) + AlvaLib.AlvaOpen(0) + self._alva_NumCells = 0 + self._keysDown = set() + self._keyCallback = ALVA_PKEYCALLBACK(self._keyCallback) + AlvaLib.AlvaSetKeyCallback(0, self._keyCallback, None) def terminate(self): super(BrailleDisplayDriver, self).terminate() - try: - self._alva_KeyCheckTimer.Stop() - self._alva_KeyCheckTimer = None - except: - pass AlvaLib.AlvaClose(1) def _get_numCells(self): @@ -111,7 +76,7 @@ class BrailleDisplayDriver(braille.BrailleDisplayDriverWithCursor): NumCells = c_int(0) AlvaLib.AlvaGetCells(0, byref(NumCells)) if NumCells.value==0: - raise RuntimeError("Cannot obtain number of cells") + raise RuntimeError("Cannot obtain number of cells") self._alva_NumCells = NumCells.value log.info("ALVA BC6xx has %d cells" %self._alva_NumCells) return self._alva_NumCells @@ -121,117 +86,55 @@ class BrailleDisplayDriver(braille.BrailleDisplayDriverWithCursor): cells="".join([chr(x) for x in cells]) AlvaLib.AlvaSendBraille(0, cells, 0, len(cells)) - def _alva_CheckKeyPresses(self): - Key = c_uint16(0) - while True: - try: - AlvaLib.AlvaGetKey(0, 0, byref(Key)) - except: - log.error("Error reading keypress from ALVAW32.DLL", exc_info=True) - return - if Key.value == ALVA_NO_KEY: - break - if Key.value & ALVA_RELEASE_MASK: - self._alva_OnKeyRelease() + def _keyCallback(self, dev, key, userData): + group = (key >> 8) & 0xFF + number = key & 0xFF + if key & ALVA_RELEASE_MASK: + # The key is being released. + if self._keysDown: + try: + inputCore.manager.executeGesture(InputGesture(self._keysDown)) + except inputCore.NoInputGestureAction: + pass + self._keysDown.clear() + else: + if group == ALVA_CR_GROUP: + try: + inputCore.manager.executeGesture(InputGesture(((group, number),))) + except inputCore.NoInputGestureAction: + pass else: - self._alva_OnKeyPress(Key.value) - - def _alva_OnKeyPress(self, key): - KeyGroup = (key >> 8) & 0xFF - KeyNumber = key & 0xFF - if KeyGroup == ALVA_FRONT_GROUP: - self._alva_KeyMask = self._alva_KeyMask | ( ALVA_T1 << KeyNumber) - elif KeyGroup == ALVA_ET_GROUP: - self._alva_KeyMask = self._alva_KeyMask | ( ALVA_ETOUCH1 << KeyNumber) - elif KeyGroup == ALVA_SP_GROUP: - self._alva_KeyMask = self._alva_KeyMask | (ALVA_SP1 << KeyNumber) - elif KeyGroup == ALVA_CR_GROUP: - log.debug("CR key %d" %KeyNumber) - braille.handler.routeTo(KeyNumber) - - def _alva_OnKeyRelease(self): - _KeyMask = self._alva_KeyMask - self._alva_KeyMask = 0 - - if _KeyMask == ALVA_SP1 or _KeyMask == ALVA_SPR1: # Shift-TAB - KeyboardInputGesture.fromName('shift+tab').send() - - elif _KeyMask == ALVA_SP2 or _KeyMask == ALVA_SPR2: # Alt - KeyboardInputGesture.fromName('f10').send() - - elif _KeyMask == ALVA_SP3 or _KeyMask == ALVA_SPR3: # ESC - KeyboardInputGesture.fromName('escape').send() - - elif _KeyMask == ALVA_SP4 or _KeyMask == ALVA_SPR4: # Tab - KeyboardInputGesture.fromName('tab').send() - - elif _KeyMask == ALVA_SP_UP or _KeyMask == ALVA_SPR_UP: # Arrow up - KeyboardInputGesture.fromName('upArrow').send() - - elif _KeyMask == ALVA_SP_DOWN or _KeyMask == ALVA_SPR_DOWN: # Arrow down - KeyboardInputGesture.fromName('downArrow').send() - - elif _KeyMask == ALVA_SP_LEFT or _KeyMask == ALVA_SPR_LEFT: #Arrow left - KeyboardInputGesture.fromName('leftArrow').send() - - elif _KeyMask == ALVA_SP_RIGHT or _KeyMask == ALVA_SPR_RIGHT: #Arrow right - KeyboardInputGesture.fromName('rightArrow').send() - - elif _KeyMask == ALVA_SP_ENTER or _KeyMask == ALVA_SPR_ENTER: # enter key - KeyboardInputGesture.fromName('enter').send() - - elif _KeyMask == (ALVA_SP1 | ALVA_SP3) or _KeyMask == (ALVA_SPR1 | ALVA_SPR3): # control panel - import gui - gui.showGui() - - elif _KeyMask == (ALVA_SP1 | ALVA_SP4) or _KeyMask == (ALVA_SPR1 | ALVA_SPR4): #Minimiza all apps - KeyboardInputGesture.fromName('windows+d').send() - - elif _KeyMask == (ALVA_SP2 | ALVA_SP3) or _KeyMask == (ALVA_SPR2 | ALVA_SPR3): # Start menu - KeyboardInputGesture.fromName('windows').send() - - elif _KeyMask == (ALVA_SP2 | ALVA_SP4) or _KeyMask == (ALVA_SPR2 | ALVA_SPR4): #Alt Tab - KeyboardInputGesture.fromName('alt+tab').send() - - elif _KeyMask == ALVA_T1 or _KeyMask == ALVA_R_T1: # Scroll backwards - braille.handler.scrollBack() - - elif _KeyMask == ALVA_T2 or _KeyMask == ALVA_R_T2: # Scroll line up - if braille.handler.buffer.regions: - braille.handler.buffer.regions[-1].previousLine() - - elif _KeyMask == ALVA_T3 or _KeyMask == ALVA_R_T3: # Goto focus - log.warning("Goto focus not yet implemented") - - elif _KeyMask == ALVA_T4 or _KeyMask == ALVA_R_T4: # Scroll line down - if braille.handler.buffer.regions: - braille.handler.buffer.regions[-1].nextLine() - - elif _KeyMask == ALVA_T5 or _KeyMask == ALVA_R_T5: # Scroll forward - braille.handler.scrollForward() - - elif _KeyMask == (ALVA_T1 | ALVA_T2) or _KeyMask == (ALVA_R_T1 | ALVA_R_T2): # Braille top - pass - - elif _KeyMask == (ALVA_T1 | ALVA_T3) or _KeyMask == (ALVA_R_T1 | ALVA_R_T3): # Set verbosity - pass - - elif _KeyMask == (ALVA_T1 | ALVA_T4) or _KeyMask == (ALVA_R_T1 | ALVA_R_T4): # Change cursor shape - self.cursorShape = 0xFF if self.cursorShape == 0xC0 else 0xC0 - - elif _KeyMask == (ALVA_T1 | ALVA_T5) or _KeyMask == (ALVA_R_T1 | ALVA_R_T5): # Braille input on/off - pass - - elif _KeyMask == (ALVA_T2 | ALVA_T4) or _KeyMask == (ALVA_R_T2 | ALVA_R_T4): # Braille toggle 8/6 dots - pass - - elif _KeyMask == (ALVA_T2 | ALVA_T3) or _KeyMask == (ALVA_R_T2 | ALVA_R_T3): # Literary Braille on/off - pass - - elif _KeyMask == (ALVA_T2 | ALVA_T5) or _KeyMask == (ALVA_R_T2 | ALVA_R_T5): # Braille cursor on/off - self.cursorBlinkRate = 0 if self.cursorBlinkRate else config.conf["braille"]["cursorBlinkRate"] - # cursorBlinkRate sets the cursor up, so we have to blink ourself one more time to switch the cursor off - self._blink() + self._keysDown.add((group, number)) + return False + + gestureMap = inputCore.GlobalGestureMap({ + "globalCommands.GlobalCommands": { + "showGui": ("br(alvaBC6):sp1+sp3",), + "braille_scrollBack": ("br(alvaBC6):t1",), + "braille_previousLine": ("br(alvaBC6):t2",), + "braille_nextLine": ("br(alvaBC6):t4",), + "braille_scrollForward": ("br(alvaBC6):t5",), + "braille_routeTo": ("br(alvaBC6):routing",), + } + }) + +class InputGesture(braille.BrailleDisplayGesture): + + source = BrailleDisplayDriver.name + + def __init__(self, keys): + super(InputGesture, self).__init__() + self.keyCodes = set(keys) + + self.keyNames = names = set() + for group, number in self.keyCodes: + if group == ALVA_CR_GROUP: + names.add("routing") + self.routingIndex = number + else: + try: + names.add(ALVA_KEYS[group][number]) + except (KeyError, IndexError): + log.debugWarning("Unknown key with group %d and number %d" % (group, number)) - elif _KeyMask == (ALVA_T4 | ALVA_T5) or _KeyMask == (ALVA_R_T4 | ALVA_R_T5): # Braille bottom - pass + self.id = "+".join(names) -- 2.11.0