Skip to content
Merged
Changes from 1 commit
Commits
Show all changes
59 commits
Select commit Hold shift + click to select a range
c1784f0
Attempt to create a new hwIo class for USB bulk devices
Sep 25, 2017
dd6fae5
Merge branch 'bulk' into freedomscientific-native
bramd Sep 25, 2017
20da169
Native driver for Freedom Scientific braille displays, work in progre…
bramd Sep 25, 2017
882a307
Key and extended key handling
bramd Sep 27, 2017
52f041e
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Sep 28, 2017
bff76e8
Add support for routing and second row of buttons on older Focus/Pacm…
bramd Oct 1, 2017
2bddae8
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Oct 14, 2017
f17a225
Properly implement Freedom Scientific displays connected via Bluetooth
bramd Oct 14, 2017
a4b647a
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Oct 24, 2017
685d360
Fix output on Focus series 1 (44/70/84), improve initialization
bramd Oct 24, 2017
fd7e914
Add USB VID/PID for Focus/PAC mate displays
bramd Nov 4, 2017
e37038a
Code documentation and cleanup
bramd Nov 4, 2017
d49a493
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Nov 4, 2017
d60639f
Fix typo in br(breedomscientific]:leftBumerbarUp gesture. Fix merged …
bramd Nov 4, 2017
331e13d
Mark brailleDisplayDrivers/freedomScientific.py as UTF-8
bramd Nov 4, 2017
2249037
Fixed reference from numCells to self.numCells
bramd Nov 8, 2017
f54e54e
Merge branch 'master' into freedomscientific-native
bramd Aug 4, 2018
cef92d2
Enable auto detection of Freedom Scientific braille displays
bramd Aug 5, 2018
d783b49
Some code cleanup
bramd Aug 5, 2018
8364932
Fix mistake in display initialization introduced in the previous comm…
bramd Aug 5, 2018
4a50975
Fix key handling if multiple keys are released at the same time
bramd Aug 5, 2018
5ad6818
Don't set _awaitingAck manually in display method
bramd Aug 5, 2018
d0f6e6e
Merge branch 'master' into freedomscientific-native
bramd Sep 8, 2018
a3fb229
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Sep 29, 2018
4f4d6ce
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Oct 13, 2018
363ea52
Code cleanup
bramd Oct 14, 2018
224bdca
The Focus displays seem to switch to USB as soon as a USB cable is co…
bramd Oct 14, 2018
2194af1
The Focus displays seem to switch to USB as soon as a USB cable is co…
bramd Oct 14, 2018
4125f0f
Update UserGuide:Freedom Scientific displays support auto detection
bramd Oct 15, 2018
318439b
Add key bindings for win+tab and alt+shift+tab
bramd Oct 15, 2018
4a2020c
Support model specific gestures
bramd Oct 17, 2018
9da6213
Use self.timeout instead of the TIMEOUTconstant
bramd Oct 17, 2018
2422b3d
Review comment:use BytesIO instead of StringIO
bramd Oct 21, 2018
50c8a89
Handle bumper keys correctly (#8849)
bramd Oct 21, 2018
3cae439
Merge remote-tracking branch 'origin/master' into freedomscientific-n…
Dec 18, 2018
7baac86
Get rid of heavily confusing _resumeDetection method on the braille h…
Dec 18, 2018
78bdc84
Additional clarifying comment
Dec 18, 2018
f5984e4
Respect limitToDevices when bluetooth polling or when a rescan is tri…
Dec 18, 2018
82d2120
Also respect a dedetector without bluetooth when a new device is added
Dec 18, 2018
e43fa7a
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd Dec 27, 2018
1b7f7f3
Merge pull request #2 from leonardder/freedomChanges
bramd Dec 27, 2018
4a5f6c7
Fix double and
Dec 27, 2018
1778224
Fix top routing for Focus 2nd generation
Dec 27, 2018
7b29486
Improve python 3 compat
Dec 27, 2018
06d7efc
Make a constant for TOP_ROUTING_KEY_GROUP
Dec 27, 2018
0eccac8
isTopRow now treates the keygroup as a boolean. It looks like key gro…
Dec 27, 2018
bd6384d
Merge remote-tracking branch 'origin/master' into freedomscientific-n…
Mar 5, 2019
a88f6d9
Fixed issues in braille module
Mar 5, 2019
1719423
Review actions for bdDetect
Mar 25, 2019
72ce2f1
Merge remote-tracking branch 'origin/master' into freedomscientific-n…
Mar 25, 2019
f411a43
Review actions
bramd Mar 31, 2019
9beffd1
Use FS_BYTE_NULL instead of FS_DATA_EMPTY in string replacement for m…
bramd Apr 10, 2019
99cbc02
Use constants for offsets in INFO packet
bramd Apr 13, 2019
90447e1
Fix documentation ans usage of special translation table for Focus 1
bramd Apr 13, 2019
1545299
Improve documentation
bramd May 4, 2019
52dc60b
Documentation and code clarification.
feerrenrut May 6, 2019
2869c9e
Update source/brailleDisplayDrivers/freedomScientific.py
feerrenrut May 20, 2019
4cd583c
Merge pull request #3 from nvaccess/freedomscientific-native
bramd May 20, 2019
dc2913b
Merge remote-tracking branch 'upstream/master' into freedomscientific…
bramd May 20, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Code documentation and cleanup
  • Loading branch information
bramd committed Nov 4, 2017
commit e37038a8a0d2aad9f821ae5ba6c4317ccee0bffc
165 changes: 99 additions & 66 deletions source/brailleDisplayDrivers/freedomScientific.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
"Focus 80 BT",
)

#: Model names and number of cells
MODELS = {
"Focus 14": 14,
"Focus 40": 40,
Expand All @@ -66,9 +67,13 @@
FS_PKT_EXT_KEY = "\x82"

def _makeTranslationTable(dotsTable):
"""Create a translation table for braille dot combinations

@param dotsTable: The list of 8 bitmasks to use for every dot
"""
def isoDot(number):
return 1 << ((number + 1) - 1)
Comment thread
bramd marked this conversation as resolved.
Outdated

outputTable = [0] * 256
Comment thread
bramd marked this conversation as resolved.
Outdated
for byte in xrange(256):
cell = 0
Expand All @@ -79,22 +84,30 @@ def isoDot(number):
return outputTable

def _translate(cells, dotsTable):
"""Translate cells according to a dots table

@param cells: The cells to translate, given in ISO 11548 format (used by most braille displays)
@type cells: [int]
@param dotsTable: A list of 8 items containing the bitmask to use for every dot
@type dotsTable: [int]
"""
outCells = [0] * len(cells)
for i, cell in enumerate(cells):
outCells[i] = dotsTable[cell]
Comment thread
feerrenrut marked this conversation as resolved.
Outdated
return outCells

#: Dots table used by first generation Focus displays
FOCUS_1_DOTS_TABLE = [
0X01, 0X02, 0X04, 0X10, 0X20, 0X40, 0X08, 0X80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any information you can provide about what these values represent would be helpful.

]

FOCUS_1_TRANSLATION_TABLE = _makeTranslationTable(FOCUS_1_DOTS_TABLE)

class BrailleDisplayDriver(braille.BrailleDisplayDriver,ScriptableObject):
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):

name="freedomScientific"
name = "freedomScientific"
# Translators: Names of braille displays.
description=_("Freedom Scientific Focus/PAC Mate series")
description = _("Freedom Scientific Focus/PAC Mate series")
isThreadSafe = True

@classmethod
Expand Down Expand Up @@ -139,7 +152,7 @@ def _getAutoPorts(cls, comPorts):
with _winreg.OpenKey(rootKey, os.path.join(keyName, "Device Parameters")) as paramsKey:
yield _winreg.QueryValueEx(paramsKey, "SymbolicName")[0], "USB"
except WindowsError:
continue
continue

# Bluetooth
for portInfo in comPorts:
Expand All @@ -159,11 +172,13 @@ def _getBluetoothPorts(cls):
continue
yield p

wizWheelActions=[
wizWheelActions = [
# Translators: The name of a key on a braille display, that scrolls the display to show previous/next part of a long line.
(_("display scroll"),("globalCommands","GlobalCommands","braille_scrollBack"),("globalCommands","GlobalCommands","braille_scrollForward")),
(_("display scroll"), ("globalCommands", "GlobalCommands", "braille_scrollBack"),
("globalCommands", "GlobalCommands", "braille_scrollForward")),
# Translators: The name of a key on a braille display, that scrolls the display to show the next/previous line.
(_("line scroll"),("globalCommands","GlobalCommands","braille_previousLine"),("globalCommands","GlobalCommands","braille_nextLine")),
(_("line scroll"), ("globalCommands", "GlobalCommands", "braille_previousLine"),
("globalCommands", "GlobalCommands", "braille_nextLine")),
]

def __init__(self, port="auto"):
Expand All @@ -173,13 +188,13 @@ def __init__(self, port="auto"):
self._keyBits = 0
self._extendedKeyBits = 0
self.translationTable = None
self.leftWizWheelActionCycle=itertools.cycle(self.wizWheelActions)
action=self.leftWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):leftWizWheelUp",*action[1])
self.gestureMap.add("br(freedomScientific):leftWizWheelDown",*action[2])
self.rightWizWheelActionCycle=itertools.cycle(self.wizWheelActions)
action=self.rightWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):rightWizWheelUp",*action[1])
self.leftWizWheelActionCycle = itertools.cycle(self.wizWheelActions)
action = self.leftWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):leftWizWheelUp", *action[1])
self.gestureMap.add("br(freedomScientific):leftWizWheelDown", *action[2])
self.rightWizWheelActionCycle = itertools.cycle(self.wizWheelActions)
action = self.rightWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):rightWizWheelUp", *action[1])
self.gestureMap.add("br(freedomScientific):rightWizWheelDown",*action[2])
super(BrailleDisplayDriver,self).__init__()
tryPorts = ()
Expand Down Expand Up @@ -228,6 +243,19 @@ def terminate(self):
self._dev.close()

def _sendPacket(self, packetType, arg1="\x00", arg2="\x00", arg3="\x00", data=""):
"""Send a packet to the display

@param packetType: Type of packet (first byte), use one of the FS_PKT constants
@type packetType: str
@param arg1: First argument (second byte of packet)
@type arg1: str
@param arg2: Second argument (third byte of packet)
@type arg2: str
@param arg3: Third argument (fourth byte of packet)
@type arg3: str
@param data: Data to send if this is an extended packet, required checksum will be added automatically
@type data: str
"""
def handleArg(arg):
if type(arg) == int:
return chr(arg)
Expand Down Expand Up @@ -265,8 +293,8 @@ def _onReceive(self, data):
self._handlePacket(packetType, arg1, arg2, arg3, payload)

def _handlePacket(self, packetType, arg1, arg2, arg3, payload):
"Handle a packet from the device"
if packetType == FS_PKT_ACK:
log.debug("ACK received")
self._awaitingAck = False
if self._pendingCells:
self.display(self._pendingCells)
Expand Down Expand Up @@ -322,9 +350,12 @@ def _handlePacket(self, packetType, arg1, arg2, arg3, payload):
keyBits = ord(payload[0]) >> 4
self._handleExtendedKeys(keyBits)
else:
log.debug("Unknown packet of type: %r" % packetType)
log.debugWarning("Unknown packet of type: %r" % packetType)

def _updateKeyBits(self, keyBits, oldKeyBits, keyCount):
"""Helper function that reports if keys have been pressed and which keys have been released
based on old and new keybits.
"""
isRelease = False
keyBitsBeforeRelease = 0
newKeysPressed = False
Expand All @@ -342,11 +373,12 @@ def _updateKeyBits(self, keyBits, oldKeyBits, keyCount):
elif newKey and not oldKey:
oldKeyBits |= keyBit
newKeysPressed = True

keyBit <<= 1
return oldKeyBits, isRelease, keyBitsBeforeRelease, newKeysPressed

def _handleKeys(self, keyBits):
"""Send gestures if keys are released and update self._keyBits"""
keyBits, isRelease, keyBitsBeforeRelease, newKeysPressed = self._updateKeyBits(keyBits, self._keyBits, 24)
if newKeysPressed:
self._ignoreKeyReleases = False
Expand All @@ -360,6 +392,7 @@ def _handleKeys(self, keyBits):
self._ignoreKeyReleases = True

def _handleExtendedKeys(self, keyBits):
"""Send gestures if keys are released and update self._extendedKeyBits"""
keyBits, isRelease, keyBitsBeforeRelease, newKeysPressed = self._updateKeyBits(keyBits, self._extendedKeyBits, 24)
if newKeysPressed:
self._ignoreKeyReleases = False
Expand All @@ -373,6 +406,7 @@ def _handleExtendedKeys(self, keyBits):
self._ignoreKeyReleases = True

def _calculateChecksum(self, data):
"Calculate the checksum for extended packets"
checksum = 0
for byte in data:
checksum -= ord(byte)
Expand All @@ -391,55 +425,56 @@ def display(self,cells):
self._pendingCells = cells

def _configureDisplay(self):
"""Enable extended keys on Focus firmware 3 and up"""
if self._model and self._firmwareVersion and self._model.startswith("Focus") and ord(self._firmwareVersion[0]) >= ord('3'):
# Focus 2 or later. Make sure extended keys support is enabled.
log.debug("Activating extended keys on freedom Scientific display. Display name: %s, firmware version: %s.", self._model, self._firmwareVersion)
self._sendPacket(FS_PKT_CONFIG, "\x02")

def script_toggleLeftWizWheelAction(self,gesture):
action=self.leftWizWheelActionCycle.next()
action = self.leftWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):leftWizWheelUp",*action[1],replace=True)
self.gestureMap.add("br(freedomScientific):leftWizWheelDown",*action[2],replace=True)
braille.handler.message(action[0])

def script_toggleRightWizWheelAction(self,gesture):
action=self.rightWizWheelActionCycle.next()
action = self.rightWizWheelActionCycle.next()
self.gestureMap.add("br(freedomScientific):rightWizWheelUp",*action[1],replace=True)
self.gestureMap.add("br(freedomScientific):rightWizWheelDown",*action[2],replace=True)
braille.handler.message(action[0])

__gestures={
"br(freedomScientific):leftWizWheelPress":"toggleLeftWizWheelAction",
"br(freedomScientific):rightWizWheelPress":"toggleRightWizWheelAction",
"br(freedomScientific):leftWizWheelPress": "toggleLeftWizWheelAction",
"br(freedomScientific):rightWizWheelPress": "toggleRightWizWheelAction",
}

gestureMap=inputCore.GlobalGestureMap({
"globalCommands.GlobalCommands" : {
"braille_routeTo":("br(freedomScientific):routing",),
"braille_scrollBack" : ("br(freedomScientific):leftAdvanceBar", "br(freedomScientific]:leftBumperBarUp","br(freedomScientific):rightBumperBarUp",),
"braille_scrollForward" : ("br(freedomScientific):rightAdvanceBar","br(freedomScientific):leftBumperBarDown","br(freedomScientific):rightBumperBarDown",),
"braille_previousLine" : ("br(freedomScientific):leftRockerBarUp", "br(freedomScientific):rightRockerBarUp",),
"braille_nextLine" : ("br(freedomScientific):leftRockerBarDown", "br(freedomScientific):rightRockerBarDown",),
"globalCommands.GlobalCommands": {
"braille_routeTo": ("br(freedomScientific):routing",),
"braille_scrollBack": ("br(freedomScientific):leftAdvanceBar", "br(freedomScientific]:leftBumperBarUp","br(freedomScientific):rightBumperBarUp",),
"braille_scrollForward": ("br(freedomScientific):rightAdvanceBar", "br(freedomScientific):leftBumperBarDown", "br(freedomScientific):rightBumperBarDown",),
"braille_previousLine": ("br(freedomScientific):leftRockerBarUp", "br(freedomScientific):rightRockerBarUp",),
"braille_nextLine": ("br(freedomScientific):leftRockerBarDown", "br(freedomScientific):rightRockerBarDown",),
"kb:shift+tab": ("br(freedomScientific):dot1+dot2+brailleSpaceBar",),
"kb:tab" : ("br(freedomScientific):dot4+dot5+brailleSpaceBar",),
"kb:upArrow" : ("br(freedomScientific):dot1+brailleSpaceBar",),
"kb:downArrow" : ("br(freedomScientific):dot4+brailleSpaceBar",),
"kb:leftArrow" : ("br(freedomScientific):dot3+brailleSpaceBar",),
"kb:rightArrow" : ("br(freedomScientific):dot6+brailleSpaceBar",),
"kb:control+leftArrow" : ("br(freedomScientific):dot2+brailleSpaceBar",),
"kb:control+rightArrow" : ("br(freedomScientific):dot5+brailleSpaceBar",),
"kb:home" : ("br(freedomScientific):dot1+dot3+brailleSpaceBar",),
"kb:control+home" : ("br(freedomScientific):dot1+dot2+dot3+brailleSpaceBar",),
"kb:end" : ("br(freedomScientific):dot4+dot6+brailleSpaceBar",),
"kb:control+end" : ("br(freedomScientific):dot4+dot5+dot6+brailleSpaceBar",),
"kb:alt" : ("br(freedomScientific):dot1+dot3+dot4+brailleSpaceBar",),
"kb:alt+tab" : ("br(freedomScientific):dot2+dot3+dot4+dot5+brailleSpaceBar",),
"kb:escape" : ("br(freedomScientific):dot1+dot5+brailleSpaceBar",),
"kb:windows" : ("br(freedomScientific):dot2+dot4+dot5+dot6+brailleSpaceBar",),
"kb:windows+d" : ("br(freedomScientific):dot1+dot2+dot3+dot4+dot5+dot6+brailleSpaceBar",),
"reportCurrentLine" : ("br(freedomScientific):dot1+dot4+brailleSpaceBar",),
"showGui" :("br(freedomScientific):dot1+dot3+dot4+dot5+brailleSpaceBar",),
"braille_toggleTether" : ("br(freedomScientific):leftGDFButton+rightGDFButton",),
"kb:tab": ("br(freedomScientific):dot4+dot5+brailleSpaceBar",),
"kb:upArrow": ("br(freedomScientific):dot1+brailleSpaceBar",),
"kb:downArrow": ("br(freedomScientific):dot4+brailleSpaceBar",),
"kb:leftArrow": ("br(freedomScientific):dot3+brailleSpaceBar",),
"kb:rightArrow": ("br(freedomScientific):dot6+brailleSpaceBar",),
"kb:control+leftArrow": ("br(freedomScientific):dot2+brailleSpaceBar",),
"kb:control+rightArrow": ("br(freedomScientific):dot5+brailleSpaceBar",),
"kb:home": ("br(freedomScientific):dot1+dot3+brailleSpaceBar",),
"kb:control+home": ("br(freedomScientific):dot1+dot2+dot3+brailleSpaceBar",),
"kb:end": ("br(freedomScientific):dot4+dot6+brailleSpaceBar",),
"kb:control+end": ("br(freedomScientific):dot4+dot5+dot6+brailleSpaceBar",),
"kb:alt": ("br(freedomScientific):dot1+dot3+dot4+brailleSpaceBar",),
"kb:alt+tab": ("br(freedomScientific):dot2+dot3+dot4+dot5+brailleSpaceBar",),
"kb:escape": ("br(freedomScientific):dot1+dot5+brailleSpaceBar",),
"kb:windows": ("br(freedomScientific):dot2+dot4+dot5+dot6+brailleSpaceBar",),
"kb:windows+d": ("br(freedomScientific):dot1+dot2+dot3+dot4+dot5+dot6+brailleSpaceBar",),
"reportCurrentLine": ("br(freedomScientific):dot1+dot4+brailleSpaceBar",),
"showGui": ("br(freedomScientific):dot1+dot3+dot4+dot5+brailleSpaceBar",),
"braille_toggleTether": ("br(freedomScientific):leftGDFButton+rightGDFButton",),
}
})

Expand All @@ -448,16 +483,16 @@ class InputGesture(braille.BrailleDisplayGesture):

class KeyGesture(InputGesture, brailleInput.BrailleInputGesture):

keyLabels=[
#Braille keys (byte 1)
keyLabels = [
# Braille keys (byte 1)
'dot1','dot2','dot3','dot4','dot5','dot6','dot7','dot8',
#Assorted keys (byte 2)
# Assorted keys (byte 2)
'leftWizWheelPress','rightWizWheelPress',
'leftShiftKey','rightShiftKey',
'leftAdvanceBar','rightAdvanceBar',
None,
'brailleSpaceBar',
#GDF keys (byte 3)
# GDF keys (byte 3)
'leftGDFButton','rightGDFButton',
None,
'leftBumperBarUp','leftBumperBarDown','rightBumperBarUp','rightBumperBarDown',
Expand All @@ -467,11 +502,11 @@ class KeyGesture(InputGesture, brailleInput.BrailleInputGesture):
"leftRockerBarUp", "leftRockerBarDown", "rightRockerBarUp", "rightRockerBarDown",
]

def __init__(self,keyBits, extendedKeyBits):
def __init__(self, keyBits, extendedKeyBits):
super(KeyGesture,self).__init__()
keys=[self.keyLabels[num] for num in xrange(24) if (keyBits>>num)&1]
extendedKeys=[self.extendedKeyLabels[num] for num in xrange(4) if (extendedKeyBits>>num)&1]
self.id="+".join(keys+extendedKeys)
keys=[self.keyLabels[num] for num in xrange(24) if (keyBits>>num) & 1]
extendedKeys=[self.extendedKeyLabels[num] for num in xrange(4) if (extendedKeyBits>>num) & 1]
self.id = "+".join(keys+extendedKeys)
# Don't say is this a dots gesture if some keys either from dots and space are pressed.
if not extendedKeyBits and not keyBits & ~(0xff | (1 << 0xf)):
self.dots = keyBits & 0xff
Expand All @@ -480,19 +515,17 @@ def __init__(self,keyBits, extendedKeyBits):
self.space = True

class RoutingGesture(InputGesture):

def __init__(self,routingIndex,topRow=False):
def __init__(self, routingIndex, topRow=False):
if topRow:
self.id="topRouting%d"%(routingIndex+1)
self.id = "topRouting%d"%(routingIndex+1)
else:
self.id="routing"
self.routingIndex=routingIndex
self.id = "routing"
self.routingIndex = routingIndex
super(RoutingGesture,self).__init__()

class WizWheelGesture(InputGesture):

def __init__(self,isDown,isRight):
which="right" if isRight else "left"
direction="Down" if isDown else "Up"
self.id="%sWizWheel%s"%(which,direction)
super(WizWheelGesture,self).__init__()
def __init__(self, isDown, isRight):
which = "right" if isRight else "left"
direction = "Down" if isDown else "Up"
self.id = "%sWizWheel%s" % (which, direction)
super(WizWheelGesture, self).__init__()