Пример #1
0
 def update_brl_display_gesture_map(cls, display=braille.handler.display):
     if not isinstance(display.gestureMap, inputCore.GlobalGestureMap):
         display.gestureMap = inputCore.GlobalGestureMap()
     source = "bk:" if cmpNVDAver(2018, 3) < 0 else "br({0}):".format(
         cls.source)
     for g, f in GlobalPlugin.default_bk_gestures.items():
         display.gestureMap.add(source + g, *f)
Пример #2
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    """ HIMS SyncBraille braille display.
	"""
    name = "syncBraille"
    # Translators: The name of a braille display.
    description = _("HIMS SyncBraille")

    @classmethod
    def check(cls):
        return bool(himsSyncBrailleLib)

    def __init__(self):
        super(BrailleDisplayDriver, self).__init__()
        self._messageWindowClassAtom = windll.user32.RegisterClassExW(
            byref(nvdaHIMSBrlWndCls))
        self._messageWindow = windll.user32.CreateWindowExW(
            0, self._messageWindowClassAtom, u"nvdaHIMSBrlWndCls window", 0, 0,
            0, 0, 0, None, None, appInstance, None)
        if himsSyncBrailleLib.OpenSyncBrl(self._messageWindow,
                                          nvdaHIMSBrlWm) == 1:
            return
        raise RuntimeError("No display found")

    def terminate(self):
        super(BrailleDisplayDriver, self).terminate()
        himsSyncBrailleLib.CloseSyncBrl()
        windll.user32.DestroyWindow(self._messageWindow)
        windll.user32.UnregisterClassW(self._messageWindowClassAtom,
                                       appInstance)

    def _get_numCells(self):
        return himsSyncBrailleLib.GetCellCount()

    def display(self, cells):
        cells = "".join([chr(x) for x in cells])
        himsSyncBrailleLib.SendSyncBrl(cells)

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(syncBraille):routing", ),
            "brailleScrollBack": ("br(syncBraille):leftSideScrollDown", ),
            "brailleScrollForward": ("br(syncBraille):rightSideScrollDown", ),
        }
    })
Пример #3
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "lilli"
    # Translators: Name of a braille display.
    description = _("MDV Lilli")

    @classmethod
    def check(cls):
        return bool(lilliDll)

    def __init__(self):
        global lilliCellsMap
        super(BrailleDisplayDriver, self).__init__()
        lilliCellsMap = [convertLilliCells(x) for x in range(256)]
        if (lilliDll.Init408USB()):
            self._keyCheckTimer = wx.PyTimer(self._handleKeyPresses)
            self._keyCheckTimer.Start(KEY_CHECK_INTERVAL)
        else:
            raise RuntimeError("No display found")

    def terminate(self):
        super(BrailleDisplayDriver, self).terminate()
        try:
            self._keyCheckTimer.Stop()
            self._keyCheckTimer = None
        except:
            pass
        lilliDll.Close408USB()

    def _get_numCells(self) -> int:
        return 40

    def _handleKeyPresses(self):
        while True:
            key: Optional[int] = None
            try:
                # Python 3: review required
                # The code seems to assume this returns an int.
                # I haven't confirmed this.
                key = lilliDll.ReadBuf()
            except:
                log.debug("", exc_info=True)
                pass
            if not key:
                break
            if (key <= 0x40) or (0x101 <= key <= 0x128):
                self._onKeyPress(key)

    def _onKeyPress(self, key: int):
        try:
            if 0x101 <= key <= 0x128:
                inputCore.manager.executeGesture(
                    InputGesture(ROUTE_COMMAND, key - 0x101))
            elif key <= 0x40:
                inputCore.manager.executeGesture(
                    InputGesture(LILLI_KEYS[key], 0))
        except inputCore.NoInputGestureAction:
            pass

    def display(self, cells: List[int]):
        lilliDll.WriteBuf(bytes(lilliCellsMap[x] for x in cells))

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(lilli):route", ),
            "braille_scrollBack": ("br(lilli):LF", ),
            "braille_previousLine": ("br(lilli):UP", ),
            "braille_nextLine": ("br(lilli):DN", ),
            "braille_scrollForward": ("br(lilli):RG", ),
            "kb:shift+tab": ("br(lilli):SLF", ),
            "kb:tab": ("br(lilli):SRG", ),
            "kb:alt+tab": ("br(lilli):SDN", ),
            "kb:alt+shift+tab": ("br(lilli):SUP", ),
        }
    })
Пример #4
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    _dev: hwIo.IoBase
    name = SEIKA_NAME
    # Translators: Name of a braille display.
    description = _("Seika Notetaker")
    path = ""
    isThreadSafe = True
    for d in hwPortUtils.listHidDevices():
        if d["hardwareID"].startswith(hidvidpid):
            path = d["devicePath"]

    @classmethod
    def getManualPorts(cls) -> typing.Iterator[typing.Tuple[str, str]]:
        """@return: An iterator containing the name and description for each port.
		"""
        return braille.getSerialPorts()

    def __init__(self, port="hid"):
        super().__init__()
        self.numCells = 0
        self.numBtns = 0
        self.numRoutingKeys = 0
        self.handle = None

        self._hidBuffer = b""
        self._command: typing.Optional[bytes] = None
        self._argsLen: typing.Optional[int] = None
        log.info(f"Seika Notetaker braille driver path: {self.path}")

        if self.path == "":
            raise RuntimeError("No MINI-SEIKA display found, no path found")
        self._dev = dev = hwIo.Hid(path=self.path, onReceive=self._onReceive)
        if dev._file == INVALID_HANDLE_VALUE:
            raise RuntimeError("No MINI-SEIKA display found, open error")
        dev.setFeature(SEIKA_CONFIG)  # baud rate, stop bit usw
        dev.setFeature(SEIKA_CMD_ON)  # device on
        dev.write(SEIKA_REQUEST_INFO)  # Request the Info from the device

        # wait and try to get info from the Braille display
        for i in range(MAX_READ_ATTEMPTS):  # the info-block is about
            dev.waitForRead(READ_TIMEOUT_SECS)
            if self.numCells:
                log.info(f"Seika notetaker on USB-HID,"
                         f" Cells {self.numCells}"
                         f" Buttons {self.numBtns}")
                break

        if self.numCells == 0:
            dev.close()
            raise RuntimeError("No MINI-SEIKA display found, no response")

    def terminate(self):
        try:
            super().terminate()
        finally:
            self._dev.close()

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        cellBytes = SEIKA_SEND_TEXT + self.numCells.to_bytes(
            1, 'little') + bytes(cells)
        self._dev.write(cellBytes)

    def _onReceive(self, data: bytes):
        """
		Note: Further insight into this function would be greatly appreciated.
		This function is a very simple state machine, each stage represents the collection of a field, when all
		fields are collected the command they represent can be processed.

		On each call to _onReceive three bytes are read from the device.
		The first and third bytes are discarded, the second byte is appended to a buffer.
		The buffer is accumulated until the buffer has the required number of bytes for the field being collected.
		There are 3 fields to be collected before a command can be processed:
		1: first 3 bytes: command
		2: 1 byte: specify length of subsequent arguments in bytes
		3: variable length: arguments for command type

		After accumulating enough bytes for each phase, the buffer is cleared and the next stage is entered.
		"""
        COMMAND_LEN = 3
        stream = BytesIO(data)
        cmd = stream.read(3)  # Note, first and third bytes are discarded
        newByte: bytes = cmd[1:2]  # use range to return bytes
        self._hidBuffer += newByte
        hasCommandBeenCollected = self._command is not None
        hasArgLenBeenCollected = self._argsLen is not None
        if (  # still collecting command bytes
                not hasCommandBeenCollected
                and len(self._hidBuffer) == COMMAND_LEN):
            self._command = self._hidBuffer  # command found reset and wait for args length
            self._hidBuffer = b""
        elif (  # next byte gives the command + args length
                hasCommandBeenCollected
                and not hasArgLenBeenCollected  # argsLen has not
        ):
            # the data is sent with the following structure
            # - command name (3 bytes)
            # - number of subsequent bytes to read (1 byte)
            # - Args (variable bytes)
            self._argsLen = ord(newByte)
            self._hidBuffer = b""
        elif (  # now collect the args,
                hasCommandBeenCollected and hasArgLenBeenCollected
                and len(self._hidBuffer) == self._argsLen):
            arg = self._hidBuffer
            command = self._command

            # reset state variables
            self._command = None
            self._argsLen = None
            self._hidBuffer = b""
            self._processCommand(command, arg)

    def _processCommand(self, command: bytes, arg: bytes) -> None:
        if command == SEIKA_INFO:
            self._handleInfo(arg)
        elif command == SEIKA_ROUTING:
            self._handleRouting(arg)
        elif command == SEIKA_KEYS:
            self._handleKeys(arg)
        elif command == SEIKA_KEYS_ROU:
            self._handleKeysRouting(arg)
        else:
            log.warning(
                f"Seika device has received an unknown command {command}")

    def _handleInfo(self, arg: bytes):
        """After sending a request for information from the braille device this data is returned to complete
		the handshake.
		"""
        self.numBtns = arg[0]
        self.numCells = arg[1]
        self.numRoutingKeys = arg[2]
        try:
            self._description = arg[3:].decode("ascii")
        except UnicodeDecodeError:
            log.debugWarning(
                f"Unable to decode Seika Notetaker description {arg[3:]}")

    def _handleRouting(self, arg: bytes):
        routingIndexes = _getRoutingIndexes(arg)
        for routingIndex in routingIndexes:
            gesture = InputGestureRouting(routingIndex)
            try:
                inputCore.manager.executeGesture(gesture)
            except inputCore.NoInputGestureAction:
                log.debug("No action for Seika Notetaker routing command")

    def _handleKeys(self, arg: bytes):
        brailleDots = arg[0]
        key = arg[1] | (arg[2] << 8)
        gestures = []
        if key:
            gestures.append(InputGesture(keys=key))
        if brailleDots:
            gestures.append(InputGesture(dots=brailleDots))
        for gesture in gestures:
            try:
                inputCore.manager.executeGesture(gesture)
            except inputCore.NoInputGestureAction:
                log.debug("No action for Seika Notetaker keys.")

    def _handleKeysRouting(self, arg: bytes):
        self._handleRouting(arg[3:])
        self._handleKeys(arg[:3])

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(seikantk):routing", ),
            "braille_scrollBack": ("br(seikantk):LB", ),
            "braille_scrollForward": ("br(seikantk):RB", ),
            "braille_previousLine": ("br(seikantk):LJ_UP", ),
            "braille_nextLine": ("br(seikantk):LJ_DOWN", ),
            "braille_toggleTether": ("br(seikantk):LJ_CENTER", ),
            "sayAll": ("br(seikantk):SPACE+BACKSPACE", ),
            "showGui": ("br(seikantk):RB+LB", ),
            "kb:tab": ("br(seikantk):LJ_RIGHT", ),
            "kb:shift+tab": ("br(seikantk):LJ_LEFT", ),
            "kb:upArrow": ("br(seikantk):RJ_UP", ),
            "kb:downArrow": ("br(seikantk):RJ_DOWN", ),
            "kb:leftArrow": ("br(seikantk):RJ_LEFT", ),
            "kb:rightArrow": ("br(seikantk):RJ_RIGHT", ),
            "kb:shift+upArrow":
            ("br(seikantk):SPACE+RJ_UP", "br(seikantk):BACKSPACE+RJ_UP"),
            "kb:shift+downArrow":
            ("br(seikantk):SPACE+RJ_DOWN", "br(seikantk):BACKSPACE+RJ_DOWN"),
            "kb:shift+leftArrow":
            ("br(seikantk):SPACE+RJ_LEFT", "br(seikantk):BACKSPACE+RJ_LEFT"),
            "kb:shift+rightArrow":
            ("br(seikantk):SPACE+RJ_RIGHT", "br(seikantk):BACKSPACE+RJ_RIGHT"),
            "kb:escape": ("br(seikantk):SPACE+RJ_CENTER", ),
            "kb:windows": ("br(seikantk):BACKSPACE+RJ_CENTER", ),
            "kb:space": (
                "br(seikantk):BACKSPACE",
                "br(seikantk):SPACE",
            ),
            "kb:backspace": ("br(seikantk):d7", ),
            "kb:pageup": ("br(seikantk):SPACE+LJ_RIGHT", ),
            "kb:pagedown": ("br(seikantk):SPACE+LJ_LEFT", ),
            "kb:home": ("br(seikantk):SPACE+LJ_UP", ),
            "kb:end": ("br(seikantk):SPACE+LJ_DOWN", ),
            "kb:control+home": ("br(seikantk):BACKSPACE+LJ_UP", ),
            "kb:control+end": ("br(seikantk):BACKSPACE+LJ_DOWN", ),
            "kb:enter": ("br(seikantk):RJ_CENTER", "br(seikantk):d8"),
        },
    })
Пример #5
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "brailliantB"
    # Translators: The name of a series of braille displays.
    description = _("HumanWare Brailliant BI/B series")

    @classmethod
    def check(cls):
        try:
            next(_getPorts())
        except StopIteration:
            # No possible ports found.
            return False
        return True

    def __init__(self):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0

        for portType, port in _getPorts():
            # Try talking to the display.
            try:
                self._ser = serial.Serial(port,
                                          baudrate=BAUD_RATE,
                                          parity=PARITY,
                                          timeout=TIMEOUT,
                                          writeTimeout=TIMEOUT)
            except serial.SerialException:
                continue
            # This will cause the number of cells to be returned.
            self._sendMessage(MSG_INIT)
            # #5406: With the new USB driver, the first command is ignored after a reconnection.
            # Worse, if we don't receive a reply,
            # _handleResponses freezes for some reason despite the timeout.
            # Send the init message again just in case.
            self._sendMessage(MSG_INIT)
            self._handleResponses(wait=True)
            if not self.numCells:
                # HACK: When connected via bluetooth, the display sometimes reports communication not allowed on the first attempt.
                self._sendMessage(MSG_INIT)
                self._handleResponses(wait=True)
            if self.numCells:
                # A display responded.
                log.info(
                    "Found display with {cells} cells connected via {type} ({port})"
                    .format(cells=self.numCells, type=portType, port=port))
                break

        else:
            raise RuntimeError("No display found")

        self._readTimer = wx.PyTimer(self._handleResponses)
        self._readTimer.Start(READ_INTERVAL)
        self._keysDown = set()
        self._ignoreKeyReleases = False

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
            self._readTimer.Stop()
            self._readTimer = None
        finally:
            # We absolutely must close the Serial object, as it does not have a destructor.
            # If we don't, we won't be able to re-open it later.
            self._ser.close()

    def _sendMessage(self, msgId, payload=""):
        if isinstance(payload, (int, bool)):
            payload = chr(payload)
        self._ser.write(
            "{header}{id}{length}{payload}".format(header=HEADER,
                                                   id=msgId,
                                                   length=chr(len(payload)),
                                                   payload=payload))

    def _handleResponses(self, wait=False):
        while wait or self._ser.inWaiting():
            msgId, payload = self._readPacket()
            if msgId:
                self._handleResponse(msgId, payload)
            wait = False

    def _readPacket(self):
        # Wait for the header.
        while True:
            char = self._ser.read(1)
            if char == HEADER:
                break
        msgId = self._ser.read(1)
        length = ord(self._ser.read(1))
        payload = self._ser.read(length)
        return msgId, payload

    def _handleResponse(self, msgId, payload):
        if msgId == MSG_INIT_RESP:
            if ord(payload[0]) != 0:
                # Communication not allowed.
                log.debugWarning(
                    "Display at %r reports communication not allowed" %
                    self._ser.port)
                return
            self.numCells = ord(payload[2])

        elif msgId == MSG_KEY_DOWN:
            payload = ord(payload)
            self._keysDown.add(payload)
            # This begins a new key combination.
            self._ignoreKeyReleases = False

        elif msgId == MSG_KEY_UP:
            payload = ord(payload)
            if not self._ignoreKeyReleases and self._keysDown:
                try:
                    inputCore.manager.executeGesture(
                        InputGesture(self._keysDown))
                except inputCore.NoInputGestureAction:
                    pass
                # Any further releases are just the rest of the keys in the combination being released,
                # so they should be ignored.
                self._ignoreKeyReleases = True
            self._keysDown.discard(payload)

        else:
            log.debugWarning(
                "Unknown message: id {id!r}, payload {payload!r}".format(
                    id=msgId, payload=payload))

    def display(self, cells):
        # cells will already be padded up to numCells.
        self._sendMessage(MSG_DISPLAY, "".join(chr(cell) for cell in cells))

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(brailliantB):left", ),
            "braille_scrollForward": ("br(brailliantB):right", ),
            "braille_previousLine": ("br(brailliantB):up", ),
            "braille_nextLine": ("br(brailliantB):down", ),
            "braille_routeTo": ("br(brailliantB):routing", ),
            "braille_toggleTether": ("br(brailliantB):up+down", ),
            "kb:upArrow": ("br(brailliantB):space+dot1", ),
            "kb:downArrow": ("br(brailliantB):space+dot4", ),
            "kb:leftArrow": ("br(brailliantB):space+dot3", ),
            "kb:rightArrow": ("br(brailliantB):space+dot6", ),
            "showGui": ("br(brailliantB):c1+c3+c4+c5", ),
            "kb:shift+tab": ("br(brailliantB):space+dot1+dot3", ),
            "kb:tab": ("br(brailliantB):space+dot4+dot6", ),
            "kb:alt": ("br(brailliantB):space+dot1+dot3+dot4", ),
            "kb:escape": ("br(brailliantB):space+dot1+dot5", ),
            "kb:enter": ("br(brailliantB):dot8", ),
            "kb:windows+d": ("br(brailliantB):c1+c4+c5", ),
            "kb:windows": ("br(brailliantB):space+dot3+dot4", ),
            "kb:alt+tab": ("br(brailliantB):space+dot2+dot3+dot4+dot5", ),
            "sayAll": ("br(brailliantB):c1+c2+c3+c4+c5+c6", ),
        },
    })
Пример #6
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    """ EcoBraille display driver.
	"""
    name = "ecoBraille"
    # Translators: The name of a braille display.
    description = _("EcoBraille displays")

    @classmethod
    def check(cls):
        return True

    @classmethod
    def getPossiblePorts(cls):
        ports = OrderedDict()
        for p in hwPortUtils.listComPorts():
            # Translators: Name of a serial communications port.
            ports[p["port"]] = _("Serial: {portName}").format(
                portName=p["friendlyName"])
        return ports

    def __init__(self, port):
        super(BrailleDisplayDriver, self).__init__()
        self._port = (port)
        # Try to open port
        self._dev = serial.Serial(self._port,
                                  baudrate=19200,
                                  bytesize=serial.EIGHTBITS,
                                  parity=serial.PARITY_NONE,
                                  stopbits=serial.STOPBITS_ONE)
        # Use a longer timeout when waiting for initialisation.
        self._dev.timeout = self._dev.writeTimeout = 2.7
        self._ecoType = eco_in_init(self._dev)
        # Use a shorter timeout hereafter.
        self._dev.timeout = self._dev.writeTimeout = TIMEOUT
        # Always send the protocol answer.
        self._dev.write("\x61\x10\x02\xf1\x57\x57\x57\x10\x03")
        self._dev.write("\x10\x02\xbc\x00\x00\x00\x00\x00\x10\x03")
        # Start keyCheckTimer.
        self._readTimer = wx.PyTimer(self._handleResponses)
        self._readTimer.Start(READ_INTERVAL)

    def terminate(self):
        super(BrailleDisplayDriver, self).terminate()
        try:
            self._dev.write("\x61\x10\x02\xf1\x57\x57\x57\x10\x03")
            self._readTimer.Stop()
            self._readTimer = None
        finally:
            self._dev.close()
            self._dev = None

    def _get_numCells(self):
        return self._ecoType

    def display(self, cells):
        try:
            self._dev.write(eco_out(cells))
        except:
            pass

    def _handleResponses(self):
        if self._dev.inWaiting():
            command = eco_in(self._dev)
            if command:
                try:
                    self._handleResponse(command)
                except KeyError:
                    pass

    def _handleResponse(self, command):
        if command in (ECO_KEY_STATUS1, ECO_KEY_STATUS2, ECO_KEY_STATUS3,
                       ECO_KEY_STATUS4):
            # Nothing to do with the status cells
            return 0
        if (command < ECO_END_ROUTING) and (command >= ECO_START_ROUTING):
            # Routing
            try:
                inputCore.manager.executeGesture(
                    InputGestureRouting(((command - ECO_START_ROUTING) >> 24) +
                                        1))
            except:
                log.debug(
                    "EcoBraille: No function associated with this routing key {key}"
                    .format(key=command))
        elif command > 0:
            # Button
            try:
                inputCore.manager.executeGesture(InputGestureKeys(command))
            except inputCore.NoInputGestureAction:
                log.debug(
                    "EcoBraille: No function associated with this Braille key {key}"
                    .format(key=command))
        return 0

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": "br(ecoBraille):routing",
            "braille_previousLine": "br(ecoBraille):T1",
            "braille_nextLine": "br(ecoBraille):T5",
            "braille_scrollBack": "br(ecoBraille):T2",
            "braille_scrollForward": "br(ecoBraille):T4",
            "review_activate": "br(ecoBraille):T3",
            "reviewMode_next": "br(ecoBraille):F1",
            "navigatorObject_parent": "br(ecoBraille):F2",
            "reviewMode_previous": "br(ecoBraille):F3",
            "navigatorObject_previous": "br(ecoBraille):F4",
            "navigatorObject_current": "br(ecoBraille):F5",
            "navigatorObject_next": "br(ecoBraille):F6",
            "navigatorObject_toFocus": "br(ecoBraille):F7",
            "navigatorObject_firstChild": "br(ecoBraille):F8",
            "navigatorObject_moveFocus": "br(ecoBraille):F9",
            "navigatorObject_currentDimensions": "br(ecoBraille):F0",
            "braille_toggleTether": "br(ecoBraille):A",
        }
    })
Пример #7
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "baum"
    # Translators: Names of braille displays.
    description = _("Baum/HumanWare/APH/Orbit braille displays")
    isThreadSafe = True

    @classmethod
    def check(cls):
        return True

    @classmethod
    def getPossiblePorts(cls):
        ports = OrderedDict()
        comPorts = list(hwPortUtils.listComPorts(onlyAvailable=True))
        try:
            next(cls._getAutoPorts(comPorts))
            ports.update((cls.AUTOMATIC_PORT, ))
        except StopIteration:
            pass
        for portInfo in comPorts:
            # Translators: Name of a serial communications port.
            ports[portInfo["port"]] = _("Serial: {portName}").format(
                portName=portInfo["friendlyName"])
        return ports

    @classmethod
    def _getAutoPorts(cls, comPorts):
        for portInfo in hwPortUtils.listHidDevices():
            if portInfo.get("usbID") in USB_IDS_HID:
                yield portInfo["devicePath"], "USB HID"
        # Try bluetooth ports last.
        for portInfo in sorted(comPorts,
                               key=lambda item: "bluetoothName" in item):
            port = portInfo["port"]
            hwID = portInfo["hardwareID"]
            if hwID.startswith(r"FTDIBUS\COMPORT"):
                # USB.
                portType = "USB serial"
                try:
                    usbID = hwID.split("&", 1)[1]
                except IndexError:
                    continue
                if usbID not in USB_IDS_SER:
                    continue
            elif hwID == r"USB\VID_0483&PID_5740&REV_0200":
                # Generic STMicroelectronics Virtual COM Port used by Orbit Reader 20.
                portType = "USB serial"
            elif "bluetoothName" in portInfo:
                # Bluetooth.
                portType = "bluetooth"
                btName = portInfo["bluetoothName"]
                if not any(
                        btName.startswith(prefix)
                        for prefix in BLUETOOTH_NAMES):
                    continue
            else:
                continue
            yield port, portType

    def __init__(self, port="Auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._deviceID = None

        if port == "auto":
            tryPorts = self._getAutoPorts(
                hwPortUtils.listComPorts(onlyAvailable=True))
        else:
            tryPorts = ((port, "serial"), )
        for port, portType in tryPorts:
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            self.isHid = portType == "USB HID"
            try:
                if self.isHid:
                    self._dev = hwIo.Hid(port, onReceive=self._onReceive)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            timeout=TIMEOUT,
                                            writeTimeout=TIMEOUT,
                                            onReceive=self._onReceive)
            except EnvironmentError:
                continue
            if self.isHid:
                try:
                    # It's essential to send protocol on for the Orbit Reader 20.
                    self._sendRequest(BAUM_PROTOCOL_ONOFF, True)
                except EnvironmentError:
                    # Pronto! and VarioUltra don't support BAUM_PROTOCOL_ONOFF.
                    pass
                # Explicitly request device info.
                # Even where it's supported, BAUM_PROTOCOL_ONOFF doesn't always return device info.
                self._sendRequest(BAUM_REQUEST_INFO, 0)
            else:  # Serial
                # If the protocol is already on, sending protocol on won't return anything.
                # First ensure it's off.
                self._sendRequest(BAUM_PROTOCOL_ONOFF, False)
                # This will cause the device id, serial number and number of cells to be returned.
                self._sendRequest(BAUM_PROTOCOL_ONOFF, True)
                # Send again in case the display misses the first one.
                self._sendRequest(BAUM_PROTOCOL_ONOFF, True)
            for i in xrange(3):
                # An expected response hasn't arrived yet, so wait for it.
                self._dev.waitForRead(TIMEOUT)
                if self.numCells and self._deviceID:
                    break
            if self.numCells:
                # A display responded.
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self._deviceID, type=portType, port=port))
                break
            self._dev.close()

        else:
            raise RuntimeError("No Baum display found")

        self._keysDown = {}
        self._ignoreKeyReleases = False

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
            try:
                self._sendRequest(BAUM_PROTOCOL_ONOFF, False)
            except EnvironmentError:
                # Some displays don't support BAUM_PROTOCOL_ONOFF.
                pass
        finally:
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    def _sendRequest(self, command, arg=""):
        if isinstance(arg, (int, bool)):
            arg = chr(arg)
        if self.isHid:
            self._dev.write(command + arg)
        else:
            self._dev.write("\x1b{command}{arg}".format(command=command,
                                                        arg=arg.replace(
                                                            ESCAPE,
                                                            ESCAPE * 2)))

    def _onReceive(self, data):
        if self.isHid:
            # data contains the entire packet.
            stream = StringIO(data)
        else:
            if data != ESCAPE:
                log.debugWarning("Ignoring byte before escape: %r" % data)
                return
            # data only contained the escape. Read the rest from the device.
            stream = self._dev
        command = stream.read(1)
        length = BAUM_RSP_LENGTHS.get(command, 0)
        if command == BAUM_ROUTING_KEYS:
            length = 10 if self.numCells > 40 else 5
        arg = stream.read(length)
        if command == BAUM_DEVICE_ID and arg == "Refreshabraille ":
            # For most Baum devices, the argument is 16 bytes,
            # but it is 18 bytes for the Refreshabraille.
            arg += stream.read(2)
        self._handleResponse(command, arg)

    def _handleResponse(self, command, arg):
        if command == BAUM_CELL_COUNT:
            self.numCells = ord(arg)
        elif command == BAUM_DEVICE_ID:
            # Short ids can be padded with either nulls or spaces.
            self._deviceID = arg.rstrip("\0 ")
        elif command in KEY_NAMES:
            arg = sum(
                ord(byte) << offset * 8 for offset, byte in enumerate(arg))
            if arg < self._keysDown.get(command, 0):
                # Release.
                if not self._ignoreKeyReleases:
                    # The first key released executes the key combination.
                    try:
                        inputCore.manager.executeGesture(
                            InputGesture(self._keysDown))
                    except inputCore.NoInputGestureAction:
                        pass
                    # Any further releases are just the rest of the keys in the combination being released,
                    # so they should be ignored.
                    self._ignoreKeyReleases = True
            else:
                # Press.
                # This begins a new key combination.
                self._ignoreKeyReleases = False
            if arg > 0:
                self._keysDown[command] = arg
            elif command in self._keysDown:
                # All keys in this group have been released.
                # #3541: Remove this group so it doesn't count as a group with keys down.
                del self._keysDown[command]

        elif command == BAUM_POWERDOWN:
            log.debug("Power down")
        elif command in (BAUM_COMMUNICATION_CHANNEL, BAUM_SERIAL_NUMBER):
            pass

        else:
            log.debugWarning("Unknown command {command!r}, arg {arg!r}".format(
                command=command, arg=arg))

    def display(self, cells):
        # cells will already be padded up to numCells.
        self._sendRequest(BAUM_DISPLAY_DATA,
                          "".join(chr(cell) for cell in cells))

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(baum):d2", ),
            "braille_scrollForward": ("br(baum):d5", ),
            "braille_previousLine": ("br(baum):d1", ),
            "braille_nextLine": ("br(baum):d3", ),
            "braille_routeTo": ("br(baum):routing", ),
            "kb:upArrow": ("br(baum):up", ),
            "kb:downArrow": ("br(baum):down", ),
            "kb:leftArrow": ("br(baum):left", ),
            "kb:rightArrow": ("br(baum):right", ),
            "kb:enter": ("br(baum):select", ),
        },
    })
Пример #8
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    name = "handyTech"
    # Translators: The name of a series of braille displays.
    description = _("Handy Tech braille displays")
    isThreadSafe = True
    receivesAckPackets = True
    timeout = 0.2

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts()

    _dev: Optional[Union[hwIo.Hid, hwIo.Serial]]

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._model = None
        self._ignoreKeyReleases = False
        self._keysDown = set()
        self.brailleInput = False
        self._dotFirmness = 1
        self._hidSerialBuffer = b""
        self._atc = False
        self._sleepcounter = 0

        for portType, portId, port, portInfo in self._getTryPorts(port):
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            self.isHid = portType == bdDetect.KEY_HID
            self.isHidSerial = portId in USB_IDS_HID_CONVERTER
            self.port = port
            try:
                if self.isHidSerial:
                    # This is either the standalone HID adapter cable for older displays,
                    # or an older display with a HID - serial adapter built in
                    self._dev = hwIo.Hid(port,
                                         onReceive=self._hidSerialOnReceive)
                    # Send a flush to open the serial channel
                    self._dev.write(HT_HID_RPT_InCommand +
                                    HT_HID_CMD_FlushBuffers)
                elif self.isHid:
                    self._dev = hwIo.Hid(port, onReceive=self._hidOnReceive)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            parity=PARITY,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._serialOnReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue

            self.sendPacket(HT_PKT_RESET)
            for _i in range(3):
                # An expected response hasn't arrived yet, so wait for it.
                self._dev.waitForRead(self.timeout)
                if self.numCells and self._model:
                    break

            if self.numCells:
                # A display responded.
                self._model.postInit()
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self._model.name, type=portType, port=port))
                # Create the message window on the ui thread.
                wx.CallAfter(self.create_message_window)
                break
            self._dev.close()

        else:
            raise RuntimeError("No Handy Tech display found")

    def create_message_window(self):
        try:
            self._sleepcounter = 0
            self._messageWindow = InvisibleDriverWindow(self)
        except WindowsError:
            log.debugWarning("", exc_info=True)

    def destroy_message_window(self):
        try:
            self._messageWindow.destroy()
        except WindowsError:
            log.debugWarning("", exc_info=True)

    def go_to_sleep(self):
        self._sleepcounter += 1
        if self._dev is not None:
            # Must sleep before and after closing to ensure the device can be reconnected.
            time.sleep(self.timeout)
            self._dev.close()
            self._dev = None
            time.sleep(self.timeout)

    def wake_up(self):
        if self._sleepcounter > 0:
            self._sleepcounter -= 1
        if self._sleepcounter > 0:  # Still not zero after decrementing
            return
        # Might throw if device no longer exists.
        # We leave it to autodetection to grab it when it reappears.
        if self.isHidSerial:
            # This is either the standalone HID adapter cable for older displays,
            # or an older display with a HID - serial adapter built in
            self._dev = hwIo.Hid(self.port, onReceive=self._hidSerialOnReceive)
            # Send a flush to open the serial channel
            self._dev.write(HT_HID_RPT_InCommand + HT_HID_CMD_FlushBuffers)
        elif self.isHid:
            self._dev = hwIo.Hid(self.port, onReceive=self._hidOnReceive)
        else:
            self._dev = hwIo.Serial(self.port,
                                    baudrate=BAUD_RATE,
                                    parity=PARITY,
                                    timeout=self.timeout,
                                    writeTimeout=self.timeout,
                                    onReceive=self._serialOnReceive)

    def terminate(self):
        try:
            # Make sure this is called on the ui thread.
            wx.CallAfter(self.destroy_message_window)
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # We must sleep before closing the  connection as not doing this can leave the display in a bad state where it can not be re-initialized.
            # This has been observed for Easy Braille displays.
            time.sleep(self.timeout)
            # Make sure the device gets closed.
            self._dev.close()
            # We also must sleep after closing, as it sometimes takes some time for the device to disconnect.
            # This has been observed for Active Braille displays.
            time.sleep(self.timeout)

    def _get_supportedSettings(self):
        settings = [
            braille.BrailleDisplayDriver.BrailleInputSetting(),
        ]
        if self._model:
            # Add the per model supported settings to the list.
            for cls in self._model.__class__.__mro__:
                if hasattr(cls, "supportedSettings"):
                    settings.extend(cls.supportedSettings)
        return settings

    def _get_atc(self):
        return self._atc

    def _set_atc(self, state):
        if self._atc is state:
            return
        if isinstance(self._model, AtcMixin):
            self.sendExtendedPacket(HT_EXTPKT_SET_ATC_MODE, boolToByte(state))
        else:
            log.debugWarning("Changing ATC setting for unsupported device %s" %
                             self._model.name)
        # Regardless whether this setting is supported or not, we want to safe its state.
        self._atc = state

    def _get_dotFirmness(self):
        return self._dotFirmness

    def _set_dotFirmness(self, value):
        if self._dotFirmness is value:
            return
        if isinstance(self._model, TimeSyncFirmnessMixin):
            self.sendExtendedPacket(HT_EXTPKT_SET_FIRMNESS, intToByte(value))
        else:
            log.debugWarning(
                "Changing dot firmness setting for unsupported device %s" %
                self._model.name)
        # Regardless whether this setting is supported or not, we want to safe its state.
        self._dotFirmness = value

    def sendPacket(self, packetType: bytes, data: bytes = b""):
        if self._sleepcounter > 0:
            return
        if self.isHid:
            self._sendHidPacket(packetType + data)
        else:
            self._dev.write(packetType + data)

    def sendExtendedPacket(self, packetType: bytes, data: bytes = b""):
        if self._sleepcounter > 0:
            log.debug("Packet discarded as driver was requested to sleep")
            return
        packetBytes: bytes = b"".join([
            intToByte(len(data) + len(packetType)), packetType, data, b"\x16"
        ])
        if self._model:
            packetBytes = self._model.deviceId + packetBytes
        self.sendPacket(HT_PKT_EXTENDED, packetBytes)

    def _sendHidPacket(self, packet: bytes):
        assert self.isHid
        maxBlockSize = self._dev._writeSize - 3
        # When the packet length exceeds C{writeSize}, the packet is split up into several packets.
        # They contain C{HT_HID_RPT_InData}, the length of the data block,
        # the data block itself and a terminating null character.
        for offset in range(0, len(packet), maxBlockSize):
            block = packet[offset:offset + maxBlockSize]
            hidPacket = HT_HID_RPT_InData + intToByte(
                len(block)) + block + b"\x00"
            self._dev.write(hidPacket)

    def _handleKeyRelease(self):
        if self._ignoreKeyReleases or not self._keysDown:
            return
        # The first key released executes the key combination.
        try:
            inputCore.manager.executeGesture(
                InputGesture(self._model, self._keysDown, self.brailleInput))
        except inputCore.NoInputGestureAction:
            pass
        # Any further releases are just the rest of the keys in the combination
        # being released, so they should be ignored.
        self._ignoreKeyReleases = True

    # pylint: disable=R0912
    # Pylint complains about many branches, might be worth refactoring
    def _hidOnReceive(self, data: bytes):
        # data contains the entire packet.
        stream = BytesIO(data)
        htPacketType = data[2:3]
        # Skip the header, so reading the stream will only give the rest of the data
        stream.seek(3)
        self._handleInputStream(htPacketType, stream)

    def _hidSerialOnReceive(self, data: bytes):
        # The HID serial converter wraps one or two bytes into a single HID packet
        hidLength = data[1]
        self._hidSerialBuffer += data[2:(2 + hidLength)]
        self._processHidSerialBuffer()

    def _processHidSerialBuffer(self):
        while self._hidSerialBuffer:
            currentBufferLength = len(self._hidSerialBuffer)
            htPacketType: bytes = self._hidSerialBuffer[0:1]
            if htPacketType != HT_PKT_EXTENDED:
                packetLength = 2 if htPacketType == HT_PKT_OK else 1
                if currentBufferLength >= packetLength:
                    stream = BytesIO(self._hidSerialBuffer[:packetLength])
                    self._hidSerialBuffer: bytes = self._hidSerialBuffer[
                        packetLength:]
                else:
                    # The packet is not yet complete
                    return
            elif htPacketType == HT_PKT_EXTENDED and currentBufferLength >= 5:
                # Check whether our packet is complete
                # Extended packets are at least 5 bytes in size.
                # The second byte is the model, the third byte is the data length, excluding the terminator
                packet_length = self._hidSerialBuffer[2] + 4
                if len(self._hidSerialBuffer) < packet_length:
                    # The packet is not yet complete
                    return
                # We have a complete packet.
                # We also isolate it from another packet that could have landed in the buffer,
                stream = BytesIO(self._hidSerialBuffer[:packet_length])
                self._hidSerialBuffer: bytes = self._hidSerialBuffer[
                    packet_length:]
                if len(self._hidSerialBuffer) == packet_length:
                    assert self._hidSerialBuffer.endswith(
                        b"\x16"), "Extended packet terminator expected"
            else:
                # The packet is not yet complete
                return
            stream.seek(1)
            self._handleInputStream(htPacketType, stream)

    def _serialOnReceive(self, data: bytes):
        self._handleInputStream(data, self._dev)

    def _handleInputStream(self, htPacketType: bytes, stream):
        if htPacketType in (HT_PKT_OK, HT_PKT_EXTENDED):
            modelId: bytes = stream.read(1)
            if not self._model:
                if modelId not in MODELS:
                    log.debugWarning("Unknown model: %r" % modelId)
                    raise RuntimeError(
                        "The model with ID %r is not supported by this driver"
                        % modelId)
                self._model = MODELS.get(modelId)(self)
                self.numCells = self._model.numCells
            elif self._model.deviceId != modelId:
                # Somehow the model ID of this display changed, probably another display
                # plugged in the same (already open) serial port.
                self.terminate()

        if htPacketType == HT_PKT_OK:
            pass
        elif htPacketType == HT_PKT_ACK:
            # This is unexpected, but we need to make sure that we handle old style ack
            self._handleAck()
        elif htPacketType == HT_PKT_NAK:
            log.debugWarning("NAK received!")
        elif htPacketType == HT_PKT_EXTENDED:
            packet_length = ord(stream.read(1))
            packet: bytes = stream.read(packet_length)
            terminator: bytes = stream.read(1)
            assert terminator == b"\x16"  # Extended packets are terminated with \x16
            extPacketType = packet[0:1]
            if extPacketType == HT_EXTPKT_CONFIRMATION:
                # Confirmation of a command.
                if packet[1:2] == HT_PKT_ACK:
                    self._handleAck()
                elif packet[1:2] == HT_PKT_NAK:
                    log.debugWarning("NAK received!")
            elif extPacketType == HT_EXTPKT_KEY:
                self._handleInput(packet[1])
            elif extPacketType == HT_EXTPKT_ATC_INFO:
                # Ignore ATC packets for now
                pass
            elif extPacketType == HT_EXTPKT_GET_PROTOCOL_PROPERTIES:
                pass
            elif isinstance(self._model, TimeSyncFirmnessMixin):
                if extPacketType == HT_EXTPKT_GET_RTC:
                    self._model.handleTime(packet[1:])
                elif extPacketType == HT_EXTPKT_GET_FIRMNESS:
                    self._dotFirmness = packet[1]
            else:
                # Unknown extended packet, log it
                log.debugWarning("Unhandled extended packet of type %r: %r" %
                                 (extPacketType, packet))
        else:
            serPacketOrd = ord(htPacketType)
            if isinstance(
                    self._model, OldProtocolMixin
            ) and serPacketOrd & ~KEY_RELEASE_MASK < ord(HT_PKT_EXTENDED):
                self._handleInput(serPacketOrd)
            else:
                # Unknown packet type, log it
                log.debugWarning("Unhandled packet of type %r" % htPacketType)

    def _handleInput(self, key: int):
        release = (key & KEY_RELEASE_MASK) != 0
        if release:
            key ^= KEY_RELEASE_MASK
            self._handleKeyRelease()
            self._keysDown.discard(key)
        else:
            # Press.
            # This begins a new key combination.
            self._ignoreKeyReleases = False
            self._keysDown.add(key)

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        self._model.display(cells)

    scriptCategory = SCRCAT_BRAILLE

    def script_toggleBrailleInput(self, _gesture):
        self.brailleInput = not self.brailleInput
        if self.brailleInput:
            # Translators: message when braille input is enabled
            ui.message(_('Braille input enabled'))
        else:
            # Translators: message when braille input is disabled
            ui.message(_('Braille input disabled'))

    # Translators: description of the script to toggle braille input
    script_toggleBrailleInput.__doc__ = _("Toggle braille input")

    __gestures = {
        'br(handytech):space+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):leftSpace+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):rightSpace+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech.easybraille):left+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech.easybraille):right+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):space+dot1+dot2+dot7': 'toggleBrailleInput',
    }

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(handyTech):routing", ),
            "braille_scrollBack": (
                "br(handytech):leftSpace",
                "br(handytech):leftTakTop",
                "br(handytech):rightTakTop",
                "br(handytech):b3",
                "br(handytech):left",
            ),
            "braille_previousLine": ("br(handytech):b4", ),
            "braille_nextLine": ("br(handytech):b5", ),
            "braille_scrollForward": (
                "br(handytech):rightSpace",
                "br(handytech):leftTakBottom",
                "br(handytech):rightTakBottom",
                "br(handytech):b6",
                "br(handytech):right",
            ),
            "braille_toggleTether": ("br(handytech):b2", ),
            "braille_toggleFocusContextPresentation": ("br(handytech):b7", ),
            "braille_toggleShowCursor": ("br(handytech):b1", ),
            "kb:shift+tab": (
                "br(handytech):leftTakTop+leftTakBottom",
                "br(handytech):escape",
            ),
            "kb:tab": (
                "br(handytech):rightTakTop+rightTakBottom",
                "br(handytech):return",
            ),
            "kb:enter": (
                "br(handytech):leftTakTop+leftTakBottom+rightTakTop+rightTakBottom",
                "br(handytech):b8",
                "br(handytech):escape+return",
                "br(handytech):joystickAction",
            ),
            "kb:alt": ("br(handytech):b2+b4+b5", ),
            "kb:escape": ("br(handytech):b4+b6", ),
            "kb:upArrow": ("br(handytech):joystickUp", ),
            "kb:downArrow": ("br(handytech):joystickDown", ),
            "kb:leftArrow": ("br(handytech):joystickLeft", ),
            "kb:rightArrow": ("br(handytech):joystickRight", ),
            "kb:1": ("br(handytech):n1", ),
            "kb:2": ("br(handytech):n2", ),
            "kb:3": ("br(handytech):n3", ),
            "kb:4": ("br(handytech):n4", ),
            "kb:5": ("br(handytech):n5", ),
            "kb:6": ("br(handytech):n6", ),
            "kb:7": ("br(handytech):n7", ),
            "kb:8": ("br(handytech):n8", ),
            "kb:9": ("br(handytech):n9", ),
            "kb:0": ("br(handytech):n0", ),
            "showGui": ("br(handytech):b2+b4+b5+b6", ),
        },
    })
Пример #9
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "hedoMobilLine"
    description = "hedo MobilLine USB"

    numCells = HEDO_MOBIL_CELL_COUNT

    @classmethod
    def check(cls):
        return True

    def __init__(self):
        super(BrailleDisplayDriver, self).__init__()

        for portInfo in hwPortUtils.listComPorts(onlyAvailable=True):
            port = portInfo["port"]
            hwID = portInfo["hardwareID"]
            # log.info("Found port {port} with hardwareID {hwID}".format(port=port, hwID=hwID))
            if not hwID.startswith(r"FTDIBUS\COMPORT"):
                continue
            if HEDO_MOBIL_USBID not in hwID:
                continue
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            try:
                self._ser = serial.Serial(port,
                                          baudrate=HEDO_MOBIL_BAUDRATE,
                                          timeout=HEDO_MOBIL_TIMEOUT,
                                          writeTimeout=HEDO_MOBIL_TIMEOUT,
                                          parity=serial.PARITY_ODD,
                                          bytesize=serial.EIGHTBITS,
                                          stopbits=serial.STOPBITS_ONE)
            except serial.SerialException:
                continue

            # Prepare a blank line
            cells = chr(HEDO_MOBIL_INIT) + chr(0) * (
                HEDO_MOBIL_CELL_COUNT + HEDO_MOBIL_STATUS_CELL_COUNT)

            # Send the blank line twice
            self._ser.write(cells)
            self._ser.flush()
            self._ser.write(cells)
            self._ser.flush()

            # Read out the input buffer
            ackS = self._ser.read(2)
            if chr(HEDO_MOBIL_ACK) in ackS:
                log.info("Found hedo MobilLine connected via {port}".format(
                    port=port))
                break

        else:
            raise RuntimeError("No display found")

        self._readTimer = wx.PyTimer(self.handleResponses)
        self._readTimer.Start(HEDO_MOBIL_READ_INTERVAL)

        self._keysDown = set()
        self._released_keys = set()

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
            self._readTimer.Stop()
            self._readTimer = None
        finally:
            # We absolutely must close the Serial object, as it does not have a destructor.
            # If we don't, we won't be able to re-open it later.
            self._ser.close()

    def display(self, cells):
        # every transmitted line consists of the preamble HEDO_MOBIL_INIT, the statusCells and the Cells
        line = chr(
            HEDO_MOBIL_INIT) + chr(0) * HEDO_MOBIL_STATUS_CELL_COUNT + "".join(
                chr(cell) for cell in cells)
        # cells are already padded up numCells
        # thus the expected length of the line is 1 + HEDO_MOBIL_STATUS_CELL_COUNT + HEDO_MOBIL_CELL_COUNT
        # ... just how it should be
        self._ser.write(line)

    def handleResponses(self, wait=False):
        while wait or self._ser.inWaiting():
            data = self._ser.read(1)
            if data:
                # do not handle acknowledge bytes
                if data != chr(HEDO_MOBIL_ACK):
                    self.handleData(ord(data))
            wait = False

    def handleData(self, data):
        if data >= HEDO_MOBIL_CR_BEGIN and data <= HEDO_MOBIL_CR_END:
            # Routing key is pressed
            try:
                inputCore.manager.executeGesture(
                    InputGestureRouting(data - HEDO_MOBIL_CR_BEGIN))
            except inputCore.NoInputGestureAction:
                log.debug("No Action for routing index " + index)
                pass
            return

        # On every keypress or keyrelease information about all keys is sent
        # There are three groups of keys thus three bytes will be sent on
        # each keypress or release
        # The 4 MSB of each byte mark the group
        # Bytes of the form 0x0? include information for B1 to B3
        # Bytes of the form 0x1? include information for B4 to B6
        # Bytes of the form 0x2? include information for K1 to K3
        # The 4 LSB mark the pressed buttons in the group
        # Are all buttons of one group released, the 4 LSB are zero
        if data & 0xF0 == 0x00:
            # B1..B3
            if data & 0x01:
                self._keysDown.add("B1")
            if data & 0x02:
                self._keysDown.add("B2")
            if data & 0x04:
                self._keysDown.add("B3")
            if data == 0x00:
                self._released_keys.add("B1")

        elif data & 0xF0 == 0x10:
            # B4..B6
            if data & 0x01:
                self._keysDown.add("B4")
            if data & 0x02:
                self._keysDown.add("B5")
            if data & 0x04:
                self._keysDown.add("B6")
            if data == 0x10:
                self._released_keys.add("B4")

        elif data & 0xF0 == 0x20:
            # K1..K3
            if data & 0x01:
                self._keysDown.add("K1")
            if data & 0x02:
                self._keysDown.add("K2")
            if data & 0x04:
                self._keysDown.add("K3")
            if data == 0x20:
                self._released_keys.add("K1")

        if "B1" in self._released_keys and "B4" in self._released_keys and "K1" in self._released_keys:
            # all keys are released
            keys = "+".join(self._keysDown)
            self._keysDown = set()
            self._released_keys = set()
            try:
                inputCore.manager.executeGesture(InputGestureKeys(keys))
            except inputCore.NoInputGestureAction:
                log.debug("No Action for keys " + keys)
                pass

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(hedoMobilLine):K1", ),
            "braille_toggleTether": ("br(hedoMobilLine):K2", ),
            "braille_scrollForward": ("br(hedoMobilLine):K3", ),
            "braille_previousLine": ("br(hedoMobilLine):B2", ),
            "braille_nextLine": ("br(hedoMobilLine):B5", ),
            "sayAll": ("br(hedoMobilLine):B6", ),
            "braille_routeTo": ("br(hedoMobilLine):routing", ),
        },
    })
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    _dev: hwIo.IoBase
    # Used to for error checking.
    _awaitingFrameReceipts: Dict[int, Any]
    name = "eurobraille"
    # Translators: Names of braille displays.
    description = _("Eurobraille Esys/Esytime/Iris displays")
    isThreadSafe = True
    timeout = 0.2
    supportedSettings = (braille.BrailleDisplayDriver.HIDInputSetting(
        useConfig=True), )

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts()

    def __init__(self, port="Auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self.deviceType = None
        self._deviceData = {}
        self._awaitingFrameReceipts = {}
        self._frameLength = None
        self._frame = 0x20
        self._frameLock = threading.Lock()
        self._hidKeyboardInput = False
        self._hidInputBuffer = b""

        for portType, portId, port, portInfo in self._getTryPorts(port):
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            self.isHid = portType == bdDetect.KEY_HID
            try:
                if self.isHid:
                    self._dev = hwIo.Hid(
                        port,
                        onReceive=self._onReceive,
                        # Eurobraille wants us not to block other application's access to this handle.
                        exclusive=False)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            bytesize=serial.EIGHTBITS,
                                            parity=serial.PARITY_EVEN,
                                            stopbits=serial.STOPBITS_ONE,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("Error while connecting to port %r" % port,
                                 exc_info=True)
                continue

            for i in range(3):
                # Request device identification
                self._sendPacket(EB_SYSTEM, EB_SYSTEM_IDENTITY)
                # Make sure visualisation packets are disabled, as we ignore them anyway.
                self._sendPacket(EB_VISU, EB_VISU_DOT, EB_FALSE)
                # A device identification results in multiple packets.
                # Make sure we've received everything before we continue
                while self._dev.waitForRead(self.timeout * 2):
                    continue
                if self.numCells and self.deviceType:
                    break
            if self.numCells and self.deviceType:
                # A display responded.
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self.deviceType, type=portType, port=port))
                break
            self._dev.close()

        else:
            raise RuntimeError("No supported Eurobraille display found")

        self.keysDown = defaultdict(int)
        self._ignoreCommandKeyReleases = False

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # We must sleep before closing the port as not doing this can leave the display in a bad state where it can not be re-initialized.
            time.sleep(self.timeout)
            self._dev.close()
            self._dev = None
            self._deviceData.clear()

    def _prepFirstByteStreamAndData(
            self, data: bytes) -> (bytes, Union[BytesIO, hwIo.IoBase], bytes):
        if self.isHid:
            # data contains the entire packet.
            # HID Packets start with 0x00.
            byte0 = data[0:1]
            assert byte0 == b"\x00", "byte 0 is %r" % byte0
            # Check whether there is an incomplete packet in the buffer
            if self._hidInputBuffer:
                data = self._hidInputBuffer + data[1:]
                self._hidInputBuffer = b""
            byte1 = data[1:2]
            stream = BytesIO(data)
            stream.seek(2)
            return byte1, stream, data
        else:  # is serial
            return data, self._dev, data

    def _onReceive(self, data: bytes):
        byte1, stream, data = self._prepFirstByteStreamAndData(data)

        if byte1 == ACK:
            frame = ord(stream.read(1))
            self._handleAck(frame)
        elif byte1 == STX:
            length = bytesToInt(
                stream.read(2)) - 2  # length includes the length itself
            packet: bytes = stream.read(length)
            if self.isHid and not stream.read(1) == ETX:
                # Incomplete packet
                self._hidInputbuffer = data
                return
            packetType: bytes = packet[0:1]
            packetSubType: bytes = packet[1:2]
            packetData: bytes = packet[2:] if length > 2 else b""
            if packetType == EB_SYSTEM:
                self._handleSystemPacket(packetSubType, packetData)
            elif packetType == EB_MODE:
                if packetSubType == EB_MODE_DRIVER:
                    log.debug(
                        "Braille display switched to driver mode, updating display..."
                    )
                    braille.handler.update()
                elif packetSubType == EB_MODE_INTERNAL:
                    log.debug("Braille display switched to internal mode")
            elif packetType == EB_KEY:
                self._handleKeyPacket(packetSubType, packetData)
            elif packetType == EB_IRIS_TEST and packetSubType == EB_IRIS_TEST_sub:
                # Ping command sent by Iris every two seconds, send it back on the main thread.
                # This means that, if the main thread is frozen, Iris will be notified of this.
                log.debug("Received ping from Iris braille display")
                wx.CallAfter(self._sendPacket, packetType, packetSubType,
                             packetData)
            elif packetType == EB_VISU:
                log.debug("Ignoring visualisation packet")
            elif packetType == EB_ENCRYPTION_KEY:
                log.debug("Ignoring encryption key packet")
            else:
                log.debug("Ignoring packet: type %r, subtype %r, data %r" %
                          (packetType, packetSubType, packetData))

    def _handleAck(self, frame: int):
        try:
            super(BrailleDisplayDriver, self)._handleAck()
        except NotImplementedError:
            log.debugWarning(
                "Received ACK for frame %d while ACK handling is disabled" %
                frame)
        else:
            try:
                del self._awaitingFrameReceipts[frame]
            except KeyError:
                log.debugWarning("Received ACK for unregistered frame %d" %
                                 frame)

    def _handleSystemPacket(self, packetType: bytes, data: bytes):
        if packetType == EB_SYSTEM_TYPE:
            deviceType = ord(data)
            self.deviceType = DEVICE_TYPES[deviceType]
            if 0x01 <= deviceType <= 0x06:  # Iris
                self.keys = KEYS_IRIS
            elif 0x07 <= deviceType <= 0x0d:  # Esys
                self.keys = KEYS_ESYS
            elif 0x0e <= deviceType <= 0x11:  # Esitime
                self.keys = KEYS_ESITIME
            else:
                log.debugWarning("Unknown device identifier %r" % data)
        elif packetType == EB_SYSTEM_DISPLAY_LENGTH:
            self.numCells = ord(data)
        elif packetType == EB_SYSTEM_FRAME_LENGTH:
            self._frameLength = bytesToInt(data)
        elif packetType == EB_SYSTEM_PROTOCOL and self.isHid:
            protocol = data.rstrip(b"\x00 ")
            try:
                version = float(protocol[:4])
            except ValueError:
                pass
            else:
                self.receivesAckPackets = version >= 3.0
        elif packetType == EB_SYSTEM_IDENTITY:
            return  # End of system information
        self._deviceData[packetType] = data.rstrip(b"\x00 ")

    def _handleKeyPacket(self, group: bytes, data: bytes):
        if group == EB_KEY_USB_HID_MODE:
            assert data in [EB_TRUE, EB_FALSE]
            self._hidKeyboardInput = EB_TRUE == data
            return
        if group == EB_KEY_QWERTY:
            log.debug("Ignoring Iris AZERTY/QWERTY input")
            return
        if group == EB_KEY_INTERACTIVE and data[
                0:1] == EB_KEY_INTERACTIVE_REPETITION:
            log.debug("Ignoring routing key %d repetition" % (data[1] - 1))
            return
        arg = bytesToInt(data)
        if arg == self.keysDown[group]:
            log.debug("Ignoring key repetition")
            return
        self.keysDown[group] |= arg
        isIris = self.deviceType.startswith("Iris")
        if not isIris and group == EB_KEY_COMMAND and arg >= self.keysDown[
                group]:
            # Started a gesture including command keys
            self._ignoreCommandKeyReleases = False
        else:
            if isIris or group != EB_KEY_COMMAND or not self._ignoreCommandKeyReleases:
                try:
                    inputCore.manager.executeGesture(InputGesture(self))
                except inputCore.NoInputGestureAction:
                    pass
                self._ignoreCommandKeyReleases = not isIris and (
                    group == EB_KEY_COMMAND
                    or self.keysDown[EB_KEY_COMMAND] > 0)
            if not isIris and group == EB_KEY_COMMAND:
                self.keysDown[group] = arg
            else:
                del self.keysDown[group]

    def _sendPacket(self,
                    packetType: bytes,
                    packetSubType: bytes,
                    packetData: bytes = b""):
        packetSize = len(packetData) + 4
        packetBytes = bytearray(b"".join([
            STX,
            packetSize.to_bytes(2, "big", signed=False), packetType,
            packetSubType, packetData, ETX
        ]))
        if self.receivesAckPackets:
            with self._frameLock:
                frame = self._frame
                # Assumption: frame will only ever be 1 byte, otherwise consider byte order
                packetBytes.insert(-1, frame)
                self._awaitingFrameReceipts[frame] = packetBytes
                self._frame = frame + 1 if frame < 0x7F else 0x20
        packetData = bytes(packetBytes)
        if self.isHid:
            self._sendHidPacket(packetData)
        else:
            self._dev.write(packetData)

    def _sendHidPacket(self, packet: bytes):
        assert self.isHid
        blockSize = self._dev._writeSize - 1
        # When the packet length exceeds C{blockSize}, the packet is split up into several block packets.
        # These blocks are of size C{blockSize}.
        for offset in range(0, len(packet), blockSize):
            bytesToWrite = packet[offset:(offset + blockSize)]
            hidPacket = b"".join([
                b"\x00",
                bytesToWrite,
                b"\x55" * (blockSize - len(bytesToWrite))  # padding
            ])
            self._dev.write(hidPacket)

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        self._sendPacket(packetType=EB_BRAILLE_DISPLAY,
                         packetSubType=EB_BRAILLE_DISPLAY_STATIC,
                         packetData=bytes(cells))

    def _get_hidKeyboardInput(self):
        return self._hidKeyboardInput

    def _set_hidKeyboardInput(self, state: bool):
        self._sendPacket(packetType=EB_KEY,
                         packetSubType=EB_KEY_USB_HID_MODE,
                         packetData=EB_TRUE if state else EB_FALSE)
        for i in range(3):
            self._dev.waitForRead(self.timeout)
            if state is self._hidKeyboardInput:
                break

    scriptCategory = SCRCAT_BRAILLE

    def script_toggleHidKeyboardInput(self, gesture):
        def announceUnavailableMessage():
            # Translators: Message when HID keyboard simulation is unavailable.
            ui.message(_("HID keyboard input simulation is unavailable."))

        if not self.isHid:
            announceUnavailableMessage()
            return

        state = not self.hidKeyboardInput
        self.hidKeyboardInput = state

        if state is not self._hidKeyboardInput:
            announceUnavailableMessage()
        elif state:
            # Translators: Message when HID keyboard simulation is enabled.
            ui.message(_('HID keyboard simulation enabled'))
        else:
            # Translators: Message when HID keyboard simulation is disabled.
            ui.message(_('HID keyboard simulation disabled'))

    # Translators: Description of the script that toggles HID keyboard simulation.
    script_toggleHidKeyboardInput.__doc__ = _("Toggle HID keyboard simulation")

    __gestures = {
        "br(eurobraille.esytime):l1+joystick1Down": "toggleHidKeyboardInput",
        "br(eurobraille):switch1Left+joystick1Down": "toggleHidKeyboardInput",
        "br(eurobraille.esytime):l8+joystick1Down": "toggleHidKeyboardInput",
        "br(eurobraille):switch1Right+joystick1Down": "toggleHidKeyboardInput",
    }

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(eurobraille):routing", ),
            "braille_reportFormatting": ("br(eurobraille):doubleRouting", ),
            "braille_scrollBack": (
                "br(eurobraille):switch1Left",
                "br(eurobraille):l1",
            ),
            "braille_scrollForward": (
                "br(eurobraille):switch1Right",
                "br(eurobraille):l8",
            ),
            "braille_toFocus": (
                "br(eurobraille):switch1Left+switch1Right",
                "br(eurobraille):switch2Left+switch2Right",
                "br(eurobraille):switch3Left+switch3Right",
                "br(eurobraille):switch4Left+switch4Right",
                "br(eurobraille):switch5Left+switch5Right",
                "br(eurobraille):switch6Left+switch6Right",
                "br(eurobraille):l1+l8",
            ),
            "review_previousLine": ("br(eurobraille):joystick1Up", ),
            "review_nextLine": ("br(eurobraille):joystick1Down", ),
            "review_previousCharacter": ("br(eurobraille):joystick1Left", ),
            "review_nextCharacter": ("br(eurobraille):joystick1Right", ),
            "reviewMode_previous":
            ("br(eurobraille):joystick1Left+joystick1Up", ),
            "reviewMode_next":
            ("br(eurobraille):joystick1Right+joystick1Down", ),
            # Esys and esytime have a dedicated key for backspace and combines backspace and space to perform a return.
            "braille_eraseLastCell": ("br(eurobraille):backSpace", ),
            "braille_enter": ("br(eurobraille):backSpace+space", ),
            "kb:insert": (
                "br(eurobraille):dot3+dot5+space",
                "br(eurobraille):l7",
            ),
            "kb:delete": ("br(eurobraille):dot3+dot6+space", ),
            "kb:home": (
                "br(eurobraille):dot1+dot2+dot3+space",
                "br(eurobraille):joystick2Left+joystick2Up",
            ),
            "kb:end": (
                "br(eurobraille):dot4+dot5+dot6+space",
                "br(eurobraille):joystick2Right+joystick2Down",
            ),
            "kb:leftArrow": (
                "br(eurobraille):dot2+space",
                "br(eurobraille):joystick2Left",
                "br(eurobraille):leftArrow",
            ),
            "kb:rightArrow": (
                "br(eurobraille):dot5+space",
                "br(eurobraille):joystick2Right",
                "br(eurobraille):rightArrow",
            ),
            "kb:upArrow": (
                "br(eurobraille):dot1+space",
                "br(eurobraille):joystick2Up",
                "br(eurobraille):upArrow",
            ),
            "kb:downArrow": (
                "br(eurobraille):dot6+space",
                "br(eurobraille):joystick2Down",
                "br(eurobraille):downArrow",
            ),
            "kb:enter": ("br(eurobraille):joystick2Center", ),
            "kb:pageUp": ("br(eurobraille):dot1+dot3+space", ),
            "kb:pageDown": ("br(eurobraille):dot4+dot6+space", ),
            "kb:numpad1": ("br(eurobraille):dot1+dot6+backspace", ),
            "kb:numpad2": ("br(eurobraille):dot1+dot2+dot6+backspace", ),
            "kb:numpad3": ("br(eurobraille):dot1+dot4+dot6+backspace", ),
            "kb:numpad4": ("br(eurobraille):dot1+dot4+dot5+dot6+backspace", ),
            "kb:numpad5": ("br(eurobraille):dot1+dot5+dot6+backspace", ),
            "kb:numpad6": ("br(eurobraille):dot1+dot2+dot4+dot6+backspace", ),
            "kb:numpad7":
            ("br(eurobraille):dot1+dot2+dot4+dot5+dot6+backspace", ),
            "kb:numpad8": ("br(eurobraille):dot1+dot2+dot5+dot6+backspace", ),
            "kb:numpad9": ("br(eurobraille):dot2+dot4+dot6+backspace", ),
            "kb:numpadInsert":
            ("br(eurobraille):dot3+dot4+dot5+dot6+backspace", ),
            "kb:numpadDecimal": ("br(eurobraille):dot2+backspace", ),
            "kb:numpadDivide": ("br(eurobraille):dot3+dot4+backspace", ),
            "kb:numpadMultiply": ("br(eurobraille):dot3+dot5+backspace", ),
            "kb:numpadMinus": ("br(eurobraille):dot3+dot6+backspace", ),
            "kb:numpadPlus": ("br(eurobraille):dot2+dot3+dot5+backspace", ),
            "kb:numpadEnter": ("br(eurobraille):dot3+dot4+dot5+backspace", ),
            "kb:escape": (
                "br(eurobraille):dot1+dot2+dot4+dot5+space",
                "br(eurobraille):l2",
            ),
            "kb:tab": (
                "br(eurobraille):dot2+dot5+dot6+space",
                "br(eurobraille):l3",
            ),
            "kb:shift+tab": ("br(eurobraille):dot2+dot3+dot5+space", ),
            "kb:printScreen": ("br(eurobraille):dot1+dot3+dot4+dot6+space", ),
            "kb:pause": ("br(eurobraille):dot1+dot4+space", ),
            "kb:applications": ("br(eurobraille):dot5+dot6+backspace", ),
            "kb:f1": ("br(eurobraille):dot1+backspace", ),
            "kb:f2": ("br(eurobraille):dot1+dot2+backspace", ),
            "kb:f3": ("br(eurobraille):dot1+dot4+backspace", ),
            "kb:f4": ("br(eurobraille):dot1+dot4+dot5+backspace", ),
            "kb:f5": ("br(eurobraille):dot1+dot5+backspace", ),
            "kb:f6": ("br(eurobraille):dot1+dot2+dot4+backspace", ),
            "kb:f7": ("br(eurobraille):dot1+dot2+dot4+dot5+backspace", ),
            "kb:f8": ("br(eurobraille):dot1+dot2+dot5+backspace", ),
            "kb:f9": ("br(eurobraille):dot2+dot4+backspace", ),
            "kb:f10": ("br(eurobraille):dot2+dot4+dot5+backspace", ),
            "kb:f11": ("br(eurobraille):dot1+dot3+backspace", ),
            "kb:f12": ("br(eurobraille):dot1+dot2+dot3+backspace", ),
            "kb:windows": ("br(eurobraille):dot1+dot2+dot3+dot4+backspace", ),
            "kb:capsLock": (
                "br(eurobraille):dot7+backspace",
                "br(eurobraille):dot8+backspace",
            ),
            "kb:numLock": (
                "br(eurobraille):dot3+backspace",
                "br(eurobraille):dot6+backspace",
            ),
            "kb:shift": (
                "br(eurobraille):dot7+space",
                "br(eurobraille):l4",
            ),
            "braille_toggleShift": (
                "br(eurobraille):dot1+dot7+space",
                "br(eurobraille):dot4+dot7+space",
            ),
            "kb:control": (
                "br(eurobraille):dot7+dot8+space",
                "br(eurobraille):l5",
            ),
            "braille_toggleControl": (
                "br(eurobraille):dot1+dot7+dot8+space",
                "br(eurobraille):dot4+dot7+dot8+space",
            ),
            "kb:alt": (
                "br(eurobraille):dot8+space",
                "br(eurobraille):l6",
            ),
            "braille_toggleAlt": (
                "br(eurobraille):dot1+dot8+space",
                "br(eurobraille):dot4+dot8+space",
            ),
        },
    })
Пример #11
0
Файл: kgs.py Проект: ma10/nvdajp
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "kgs"
    description = _(u"KGS BrailleMemo series")
    _portName = None
    _directBM = None

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        global fConnection, numCells
        if not lock():
            return
        if port != self._portName and self._portName:
            execEndConnection = True
            log.info("changing connection %s to %s" % (self._portName, port))
        elif fConnection:
            log.info("already connection %s" % port)
            execEndConnection = False
            self.numCells = numCells
            unlock()
            return
        else:
            log.info("first connection %s" % port)
            execEndConnection = False
            self.numCells = 0
        kgs_dll = os.path.join(kgs_dir, 'DirectBM.dll')
        self._directBM = windll.LoadLibrary(kgs_dll.encode('mbcs'))
        if not self._directBM:
            unlock()
            raise RuntimeError("No KGS instance found")
        self._keyCallbackInst = KGS_PKEYCALLBACK(nvdaKgsHandleKeyInfoProc)
        self._statusCallbackInst = KGS_PSTATUSCALLBACK(
            nvdaKgsStatusChangedProc)
        ret, self._portName = bmConnect(self._directBM, port,
                                        self._keyCallbackInst,
                                        self._statusCallbackInst,
                                        execEndConnection)
        if ret:
            self.numCells = numCells
            log.info("connected %s" % port)
        else:
            self.numCells = 0
            log.info("failed %s" % port)
            unlock()
            raise RuntimeError("No KGS display found")
        unlock()

    def terminate(self):
        if not lock():
            return
        log.info("KGS driver terminating")
        super(BrailleDisplayDriver, self).terminate()
        if self._directBM and self._directBM._handle:
            bmDisConnect(self._directBM, self._portName)
            waitAfterDisconnect()
            ret = windll.kernel32.FreeLibrary(self._directBM._handle)
            # ret is not zero if success
            log.info("KGS driver terminated %d" % ret)
        self._directBM = None
        self._portName = None
        self._keyCallbackInst = None
        self._statusCallbackInst = None
        unlock()

    @classmethod
    def check(cls):
        return True

    @classmethod
    def getPossiblePorts(cls):
        ar = [cls.AUTOMATIC_PORT]
        ports = {}
        for p in kgsListComPorts():
            log.info(p)
            ports[p["port"]] = p["friendlyName"]
        log.info(ports)
        for i in xrange(64):
            p = "COM%d" % (i + 1)
            if p in ports:
                fname = ports[p]
                ar.append((p, fname))
        return OrderedDict(ar)

    def display(self, data):
        if not data: return
        s = ''
        for c in data:
            d = 0
            if c & 0x01: d += 0x80
            if c & 0x02: d += 0x40
            if c & 0x04: d += 0x20
            if c & 0x08: d += 0x08
            if c & 0x10: d += 0x04
            if c & 0x20: d += 0x02
            if c & 0x40: d += 0x10
            if c & 0x80: d += 0x01
            s += chr(d)
        dataBuf = create_string_buffer(s, 256)
        cursorBuf = create_string_buffer('', 256)
        try:
            ret = self._directBM.bmDisplayData(dataBuf, cursorBuf,
                                               self.numCells)
            log.debug("bmDisplayData %d" % ret)
        except:
            log.debug("error bmDisplayData")

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "showGui": ("br(kgs):ins", ),
            "kb:escape": ("br(kgs):esc", ),
            "kb:windows": ("br(kgs):read", ),
            "kb:shift": ("br(kgs):select", ),
            "kb:control": ("br(kgs):ctrl", ),
            "kb:alt": ("br(kgs):alt", ),
            "kb:alt+tab": ("br(kgs):alt+inf", ),
            "kb:enter": (
                "br(kgs):enter",
                "br(kgs):ok",
                "br(kgs):set",
            ),
            "kb:space": ("br(kgs):space", ),
            "kb:delete": ("br(kgs):del", ),
            "kb:backspace": ("br(kgs):bs", ),
            "kb:tab": ("br(kgs):inf", ),
            "kb:shift+tab": ("br(kgs):select+inf", ),
            "kb:upArrow": ("br(kgs):upArrow", ),
            "kb:downArrow": ("br(kgs):downArrow", ),
            "kb:leftArrow": ("br(kgs):leftArrow", ),
            "kb:rightArrow": ("br(kgs):rightArrow", ),
            "kb:shift+upArrow": ("br(kgs):select+upArrow", ),
            "kb:shift+downArrow": ("br(kgs):select+downArrow", ),
            "kb:shift+leftArrow": ("br(kgs):select+leftArrow", ),
            "kb:shift+rightArrow": ("br(kgs):select+rightArrow", ),
            "review_previousLine": ("br(kgs):bw", ),
            "review_nextLine": ("br(kgs):fw", ),
            "review_previousWord": ("br(kgs):ls", ),
            "review_nextWord": ("br(kgs):rs", ),
            "braille_routeTo": ("br(kgs):route", ),
            "braille_scrollBack": (
                "br(kgs):func1",
                "br(kgs):func3+leftArrow",
            ),
            "braille_scrollForward": (
                "br(kgs):func4",
                "br(kgs):func3+rightArrow",
            ),
            "braille_previousLine": ("br(kgs):func3+upArrow", ),
            "braille_nextLine": ("br(kgs):func3+downArrow", ),
            "kb:a": ("br(kgs):dot1", ),
            "kb:b": ("br(kgs):dot1+dot2", ),
            "kb:c": ("br(kgs):dot1+dot4", ),
            "kb:d": ("br(kgs):dot1+dot4+dot5", ),
            "kb:e": ("br(kgs):dot1+dot5", ),
            "kb:f": ("br(kgs):dot1+dot2+dot4", ),
            "kb:g": ("br(kgs):dot1+dot2+dot4+dot5", ),
            "kb:h": ("br(kgs):dot1+dot2+dot5", ),
            "kb:i": ("br(kgs):dot2+dot4", ),
            "kb:j": ("br(kgs):dot2+dot4+dot5", ),
            "kb:k": ("br(kgs):dot1+dot3", ),
            "kb:l": ("br(kgs):dot1+dot2+dot3", ),
            "kb:m": ("br(kgs):dot1+dot3+dot4", ),
            "kb:n": ("br(kgs):dot1+dot3+dot4+dot5", ),
            "kb:o": ("br(kgs):dot1+dot3+dot5", ),
            "kb:p": ("br(kgs):dot1+dot2+dot3+dot4", ),
            "kb:q": ("br(kgs):dot1+dot2+dot3+dot4+dot5", ),
            "kb:r": ("br(kgs):dot1+dot2+dot3+dot5", ),
            "kb:s": ("br(kgs):dot2+dot3+dot4", ),
            "kb:t": ("br(kgs):dot2+dot3+dot4+dot5", ),
            "kb:u": ("br(kgs):dot1+dot3+dot6", ),
            "kb:v": ("br(kgs):dot1+dot2+dot3+dot6", ),
            "kb:w": ("br(kgs):dot2+dot4+dot5+dot6", ),
            "kb:x": ("br(kgs):dot1+dot3+dot4+dot6", ),
            "kb:y": ("br(kgs):dot1+dot3+dot4+dot5+dot6", ),
            "kb:z": ("br(kgs):dot1+dot3+dot5+dot6", ),
            "kb:control+a": ("br(kgs):ctrl+dot1", ),
            "kb:control+b": ("br(kgs):ctrl+dot1+dot2", ),
            "kb:control+c": ("br(kgs):ctrl+dot1+dot4", ),
            "kb:control+d": ("br(kgs):ctrl+dot1+dot4+dot5", ),
            "kb:control+e": ("br(kgs):ctrl+dot1+dot5", ),
            "kb:control+f": ("br(kgs):ctrl+dot1+dot2+dot4", ),
            "kb:control+g": ("br(kgs):ctrl+dot1+dot2+dot4+dot5", ),
            "kb:control+h": ("br(kgs):ctrl+dot1+dot2+dot5", ),
            "kb:control+i": ("br(kgs):ctrl+dot2+dot4", ),
            "kb:control+j": ("br(kgs):ctrl+dot2+dot4+dot5", ),
            "kb:control+k": ("br(kgs):ctrl+dot1+dot3", ),
            "kb:control+l": ("br(kgs):ctrl+dot1+dot2+dot3", ),
            "kb:control+m": ("br(kgs):ctrl+dot1+dot3+dot4", ),
            "kb:control+n": ("br(kgs):ctrl+dot1+dot3+dot4+dot5", ),
            "kb:control+o": ("br(kgs):ctrl+dot1+dot3+dot5", ),
            "kb:control+p": ("br(kgs):ctrl+dot1+dot2+dot3+dot4", ),
            "kb:control+q": ("br(kgs):ctrl+dot1+dot2+dot3+dot4+dot5", ),
            "kb:control+r": ("br(kgs):ctrl+dot1+dot2+dot3+dot5", ),
            "kb:control+s": ("br(kgs):ctrl+dot2+dot3+dot4", ),
            "kb:control+t": ("br(kgs):ctrl+dot2+dot3+dot4+dot5", ),
            "kb:control+u": ("br(kgs):ctrl+dot1+dot3+dot6", ),
            "kb:control+v": ("br(kgs):ctrl+dot1+dot2+dot3+dot6", ),
            "kb:control+w": ("br(kgs):ctrl+dot2+dot4+dot5+dot6", ),
            "kb:control+x": ("br(kgs):ctrl+dot1+dot3+dot4+dot6", ),
            "kb:control+y": ("br(kgs):ctrl+dot1+dot3+dot4+dot5+dot6", ),
            "kb:control+z": ("br(kgs):ctrl+dot1+dot3+dot5+dot6", ),
            "kb:alt+a": ("br(kgs):alt+dot1", ),
            "kb:alt+b": ("br(kgs):alt+dot1+dot2", ),
            "kb:alt+c": ("br(kgs):alt+dot1+dot4", ),
            "kb:alt+d": ("br(kgs):alt+dot1+dot4+dot5", ),
            "kb:alt+e": ("br(kgs):alt+dot1+dot5", ),
            "kb:alt+f": ("br(kgs):alt+dot1+dot2+dot4", ),
            "kb:alt+g": ("br(kgs):alt+dot1+dot2+dot4+dot5", ),
            "kb:alt+h": ("br(kgs):alt+dot1+dot2+dot5", ),
            "kb:alt+i": ("br(kgs):alt+dot2+dot4", ),
            "kb:alt+j": ("br(kgs):alt+dot2+dot4+dot5", ),
            "kb:alt+k": ("br(kgs):alt+dot1+dot3", ),
            "kb:alt+l": ("br(kgs):alt+dot1+dot2+dot3", ),
            "kb:alt+m": ("br(kgs):alt+dot1+dot3+dot4", ),
            "kb:alt+n": ("br(kgs):alt+dot1+dot3+dot4+dot5", ),
            "kb:alt+o": ("br(kgs):alt+dot1+dot3+dot5", ),
            "kb:alt+p": ("br(kgs):alt+dot1+dot2+dot3+dot4", ),
            "kb:alt+q": ("br(kgs):alt+dot1+dot2+dot3+dot4+dot5", ),
            "kb:alt+r": ("br(kgs):alt+dot1+dot2+dot3+dot5", ),
            "kb:alt+s": ("br(kgs):alt+dot2+dot3+dot4", ),
            "kb:alt+t": ("br(kgs):alt+dot2+dot3+dot4+dot5", ),
            "kb:alt+u": ("br(kgs):alt+dot1+dot3+dot6", ),
            "kb:alt+v": ("br(kgs):alt+dot1+dot2+dot3+dot6", ),
            "kb:alt+w": ("br(kgs):alt+dot2+dot4+dot5+dot6", ),
            "kb:alt+x": ("br(kgs):alt+dot1+dot3+dot4+dot6", ),
            "kb:alt+y": ("br(kgs):alt+dot1+dot3+dot4+dot5+dot6", ),
            "kb:alt+z": ("br(kgs):alt+dot1+dot3+dot5+dot6", ),
            "kb:.": ("br(kgs):dot2+dot5+dot6", ),
            "kb::": ("br(kgs):dot2+dot5", ),
            "kb:;": ("br(kgs):dot2+dot3", ),
            "kb:,": ("br(kgs):dot2", ),
            "kb:-": ("br(kgs):dot3+dot6", ),
            "kb:?": ("br(kgs):dot2+dot3+dot6", ),
            "kb:!": ("br(kgs):dot2+dot3+dot5", ),
            "kb:'": ("br(kgs):dot3", ),
        }
    })
Пример #12
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
	name = "alvaBC6"
	# Translators: The name of a braille display.
	description = _("ALVA BC640/680 series")

	@classmethod
	def check(cls):
		return bool(AlvaLib)

	def __init__(self):
		super(BrailleDisplayDriver,self).__init__()
		log.debug("ALVA BC6xx Braille init")
		_AlvaNumDevices=c_int(0)
		AlvaLib.AlvaScanDevices(byref(_AlvaNumDevices))
		if _AlvaNumDevices.value==0:
			raise RuntimeError("No ALVA display found")
		log.debug("%d devices found" %_AlvaNumDevices.value)
		AlvaLib.AlvaOpen(0)
		self._alva_NumCells = 0
		self._keysDown = set()
		self._ignoreKeyReleases = False
		self._keyCallbackInst = ALVA_PKEYCALLBACK(self._keyCallback)
		AlvaLib.AlvaSetKeyCallback(0, self._keyCallbackInst, None)

	def terminate(self):
		super(BrailleDisplayDriver, self).terminate()
		AlvaLib.AlvaClose(1)
		# Drop the ctypes function instance for the key callback,
		# as it is holding a reference to an instance method, which causes a reference cycle.
		self._keyCallbackInst = None

	def _get_numCells(self):
		if self._alva_NumCells==0:
			NumCells = c_int(0)
			AlvaLib.AlvaGetCells(0, byref(NumCells))
			if NumCells.value==0:
				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

	def display(self, cells):
		cells="".join([chr(x) for x in cells])
		AlvaLib.AlvaSendBraille(0, cells, 0, len(cells))

	def _keyCallback(self, dev, key, userData):
		group = (key >> 8) & 0x7F
		number = key & 0xFF
		if key & ALVA_RELEASE_MASK:
			# Release.
			if not self._ignoreKeyReleases and self._keysDown:
				try:
					inputCore.manager.executeGesture(InputGesture(self._keysDown))
				except inputCore.NoInputGestureAction:
					pass
				# Any further releases are just the rest of the keys in the combination being released,
				# so they should be ignored.
				self._ignoreKeyReleases = True
			self._keysDown.discard((group, number))
		else:
			# Press.
			if group == ALVA_CR_GROUP:
				# Execute routing keys when pressed instead of released.
				try:
					inputCore.manager.executeGesture(InputGesture(((group, number),)))
				except inputCore.NoInputGestureAction:
					pass
			else:
				self._keysDown.add((group, number))
				# This begins a new key combination.
				self._ignoreKeyReleases = False
		return False

	gestureMap = inputCore.GlobalGestureMap({
		"globalCommands.GlobalCommands": {
			"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",),
			"kb:shift+tab": ("br(alvaBC6):sp1",),
			"kb:alt": ("br(alvaBC6):sp2",),
			"kb:escape": ("br(alvaBC6):sp3",),
			"kb:tab": ("br(alvaBC6):sp4",),
			"kb:upArrow": ("br(alvaBC6):spUp",),
			"kb:downArrow": ("br(alvaBC6):spDown",),
			"kb:leftArrow": ("br(alvaBC6):spLeft",),
			"kb:rightArrow": ("br(alvaBC6):spRight",),
			"kb:enter": ("br(alvaBC6):spEnter",),
			"showGui": ("br(alvaBC6):sp1+sp3",),
			"kb:windows+d": ("br(alvaBC6):sp1+sp4",),
			"kb:windows": ("br(alvaBC6):sp2+sp3",),
			"kb:alt+tab": ("br(alvaBC6):sp2+sp4",),
		}
	})
Пример #13
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
	"""Handy Tech braille display driver.
	"""
	name = "handyTech"
	# Translators: Names of braille displays.
	description = _("Handy Tech braille displays")

	@classmethod
	def check(cls):
		try:
			GUID.from_progid(COM_CLASS)
			return True
		except WindowsError:
			return False

	def __init__(self):
		global constants, HT_KEYS
		super(BrailleDisplayDriver, self).__init__()
		self._server = comtypes.client.CreateObject(COM_CLASS)
		import comtypes.gen.HTBRAILLEDRIVERSERVERLib as constants

		HT_KEYS = {}
		for key, constant in constants.__dict__.items():
			if key.startswith('KEY_'):
				HT_KEYS[constant] = key[4:].lower().replace('_', '')

		# Keep the connection object so it won't become garbage
		self._advise = comtypes.client.GetEvents(self._server, Sink(self._server), constants.IHtBrailleDriverSink)
		self._server.initialize()

	def terminate(self):
		super(BrailleDisplayDriver, self).terminate()
		self._server.terminate()

	def _get_numCells(self):
		return self._server.getCurrentTextLength()[0]

	def display(self, cells):
		self._server.displayText(cells)

	scriptCategory = SCRCAT_BRAILLE

	def script_showConfig(self, gesture):
		self._server.startConfigDialog(False)
	script_showConfig.__doc__ = _("Show the Handy Tech driver configuration window.")

	gestureMap = inputCore.GlobalGestureMap({
		"globalCommands.GlobalCommands": {
			"braille_scrollBack": ("br(handytech):left", "br(handytech):up"),
			"braille_previousLine": ("br(handytech):b4",),
			"braille_nextLine": ("br(handytech):b5",),
			"braille_scrollForward": ("br(handytech):right", "br(handytech):down"),
			"braille_routeTo": ("br(handytech):routing",),
			"kb:shift+tab": ("br(handytech):esc",),
			"kb:alt": ("br(handytech):b2+b4+b5",),
			"kb:escape": ("br(handytech):b4+b6",),
			"kb:tab": ("br(handytech):enter",),
			"kb:enter": ("br(handytech):esc+enter",),
			"kb:upArrow": ("br(handytech):leftSpace",),
			"kb:downArrow": ("br(handytech):rightSpace",),
			"showGui": ("br(handytech):b2+b4+b5+b6",),
		}
	})

	__gestures = {
		'br(handytech):b4+b8': 'showConfig',
	}
Пример #14
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "superBrl"
    # Translators: Names of braille displays.
    description = _("SuperBraille")
    isThreadSafe = True

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts()

    def __init__(self, port="Auto"):
        super(BrailleDisplayDriver, self).__init__()
        for portType, portId, port, portInfo in self._getTryPorts(port):
            try:
                self._dev = hwIo.Serial(port,
                                        baudrate=BAUD_RATE,
                                        stopbits=serial.STOPBITS_ONE,
                                        parity=serial.PARITY_NONE,
                                        timeout=TIMEOUT,
                                        writeTimeout=TIMEOUT,
                                        onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue

            # try to initialize the device and request number of cells
            self._dev.write(DESCRIBE_TAG)
            self._dev.waitForRead(TIMEOUT)
            # Check for cell information
            if self.numCells:
                # ok, it is a SuperBraille
                log.info("Found superBraille device, version %s" %
                         self.version)
                break
            else:
                self._dev.close()
        else:
            raise RuntimeError("No SuperBraille found")

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # We must sleep before closing the COM port as not doing this can leave the display in a bad state where it can not be re-initialized
            time.sleep(TIMEOUT)
            self._dev.close()
            self._dev = None

    def _onReceive(self, data):
        # The only info this display ever sends is number of cells and the display version.
        # It sends 0x00, 0x05, number of cells,  then version string of 8 bytes.
        if data != '\x00':
            return
        data = self._dev.read(1)
        if data != '\x05':
            return
        self.numCells = ord(self._dev.read(1))
        self._dev.read(1)
        self.version = self._dev.read(8)

    def display(self, cells):
        out = []
        for cell in cells:
            out.append("\x00")
            out.append(chr(cell))
        self._dev.write(DISPLAY_TAG + "".join(out))

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("kb:numpadMinus", ),
            "braille_scrollForward": ("kb:numpadPlus", ),
        },
    })
Пример #15
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "hedoProfiLine"
    description = "hedo ProfiLine USB"

    numCells = HEDO_CELL_COUNT

    @classmethod
    def check(cls):
        return True

    def __init__(self):
        super(BrailleDisplayDriver, self).__init__()

        for portInfo in hwPortUtils.listComPorts(onlyAvailable=True):
            port = portInfo["port"]
            hwID = portInfo["hardwareID"]
            #log.info("Found port {port} with hardwareID {hwID}".format(port=port, hwID=hwID))
            if not hwID.startswith(r"FTDIBUS\COMPORT"):
                continue
            try:
                usbID = hwID.split("&", 1)[1]
            except IndexError:
                continue
            if usbID not in HEDO_USB_IDS:
                continue
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            try:
                self._ser = serial.Serial(port,
                                          baudrate=HEDO_BAUDRATE,
                                          timeout=HEDO_TIMEOUT,
                                          writeTimeout=HEDO_TIMEOUT,
                                          parity=serial.PARITY_ODD,
                                          bytesize=serial.EIGHTBITS,
                                          stopbits=serial.STOPBITS_ONE)
            except serial.SerialException:
                continue

            # Prepare a blank line
            totalCells: int = HEDO_CELL_COUNT + HEDO_CELL_COUNT
            cells: bytes = HEDO_INIT + bytes(totalCells)

            # Send the blank line twice
            self._ser.write(cells)
            self._ser.flush()
            self._ser.write(cells)
            self._ser.flush()

            # Read out the input buffer
            ackS: bytes = self._ser.read(2)
            if HEDO_ACK in ackS:
                log.info("Found hedo ProfiLine connected via {port}".format(
                    port=port))
                break

        else:
            raise RuntimeError("No hedo display found")

        self._readTimer = wx.PyTimer(self.handleResponses)
        self._readTimer.Start(HEDO_READ_INTERVAL)

        self._keysDown = set()
        self._ignoreKeyReleases = False

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
            self._readTimer.Stop()
            self._readTimer = None
        finally:
            # We absolutely must close the Serial object, as it does not have a destructor.
            # If we don't, we won't be able to re-open it later.
            self._ser.close()

    def display(self, cells: List[int]):
        # every transmitted line consists of the preamble HEDO_INIT, the statusCells and the Cells

        # add padding so total length is 1 + numberOfStatusCells + numberOfRegularCells
        cellPadding: bytes = bytes(HEDO_CELL_COUNT - len(cells))
        statusPadding: bytes = bytes(HEDO_STATUS_CELL_COUNT)
        cellBytes: bytes = HEDO_INIT + statusPadding + bytes(
            cells) + cellPadding

        self._ser.write(cellBytes)

    def handleResponses(self, wait=False):
        while wait or self._ser.in_waiting:
            data: bytes = self._ser.read(1)
            if data:
                # do not handle acknowledge bytes
                if data != HEDO_ACK:
                    self.handleData(ord(data))
            wait = False

    def handleData(self, data: int):

        if HEDO_CR_BEGIN <= data <= HEDO_CR_END:
            # Routing key is pressed
            try:
                inputCore.manager.executeGesture(
                    InputGestureRouting(data - HEDO_CR_BEGIN))
            except inputCore.NoInputGestureAction:
                log.debug("No Action for routing command: %d", data)
                pass

        elif (HEDO_CR_BEGIN + HEDO_RELEASE_OFFSET) <= data <= (
                HEDO_CR_END + HEDO_RELEASE_OFFSET):
            # Routing key is released
            return

        elif data in HEDO_KEYMAP:
            # A key is pressed
            # log.debug("Key " + HEDO_KEYMAP[data] + " pressed")
            self._keysDown.add(HEDO_KEYMAP[data])
            self._ignoreKeyReleases = False

        elif data > HEDO_RELEASE_OFFSET and (
                data - HEDO_RELEASE_OFFSET) in HEDO_KEYMAP:
            # A key is released
            # log.debug("Key " + str(self._keysDown) + " released")
            if not self._ignoreKeyReleases:
                keys = "+".join(self._keysDown)
                self._ignoreKeyReleases = True
                self._keysDown = set()
                try:
                    inputCore.manager.executeGesture(InputGestureKeys(keys))
                except inputCore.NoInputGestureAction:
                    log.debug("No Action for keys {keys}".format(keys=keys))
                    pass

        # else:
        #	log.debug("Key " + hex(data) + " not identified")

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(hedoProfiLine):K1", ),
            "braille_toggleTether": ("br(hedoProfiLine):K2", ),
            "braille_scrollForward": ("br(hedoProfiLine):K3", ),
            "braille_previousLine": ("br(hedoProfiLine):B2", ),
            "braille_nextLine": ("br(hedoProfiLine):B5", ),
            "sayAll": ("br(hedoProfiLine):B6", ),
            "braille_routeTo": ("br(hedoProfiLine):routing", ),
        },
    })
Пример #16
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "brailliantB"
    # Translators: The name of a series of braille displays.
    description = _("HumanWare Brailliant BI/B series / BrailleNote Touch")
    isThreadSafe = True

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts()

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0

        for portType, portId, port, portInfo in self._getTryPorts(port):
            self.isHid = portType == bdDetect.KEY_HID
            # Try talking to the display.
            try:
                if self.isHid:
                    self._dev = hwIo.Hid(port, onReceive=self._hidOnReceive)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            parity=PARITY,
                                            timeout=TIMEOUT,
                                            writeTimeout=TIMEOUT,
                                            onReceive=self._serOnReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue  # Couldn't connect.
            # The Brailliant can fail to init if you try immediately after connecting.
            time.sleep(DELAY_AFTER_CONNECT)
            # Sometimes, a few attempts are needed to init successfully.
            for attempt in xrange(INIT_ATTEMPTS):
                if attempt > 0:  # Not the first attempt
                    time.sleep(INIT_RETRY_DELAY)  # Delay before next attempt.
                self._initAttempt()
                if self.numCells:
                    break  # Success!
            if self.numCells:
                # A display responded.
                log.info(
                    "Found display with {cells} cells connected via {type} ({port})"
                    .format(cells=self.numCells, type=portType, port=port))
                break
            # This device can't be initialized. Move on to the next (if any).
            self._dev.close()

        else:
            raise RuntimeError("No display found")

        self._keysDown = set()
        self._ignoreKeyReleases = False

    def _initAttempt(self):
        if self.isHid:
            try:
                data = self._dev.getFeature(HR_CAPS)
            except WindowsError:
                return  # Fail!
            self.numCells = ord(data[24])
        else:
            # This will cause the display to return the number of cells.
            # The _serOnReceive callback will see this and set self.numCells.
            self._serSendMessage(MSG_INIT)
            self._dev.waitForRead(TIMEOUT)

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    def _serSendMessage(self, msgId, payload=""):
        if isinstance(payload, (int, bool)):
            payload = chr(payload)
        self._dev.write(
            "{header}{id}{length}{payload}".format(header=HEADER,
                                                   id=msgId,
                                                   length=chr(len(payload)),
                                                   payload=payload))

    def _serOnReceive(self, data):
        if data != HEADER:
            log.debugWarning("Ignoring byte before header: %r" % data)
            return
        msgId = self._dev.read(1)
        length = ord(self._dev.read(1))
        payload = self._dev.read(length)
        self._serHandleResponse(msgId, payload)

    def _serHandleResponse(self, msgId, payload):
        if msgId == MSG_INIT_RESP:
            if ord(payload[0]) != 0:
                # Communication not allowed.
                log.debugWarning(
                    "Display at %r reports communication not allowed" %
                    self._dev.port)
                return
            self.numCells = ord(payload[2])

        elif msgId == MSG_KEY_DOWN:
            payload = ord(payload)
            self._keysDown.add(payload)
            # This begins a new key combination.
            self._ignoreKeyReleases = False

        elif msgId == MSG_KEY_UP:
            payload = ord(payload)
            self._handleKeyRelease()
            self._keysDown.discard(payload)

        else:
            log.debugWarning(
                "Unknown message: id {id!r}, payload {payload!r}".format(
                    id=msgId, payload=payload))

    def _hidOnReceive(self, data):
        rId = data[0]
        if rId == HR_KEYS:
            keys = data[1:].split("\0", 1)[0]
            keys = {ord(key) for key in keys}
            if len(keys) > len(self._keysDown):
                # Press. This begins a new key combination.
                self._ignoreKeyReleases = False
            elif len(keys) < len(self._keysDown):
                self._handleKeyRelease()
            self._keysDown = keys

        elif rId == HR_POWEROFF:
            log.debug("Powering off")
        else:
            log.debugWarning("Unknown report: %r" % data)

    def _handleKeyRelease(self):
        if self._ignoreKeyReleases or not self._keysDown:
            return
        try:
            inputCore.manager.executeGesture(InputGesture(self._keysDown))
        except inputCore.NoInputGestureAction:
            pass
        # Any further releases are just the rest of the keys in the combination being released,
        # so they should be ignored.
        self._ignoreKeyReleases = True

    def display(self, cells):
        # cells will already be padded up to numCells.
        cells = "".join(chr(cell) for cell in cells)
        if self.isHid:
            outputReport = (
                "{id}"
                "\x01\x00"  # Module 1, offset 0
                "{length}{cells}".format(id=HR_BRAILLE,
                                         length=chr(self.numCells),
                                         cells=cells))
            #: Humanware HID devices require the use of HidD_SetOutputReport when sending data to the device via HID, as WriteFile seems to block forever or fail to reach the device at all.
            self._dev.setOutputReport(outputReport)
        else:
            self._serSendMessage(MSG_DISPLAY, cells)

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(brailliantB):left", ),
            "braille_scrollForward": ("br(brailliantB):right", ),
            "braille_previousLine": ("br(brailliantB):up", ),
            "braille_nextLine": ("br(brailliantB):down", ),
            "braille_routeTo": ("br(brailliantB):routing", ),
            "braille_toggleTether": ("br(brailliantB):up+down", ),
            "kb:upArrow":
            ("br(brailliantB):space+dot1", "br(brailliantB):stickUp"),
            "kb:downArrow":
            ("br(brailliantB):space+dot4", "br(brailliantB):stickDown"),
            "kb:leftArrow":
            ("br(brailliantB):space+dot3", "br(brailliantB):stickLeft"),
            "kb:rightArrow":
            ("br(brailliantB):space+dot6", "br(brailliantB):stickRight"),
            "showGui": (
                "br(brailliantB):c1+c3+c4+c5",
                "br(brailliantB):space+dot1+dot3+dot4+dot5",
            ),
            "kb:shift+tab": ("br(brailliantB):space+dot1+dot3", ),
            "kb:tab": ("br(brailliantB):space+dot4+dot6", ),
            "kb:alt": ("br(brailliantB):space+dot1+dot3+dot4", ),
            "kb:escape": ("br(brailliantB):space+dot1+dot5", ),
            "kb:enter": ("br(brailliantB):stickAction"),
            "kb:windows+d": (
                "br(brailliantB):c1+c4+c5",
                "br(brailliantB):Space+dot1+dot4+dot5",
            ),
            "kb:windows": ("br(brailliantB):space+dot3+dot4", ),
            "kb:alt+tab": ("br(brailliantB):space+dot2+dot3+dot4+dot5", ),
            "sayAll": (
                "br(brailliantB):c1+c2+c3+c4+c5+c6",
                "br(brailliantB):Space+dot1+dot2+dot3+dot4+dot5+dot6",
            ),
        },
    })
Пример #17
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    """papenmeier braille display driver.
	"""
    _dev: serial.Serial
    name = "papenmeier"
    # Translators: Names of braille displays.
    description = _("Papenmeier BRAILLEX newer models")

    @classmethod
    def check(cls):
        """should return false if there is a missing dependency"""
        return True

    def connectBluetooth(self):
        """try to connect to bluetooth device first, bluetooth is only supported on Braillex Trio"""
        if (self._baud == 0 and self._dev is None):
            for portInfo in sorted(
                    hwPortUtils.listComPorts(onlyAvailable=True),
                    key=lambda item: "bluetoothName" in item):
                port = portInfo["port"]
                hwID = portInfo["hardwareID"]
                if "bluetoothName" in portInfo:
                    if portInfo["bluetoothName"][
                            0:14] == "braillex trio " or portInfo[
                                "bluetoothName"][0:13] == "braillex live":
                        try:
                            self._dev = serial.Serial(
                                port,
                                baudrate=57600,
                                timeout=BLUETOOTH_TIMEOUT,
                                writeTimeout=BLUETOOTH_TIMEOUT)
                            log.info("connectBluetooth success")
                        except:
                            log.debugWarning("connectBluetooth failed")

    def connectUSB(self, devlist: List[bytes]):
        """Try to connect to usb device, this is triggered when bluetooth
connection could not be established"""
        try:
            self._dev = ftdi2.open_ex(devlist[0])
            self._dev.set_baud_rate(self._baud)
            self._dev.inWaiting = self._dev.get_queue_status
            log.info("connectUSB success")
        except:
            log.debugWarning("connectUSB failed")

    def __init__(self):
        """initialize driver"""
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._nlk = 0
        self._nrk = 0
        self.decodedkeys = []
        self._baud = 0
        self._dev = None
        self._proto = None
        devlist: List[bytes] = []

        #try to connect to usb device,
        #if no usb device is found there may be a bluetooth device
        if ftdi2:
            devlist = ftdi2.list_devices()
        if (len(devlist) == 0):
            self.connectBluetooth()
        elif ftdi2:
            self._baud = 57600
            self.connectUSB(devlist)
        if (self._dev is not None):
            try:
                #request type of braille display
                self._dev.write(brl_auto_id())
                time.sleep(
                    0.05
                )  # wait 50 ms in order to get response for further actions
                autoid: bytes = brl_poll(self._dev)
                if autoid == b'':
                    #no response, assume a Trio is connected
                    self._baud = 115200
                    self._dev.set_baud_rate(self._baud)
                    self._dev.purge()
                    self._dev.read(self._dev.inWaiting())
                    #request type of braille display twice because of baudrate change
                    self._dev.write(brl_auto_id())
                    self._dev.write(brl_auto_id())
                    time.sleep(
                        0.05
                    )  # wait 50 ms in order to get response for further actions
                    autoid = brl_poll(self._dev)
                if len(autoid) != 8:
                    return
                else:
                    if (autoid[3] == 0x35 and autoid[4] == 0x38):  #EL80s
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL80s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3A):  #EL70s
                        self.numCells = 70
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL70s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x35):  #EL40s
                        self.numCells = 40
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL40s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x37):  #EL66s
                        self.numCells = 66
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL66s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3E):  #EL20c
                        self.numCells = 20
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL20c")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3F):  #EL40c
                        self.numCells = 40
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL40c")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x30):  #EL60c
                        self.numCells = 60
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL60c")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x31):  #EL80c
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL80c")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3b):  #EL2D80s
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 20
                        log.info("Found EL2D80s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x39):  #trio
                        self.numCells = 40
                        self._proto = 'B'
                        self._voffset = 0
                        log.info("Found trio")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x34):  #live20
                        self.numCells = 20
                        self._proto = 'B'
                        self._voffset = 0
                        log.info("Found live 20")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x33):  #live+
                        self.numCells = 40
                        self._proto = 'B'
                        self._voffset = 0
                        log.info("Found live+")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x32):  #live
                        self.numCells = 40
                        self._proto = 'B'
                        self._voffset = 0
                        log.info("Found live")
                    else:
                        log.debugWarning('UNKNOWN BRAILLE')

            except:
                log.debugWarning('BROKEN PIPE - THIS SHOULD NEVER HAPPEN')
        if (self.numCells == 0): raise Exception('no device found')

        #start keycheck timer
        self.startTimer()
        self.initmapping()

    def startTimer(self):
        """start timers used by this driver"""
        self._keyCheckTimer = wx.PyTimer(self._handleKeyPresses)
        self._keyCheckTimer.Start(KEY_CHECK_INTERVAL)
        #the keycheck timer polls the braille display for keypresses
        self._bluetoothTimer = wx.PyTimer(self.connectBluetooth)
        self._bluetoothTimer.Start(BLUETOOTH_INTERVAL)
        #the bluetooth timer tries to reconnect if the bluetooth connection is lost

    def stopTimer(self):
        """stop all timers"""
        try:
            self._keyCheckTimer.Stop()
            self._bluetoothTimer.Stop()
        except:
            pass

        self._keyCheckTimer = None
        self._bluetoothTimer = None

    def initmapping(self):
        if (self._proto == 'A'):
            self._keynamesrepeat = {
                20: 'up2',
                21: 'up',
                22: 'dn',
                23: 'dn2',
                24: 'right',
                25: 'left',
                26: 'right2',
                27: 'left2'
            }
            x = self.numCells * 2 + 4
            self._keynames = {
                x + 28: 'r1',
                x + 29: 'r2',
                20: 'up2',
                21: 'up',
                22: 'dn',
                23: 'dn2',
                24: 'right',
                25: 'left',
                26: 'right2',
                27: 'left2',
                28: 'l1',
                29: 'l2'
            }
        else:
            self._keynamesrepeat = {
                16: 'left2',
                17: 'right2',
                18: 'left',
                19: 'right',
                20: 'dn2',
                21: 'dn',
                22: 'up',
                23: 'up2'
            }
            x = self.numCells * 2
            self._keynames = {
                16: 'left2',
                17: 'right2',
                18: 'left',
                19: 'right',
                20: 'dn2',
                21: 'dn',
                22: 'up',
                23: 'up2',
                x + 38: 'r2',
                x + 39: 'r1',
                30: 'l2',
                31: 'l1'
            }
            self._dotNames = {
                32: 'd6',
                1: 'd1',
                2: 'd2',
                4: 'd3',
                8: 'd4',
                64: 'd7',
                128: 'd8',
                16: 'd5'
            }
            self._thumbs = {1: "rt", 2: "space", 4: "lt"}

    def terminate(self):
        """free resources used by this driver"""
        try:
            super(BrailleDisplayDriver, self).terminate()
            self.stopTimer()
            if (self._dev is not None): self._dev.close()
            self._dev = None
        except:
            self._dev = None

    def display(self, cells: List[int]):
        """write to braille display"""
        if (self._dev is None): return
        try:
            self._dev.write(brl_out(cells, self._nlk, self._nrk,
                                    self._voffset))
        except:
            self._dev.close()
            self._dev = None

    def executeGesture(self, gesture):
        """executes a gesture"""
        if gesture.id or (gesture.dots or gesture.space):
            inputCore.manager.executeGesture(gesture)

    def _handleKeyPresses(self):
        """handles key presses and performs a gesture"""
        try:
            if (self._dev is None and self._baud > 0):
                try:
                    devlist: List[bytes] = ftdi2.list_devices()
                    if (len(devlist) > 0):
                        self.connectUSB(devlist)
                except:
                    return
            s: bytes = brl_poll(self._dev)
            if s:
                self._repeatcount = 0
                ig = InputGesture(s, self)
                self.executeGesture(ig)
            else:
                if (len(self.decodedkeys)):
                    ig = InputGesture(None, self)
                    self.executeGesture(ig)
        except:
            if (self._dev != None): self._dev.close()
            self._dev = None

    #global gestures
    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(papenmeier):left", ),
            "braille_scrollForward": ("br(papenmeier):right", ),
            "braille_previousLine": ("br(papenmeier):up", ),
            "braille_nextLine": ("br(papenmeier):dn", ),
            "braille_routeTo": ("br(papenmeier):route", ),
            "braille_reportFormatting": ("br(papenmeier):upperRouting", ),
            "braille_toggleTether": ("br(papenmeier):r2", ),
            "review_currentCharacter": ("br(papenmeier):l1", ),
            "review_activate": ("br(papenmeier):l2", ),
            "navigatorObject_previous": ("br(papenmeier):left2", ),
            "navigatorObject_next": ("br(papenmeier):right2", ),
            "navigatorObject_parent": ("br(papenmeier):up2", ),
            "navigatorObject_firstChild": ("br(papenmeier):dn2", ),
            "title": ("br(papenmeier):l1,up", ),
            "reportStatusLine": ("br(papenmeier):l2,dn", ),
            "kb:alt": ("br(papenmeier):lt+d3", ),
            "kb:control": ("br(papenmeier):lt+d2", ),
            "kb:escape": ("br(papenmeier):space+d7", ),
            "kb:tab": ("br(papenmeier):space+d3+d7", ),
            "kb:upArrow": ("br(papenmeier):space+d2", ),
            "kb:downArrow": ("br(papenmeier):space+d5", ),
            "kb:leftArrow": ("br(papenmeier):space+d1", ),
            "kb:rightArrow": ("br(papenmeier):space+d4", ),
            "kb:control+escape": ("br(papenmeier):space+d1+d2+d3+d4+d5+d6", ),
            "kb:control+alt+delete":
            ("br(papenmeier):space+d1+d2+d3+d4+d5+d6+d7+d8", ),
            "kb:enter": (
                "br(papenmeier):space+d8",
                "br(papenmeier):d8",
            ),
            "kb:pageup": ("br(papenmeier):space+d3", ),
            "kb:pagedown": ("br(papenmeier):space+d6", ),
            "kb:backspace": (
                "br(papenmeier):space+d6+d8",
                "br(papenmeier):d7",
            ),
            "kb:home": ("br(papenmeier):space+d1+d2", ),
            "kb:end": ("br(papenmeier):space+d4+d5", ),
            "kb:delete": ("br(papenmeier):space+d5+d6", ),
            "kb:f1": ("br(papenmeier):rt+d1", ),
            "kb:f2": ("br(papenmeier):rt+d1+d2", ),
            "kb:f3": ("br(papenmeier):rt+d1+d4", ),
            "kb:f4": ("br(papenmeier):rt+d1+d4+d5", ),
            "kb:f5": ("br(papenmeier):rt+d1+d5", ),
            "kb:f6": ("br(papenmeier):rt+d1+d2+d4", ),
            "kb:f7": ("br(papenmeier):rt+d1+d2+d4+d5", ),
            "kb:f8": ("br(papenmeier):rt+d1+d2+d5", ),
            "kb:f9": ("br(papenmeier):rt+d2+d4", ),
            "kb:f10": ("br(papenmeier):rt+d2+d4+d5", ),
            "kb:f11": ("br(papenmeier):rt+d1+d3", ),
            "kb:f12": ("br(papenmeier):rt+d1+d2+d3", ),
            "kb:control+a": ("br(papenmeier):d1+d7+d8", ),
            "kb:control+p": ("br(papenmeier):d1+d2+d3+d4+d7+d8", ),
            "kb:control+s": ("br(papenmeier):d2+d3+d4+d7+d8", ),
            "kb:control+b": ("br(papenmeier):d1+d2+d7+d8", ),
            "kb:control+c": ("br(papenmeier):d1+d4+d7+d8", ),
            "kb:control+d": ("br(papenmeier):d1+d4+d5+d7+d8", ),
            "kb:control+e": ("br(papenmeier):d1+d5+d7+d8", ),
            "kb:control+f": ("br(papenmeier):d1+d2+d4+d7+d8", ),
            "kb:control+g": ("br(papenmeier):d1+d2+d4+d5+d7+d8", ),
            "kb:control+h": ("br(papenmeier):d1+d2+d5+d7+d8", ),
            "kb:control+i": ("br(papenmeier):d2+d4+d7+d8", ),
            "kb:control+j": ("br(papenmeier):d2+d4+d5+d7+d8", ),
            "kb:control+k": ("br(papenmeier):d1+d3+d7+d8", ),
            "kb:control+l": ("br(papenmeier):d1+d2+d3+d7+d8", ),
            "kb:control+m": ("br(papenmeier):d1+d3+d4+d7+d8", ),
            "kb:control+n": ("br(papenmeier):d1+d3+d4+d5+d7+d8", ),
            "kb:control+o": ("br(papenmeier):d1+d3+d5+d7+d8", ),
            "kb:control+q": ("br(papenmeier):d1+d2+d3+d4+d5+d7+d8", ),
            "kb:control+r": ("br(papenmeier):d1+d2+d3+d5+d7+d8", ),
            "kb:control+t": ("br(papenmeier):d2+d3+d4+d5+d7+d8", ),
            "kb:control+u": ("br(papenmeier):d1+d3+d6+d7+d8", ),
            "kb:control+v": ("br(papenmeier):d1+d2+d3+d6+d7+d8", ),
            "kb:control+w": ("br(papenmeier):d2+d4+d5+d6+d7+d8", ),
            "kb:control+x": ("br(papenmeier):d1+d3+d4+d6+d7+d8", ),
            "kb:control+y": ("br(papenmeier):d1+d3+d4+d5+d6+d7+d8", ),
            "kb:control+z": ("br(papenmeier):d1+d3+d5+d6+d7+d8", ),
        }
    })
Пример #18
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    _dev: hwIo.IoBase
    name = SEIKA_NAME
    # Translators: Name of a braille display.
    description = _("Seika Notetaker")
    isThreadSafe = True

    @classmethod
    def getManualPorts(cls) -> typing.Iterator[typing.Tuple[str, str]]:
        """@return: An iterator containing the name and description for each port.
		"""
        return braille.getSerialPorts(isSeikaBluetoothDeviceInfo)

    def __init__(self, port: typing.Union[None, str, DeviceMatch]):
        super().__init__()
        self.numCells = 0
        self.numBtns = 0
        self.numRoutingKeys = 0
        self.handle = None
        self._hidBuffer = b""
        self._command: typing.Optional[bytes] = None
        self._argsLen: typing.Optional[int] = None

        log.debug(f"Seika Notetaker braille driver: ({port!r})")
        dev: typing.Optional[typing.Union[hwIo.Hid, hwIo.Serial]] = None
        for match in self._getTryPorts(port):
            self.isHid = match.type == bdDetect.KEY_HID
            self.isSerial = match.type == bdDetect.KEY_SERIAL
            try:
                if self.isHid:
                    log.info(f"Trying Seika notetaker on USB-HID")
                    self._dev = dev = hwIo.Hid(
                        path=match.
                        port,  # for a Hid match type 'port' is actually 'path'.
                        onReceive=self._onReceiveHID)
                    dev.setFeature(
                        SEIKA_HID_FEATURES)  # baud rate, stop bit usw
                    dev.setFeature(SEIKA_CMD_ON)  # device on
                elif self.isSerial:
                    log.info(
                        f"Trying Seika notetaker on Bluetooth (serial) port:{match.port}"
                    )
                    self._dev = dev = hwIo.Serial(
                        port=match.port,
                        onReceive=self._onReceiveSerial,
                        baudrate=BAUD,
                        parity=serial.PARITY_NONE,
                        bytesize=serial.EIGHTBITS,
                        stopbits=serial.STOPBITS_ONE,
                    )
                    # Note: SEIKA_CMD_ON not sent as per USB-HID, testing from users hasn't indicated any problems.
                    # The exact purpose of SEIKA_CMD_ON isn't known/documented here.
                else:
                    log.debug(f"Port type not handled: {match.type}")
                    continue
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue
            if self._getDeviceInfo(dev):
                break
            elif dev:
                dev.close()
                dev = None

        if not dev:
            RuntimeError("No MINI-SEIKA display found")
        elif self.numCells == 0:
            dev.close()
            dev = None
            raise RuntimeError("No MINI-SEIKA display found, no response")
        else:
            log.info(f"Seika notetaker,"
                     f" Cells {self.numCells}"
                     f" Buttons {self.numBtns}")

    def _getDeviceInfo(self, dev: hwIo.IoBase) -> bool:
        if not dev or dev._file == INVALID_HANDLE_VALUE:
            log.debug("No MINI-SEIKA display found, open error")
            return False

        dev.write(SEIKA_REQUEST_INFO)  # Request the Info from the device

        # wait and try to get info from the Braille display
        for i in range(MAX_READ_ATTEMPTS):  # the info-block is about
            dev.waitForRead(READ_TIMEOUT_SECS)
            if self.numCells:
                return True
        return False

    def terminate(self):
        try:
            super().terminate()
        finally:
            self._dev.close()

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        cellBytes = SEIKA_SEND_TEXT + bytes([self.numCells]) + bytes(cells)
        self._dev.write(cellBytes)

    def _onReceiveHID(self, data: bytes):
        """Three bytes at a time expected, only the middle byte is used to construct the command, the first
		and third byte are discarded.
		"""
        stream = BytesIO(data)
        cmd = stream.read(3)  # Note, first and third bytes are discarded
        newByte: bytes = cmd[
            1:2]  # use range to return bytes type, containing only index 1
        self._onReceive(newByte)

    def _onReceiveSerial(self, data: bytes):
        """One byte at a time is expected"""
        self._onReceive(data)

    def _onReceive(self, newByte: bytes):
        """
		Note: Further insight into this function would be greatly appreciated.
		This function is a very simple state machine, each stage represents the collection of a field, when all
		fields are collected the command they represent can be processed.

		On each call to _onReceive the new byte is appended to a buffer.
		The buffer is accumulated until the buffer has the required number of bytes for the field being collected.
		There are 3 fields to be collected before a command can be processed:
		1: first 3 bytes: command
		2: 1 byte: specify length of subsequent arguments in bytes
		3: variable length: arguments for command type

		After accumulating enough bytes for each phase, the buffer is cleared and the next stage is entered.
		"""
        COMMAND_LEN = 3
        self._hidBuffer += newByte
        hasCommandBeenCollected = self._command is not None
        hasArgLenBeenCollected = self._argsLen is not None
        if (  # still collecting command bytes
                not hasCommandBeenCollected
                and len(self._hidBuffer) == COMMAND_LEN):
            self._command = self._hidBuffer  # command found reset and wait for args length
            self._hidBuffer = b""
        elif (  # next byte gives the command + args length
                hasCommandBeenCollected
                and not hasArgLenBeenCollected  # argsLen has not
        ):
            # the data is sent with the following structure
            # - command name (3 bytes)
            # - number of subsequent bytes to read (1 byte)
            # - Args (variable bytes)
            self._argsLen = ord(newByte)
            self._hidBuffer = b""
        elif (  # now collect the args,
                hasCommandBeenCollected and hasArgLenBeenCollected
                and len(self._hidBuffer) == self._argsLen):
            arg = self._hidBuffer
            command = self._command

            # reset state variables
            self._command = None
            self._argsLen = None
            self._hidBuffer = b""
            self._processCommand(command, arg)

    def _processCommand(self, command: bytes, arg: bytes) -> None:
        if command == SEIKA_INFO:
            self._handleInfo(arg)
        elif command == SEIKA_ROUTING:
            self._handleRouting(arg)
        elif command == SEIKA_KEYS:
            self._handleKeys(arg)
        elif command == SEIKA_KEYS_ROU:
            self._handleKeysRouting(arg)
        else:
            log.warning(
                f"Seika device has received an unknown command {command}")

    def _handleInfo(self, arg: bytes):
        """After sending a request for information from the braille device this data is returned to complete
		the handshake.
		"""
        self.numBtns = arg[0]
        self.numCells = arg[1]
        self.numRoutingKeys = arg[2]
        try:
            self._description = arg[3:].decode("ascii")
        except UnicodeDecodeError:
            log.debugWarning(
                f"Unable to decode Seika Notetaker description {arg[3:]}")

    def _handleRouting(self, arg: bytes):
        routingIndexes = _getRoutingIndexes(arg)
        for routingIndex in routingIndexes:
            gesture = InputGestureRouting(routingIndex)
            try:
                inputCore.manager.executeGesture(gesture)
            except inputCore.NoInputGestureAction:
                log.debug("No action for Seika Notetaker routing command")

    def _handleKeys(self, arg: bytes):
        brailleDots = arg[0]
        key = arg[1] | (arg[2] << 8)
        gestures = []
        if key:
            gestures.append(InputGesture(keys=key))
        if brailleDots:
            gestures.append(InputGesture(dots=brailleDots))
        for gesture in gestures:
            try:
                inputCore.manager.executeGesture(gesture)
            except inputCore.NoInputGestureAction:
                log.debug("No action for Seika Notetaker keys.")

    def _handleKeysRouting(self, arg: bytes):
        self._handleRouting(arg[3:])
        self._handleKeys(arg[:3])

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(seikantk):routing", ),
            "braille_scrollBack": ("br(seikantk):LB", ),
            "braille_scrollForward": ("br(seikantk):RB", ),
            "braille_previousLine": ("br(seikantk):LJ_UP", ),
            "braille_nextLine": ("br(seikantk):LJ_DOWN", ),
            "braille_toggleTether": ("br(seikantk):LJ_CENTER", ),
            "sayAll": ("br(seikantk):SPACE+BACKSPACE", ),
            "showGui": ("br(seikantk):RB+LB", ),
            "kb:tab": ("br(seikantk):LJ_RIGHT", ),
            "kb:shift+tab": ("br(seikantk):LJ_LEFT", ),
            "kb:upArrow": ("br(seikantk):RJ_UP", ),
            "kb:downArrow": ("br(seikantk):RJ_DOWN", ),
            "kb:leftArrow": ("br(seikantk):RJ_LEFT", ),
            "kb:rightArrow": ("br(seikantk):RJ_RIGHT", ),
            "kb:shift+upArrow":
            ("br(seikantk):SPACE+RJ_UP", "br(seikantk):BACKSPACE+RJ_UP"),
            "kb:shift+downArrow":
            ("br(seikantk):SPACE+RJ_DOWN", "br(seikantk):BACKSPACE+RJ_DOWN"),
            "kb:shift+leftArrow":
            ("br(seikantk):SPACE+RJ_LEFT", "br(seikantk):BACKSPACE+RJ_LEFT"),
            "kb:shift+rightArrow":
            ("br(seikantk):SPACE+RJ_RIGHT", "br(seikantk):BACKSPACE+RJ_RIGHT"),
            "kb:escape": ("br(seikantk):SPACE+RJ_CENTER", ),
            "kb:windows": ("br(seikantk):BACKSPACE+RJ_CENTER", ),
            "kb:space": (
                "br(seikantk):BACKSPACE",
                "br(seikantk):SPACE",
            ),
            "kb:backspace": ("br(seikantk):d7", ),
            "kb:pageup": ("br(seikantk):SPACE+LJ_RIGHT", ),
            "kb:pagedown": ("br(seikantk):SPACE+LJ_LEFT", ),
            "kb:home": ("br(seikantk):SPACE+LJ_UP", ),
            "kb:end": ("br(seikantk):SPACE+LJ_DOWN", ),
            "kb:control+home": ("br(seikantk):BACKSPACE+LJ_UP", ),
            "kb:control+end": ("br(seikantk):BACKSPACE+LJ_DOWN", ),
            "kb:enter": ("br(seikantk):RJ_CENTER", "br(seikantk):d8"),
        },
    })
Пример #19
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    _dev: Union[hwIo.Serial, hwIo.Hid]
    name = "alva"
    # Translators: The name of a braille display.
    description = _("Optelec ALVA 6 series/protocol converter")
    isThreadSafe = True
    timeout = 0.2
    supportedSettings = (braille.BrailleDisplayDriver.HIDInputSetting(
        useConfig=False), )

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts(filterFunc=lambda info: info.get(
            "bluetoothName", "").startswith("ALVA "))

    def _get_model(self):
        if not self._deviceId:
            return ""
        self.model = ALVA_MODEL_IDS[self._deviceId]
        return self.model

    def _updateSettings(self):
        oldNumCells = self.numCells
        if self.isHid:
            displaySettings = self._dev.getFeature(
                ALVA_DISPLAY_SETTINGS_REPORT)
            if displaySettings[ALVA_DISPLAY_SETTINGS_STATUS_CELL_SIDE_POS] > 1:
                # #8106: The ALVA BC680 is known to return a malformed feature report for the first issued request.
                # Therefore, request another display settings report
                displaySettings = self._dev.getFeature(
                    ALVA_DISPLAY_SETTINGS_REPORT)
            self.numCells = displaySettings[
                ALVA_DISPLAY_SETTINGS_CELL_COUNT_POS]
            timeBytes: bytes = self._dev.getFeature(
                ALVA_RTC_REPORT)[1:ALVA_RTC_STR_LENGTH + 1]
            try:
                self._handleTime(timeBytes)
            except:
                log.debugWarning("Getting time from ALVA display failed",
                                 exc_info=True)
            keySettings = self._dev.getFeature(
                ALVA_KEY_SETTINGS_REPORT)[ALVA_KEY_SETTINGS_POS]
            self._rawKeyboardInput = bool(keySettings
                                          & ALVA_KEY_RAW_INPUT_MASK)
        else:
            # Get cell count
            self._ser6SendMessage(b"E", b"?")
            for i in range(3):
                self._dev.waitForRead(self.timeout)
                if self.numCells:  # Display responded
                    break
            else:  # No response from display, do not send the other requests.
                return
            # Get device date and time
            self._ser6SendMessage(b"H", b"?")
            # Get HID keyboard input state
            self._ser6SendMessage(b"r", b"?")
        if oldNumCells not in (0, self.numCells):
            # In case of splitpoint changes, we need to update the braille handler as well
            braille.handler.displaySize = self.numCells

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._rawKeyboardInput = False
        self._deviceId = None

        for portType, portId, port, portInfo in self._getTryPorts(port):
            self.isHid = portType == bdDetect.KEY_HID
            # Try talking to the display.
            try:
                if self.isHid:
                    self._dev = hwIo.Hid(port, onReceive=self._hidOnReceive)
                    self._deviceId = int(portId[-2:], 16)
                else:
                    self._dev = hwIo.Serial(port,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._ser6OnReceive)
                    # Get the device ID
                    self._ser6SendMessage(b"?", b"?")
                    for i in range(3):
                        self._dev.waitForRead(self.timeout)
                        if self._deviceId:  # Display responded
                            break
                    else:  # No response from display
                        continue
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue
            self._updateSettings()
            if self.numCells:
                # A display responded.
                log.info(
                    "Found display with {cells} cells connected via {type} ({port})"
                    .format(cells=self.numCells, type=portType, port=port))
                break
            self._dev.close()

        else:
            raise RuntimeError("No ALVA display found")

        self._keysDown = set()
        self._ignoreKeyReleases = False

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()
            if not self.isHid:
                # We must sleep after closing the COM port, as it takes some time for the device to disconnect.
                time.sleep(self.timeout)

    def _ser6SendMessage(self, cmd: bytes, value: bytes = b"") -> None:
        if not isinstance(value, bytes):
            raise TypeError("Expected param 'value' to have type 'bytes'")
        self._dev.write(b"".join([ESCAPE, cmd, value]))

    def _ser6OnReceive(self, data: bytes):
        """ Callback for L{self._dev} when it is L{hwIo.Serial}
		"""
        if data != ESCAPE:
            return
        cmd: bytes = self._dev.read(1)
        commandLen = ALVA_SER_CMD_LENGTHS[cmd]
        value: bytes = self._dev.read(commandLen)

        if cmd == b"K":  # Input
            self._handleInput(group=value[0], number=value[1])
        elif cmd == b"E":  # Braille cell count
            self.numCells = ord(value)
        elif cmd == b"?":  # Device ID
            self._deviceId = ord(value)  # this command only gets one byte
        elif cmd == b"r":  # Raw keyboard messages enable/disable
            self._rawKeyboardInput = bool(ord(value))
        elif cmd == b"H":  # Time
            # Handling time for serial displays does not block initialization if it fails.
            self._handleTime(value)

    def _hidOnReceive(self, data: bytes):
        """Callback for L{self._dev} when it is L{hwIo.Hid}
		"""
        reportID: bytes = data[0:1]
        if reportID == ALVA_KEY_REPORT:
            self._handleInput(data[ALVA_KEY_REPORT_KEY_GROUP_POS],
                              data[ALVA_KEY_REPORT_KEY_POS])

    def _handleInput(self, group: int, number: int) -> None:
        if group == ALVA_SPECIAL_KEYS_GROUP:
            # ALVA displays communicate setting changes as input messages.
            if number == ALVA_SPECIAL_SETTINGS_CHANGED:
                # Some internal settings have changed.
                # For example, split point could have been set, in which case the number of cells changed.
                # We must handle these properly.
                self._updateSettings()
            return
        isRelease = bool(group & ALVA_RELEASE_MASK)
        group = group & ~ALVA_RELEASE_MASK
        if isRelease:
            if not self._ignoreKeyReleases and self._keysDown:
                try:
                    inputCore.manager.executeGesture(
                        InputGesture(self.model,
                                     self._keysDown,
                                     brailleInput=self._rawKeyboardInput))
                except inputCore.NoInputGestureAction:
                    pass
                # Any further releases are just the rest of the keys in the combination being released,
                # so they should be ignored.
                self._ignoreKeyReleases = True
            self._keysDown.discard((group, number))
        else:  # Press
            self._keysDown.add((group, number))
            # This begins a new key combination.
            self._ignoreKeyReleases = False

    def _hidDisplay(self, cellBytes: bytes) -> None:
        for offset in range(0, len(cellBytes), ALVA_BRAILLE_OUTPUT_MAX_SIZE):
            cellsToWrite = cellBytes[offset:offset +
                                     ALVA_BRAILLE_OUTPUT_MAX_SIZE]
            data = b"".join([
                ALVA_BRAILLE_OUTPUT_REPORT,
                intToByte(offset),
                intToByte(len(cellsToWrite)), cellsToWrite
            ])
            self._dev.write(data)

    def _ser6Display(self, cellBytes: bytes) -> None:
        if not isinstance(cellBytes, bytes):
            raise TypeError("Expected param 'cells' to be of type 'bytes'")
        value = b"".join([b"\x00", intToByte(len(cellBytes)), cellBytes])
        self._ser6SendMessage(b"B", value)

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        cellBytes = bytes(cells)
        if self.isHid:
            self._hidDisplay(cellBytes)
        else:
            self._ser6Display(cellBytes)

    def _handleTime(self, time: bytes):
        """
		@type time: bytes
		"""
        year = time[0] | time[1] << 8
        if not ALVA_RTC_MIN_YEAR <= year <= ALVA_RTC_MAX_YEAR:
            log.debug("This ALVA display doesn't reveal clock information")
            return
        try:
            displayDateTime = datetime.datetime(year=year,
                                                month=time[2],
                                                day=time[3],
                                                hour=time[4],
                                                minute=time[5],
                                                second=time[6])
        except ValueError:
            log.debugWarning("Invalid time/date of ALVA display: %r" % time)
            return
        localDateTime = datetime.datetime.today()
        if abs((displayDateTime -
                localDateTime).total_seconds()) >= ALVA_RTC_MAX_DRIFT:
            log.debugWarning("Display time out of sync: %s" %
                             displayDateTime.isoformat())
            self._syncTime(localDateTime)
        else:
            log.debug("Time not synchronized. Display time %s" %
                      displayDateTime.isoformat())

    def _syncTime(self, dt: datetime.datetime):
        log.debug("Synchronizing braille display date and time...")
        timeList: List[int] = [
            dt.year & 0xFF, dt.year >> 8, dt.month, dt.day, dt.hour, dt.minute,
            dt.second
        ]
        if self.isHid:
            self._dev.setFeature(ALVA_RTC_REPORT + bytes(timeList))
        else:
            self._ser6SendMessage(b"H", bytes(timeList))

    def _get_hidKeyboardInput(self):
        return not self._rawKeyboardInput

    def _set_hidKeyboardInput(self, state):
        rawState = not state
        if self.isHid:
            # Make sure the device settings are up to date.
            keySettings: int = self._dev.getFeature(
                ALVA_KEY_SETTINGS_REPORT)[ALVA_KEY_SETTINGS_POS]
            # Try to update the state
            if rawState:
                newKeySettings = intToByte(keySettings
                                           | ALVA_KEY_RAW_INPUT_MASK)
            elif keySettings & ALVA_KEY_RAW_INPUT_MASK:
                newKeySettings = intToByte(keySettings
                                           ^ ALVA_KEY_RAW_INPUT_MASK)
            else:
                newKeySettings = intToByte(keySettings)
            self._dev.setFeature(ALVA_KEY_SETTINGS_REPORT + newKeySettings)
            # Check whether the state has been changed successfully.
            # If not, this device does not support this feature.
            keySettings: int = self._dev.getFeature(
                ALVA_KEY_SETTINGS_REPORT)[ALVA_KEY_SETTINGS_POS]
            # Save the new state
            self._rawKeyboardInput = bool(keySettings
                                          & ALVA_KEY_RAW_INPUT_MASK)
        else:
            self._ser6SendMessage(cmd=b"r", value=boolToByte(rawState))
            self._ser6SendMessage(b"r", b"?")
            for i in range(3):
                self._dev.waitForRead(self.timeout)
                if rawState is self._rawKeyboardInput:
                    break

    scriptCategory = SCRCAT_BRAILLE

    def script_toggleHidKeyboardInput(self, gesture):
        oldHidKeyboardInput = self.hidKeyboardInput
        self.hidKeyboardInput = not self.hidKeyboardInput
        if self.hidKeyboardInput is oldHidKeyboardInput:
            # Translators: Message when setting HID keyboard simulation failed.
            ui.message(_("Setting HID keyboard simulation not supported"))
        elif self.hidKeyboardInput:
            # Translators: Message when HID keyboard simulation is enabled.
            ui.message(_("HID keyboard simulation enabled"))
        else:
            # Translators: Message when HID keyboard simulation is disabled.
            ui.message(_("HID keyboard simulation disabled"))

    # Translators: Description of the script that toggles HID keyboard simulation.
    script_toggleHidKeyboardInput.__doc__ = _(
        "Toggles HID keyboard simulation")

    __gestures = {
        "br(alva):t1+spEnter": "toggleHidKeyboardInput",
    }

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(alva):t1", "br(alva):etouch1"),
            "braille_previousLine": ("br(alva):t2", ),
            "braille_toFocus": ("br(alva):t3", ),
            "braille_nextLine": ("br(alva):t4", ),
            "braille_scrollForward": ("br(alva):t5", "br(alva):etouch3"),
            "braille_routeTo": ("br(alva):routing", ),
            "braille_reportFormatting": ("br(alva):secondRouting", ),
            "review_top": ("br(alva):t1+t2", ),
            "review_bottom": ("br(alva):t4+t5", ),
            "braille_toggleTether": ("br(alva):t1+t3", ),
            "braille_cycleCursorShape": ("br(alva):t1+t4", ),
            "braille_toggleShowCursor": ("br(alva):t2+t5", ),
            "title": ("br(alva):etouch2", ),
            "reportStatusLine": ("br(alva):etouch4", ),
            "kb:shift+tab": ("br(alva):sp1", ),
            "kb:alt": (
                "br(alva):sp2",
                "br(alva):alt",
            ),
            "kb:escape": ("br(alva):sp3", ),
            "kb:tab": ("br(alva):sp4", ),
            "kb:upArrow": ("br(alva):spUp", ),
            "kb:downArrow": ("br(alva):spDown", ),
            "kb:leftArrow": ("br(alva):spLeft", ),
            "kb:rightArrow": ("br(alva):spRight", ),
            "kb:enter": (
                "br(alva):spEnter",
                "br(alva):enter",
            ),
            "dateTime": ("br(alva):sp2+sp3", ),
            "showGui": ("br(alva):sp1+sp3", ),
            "kb:windows+d": ("br(alva):sp1+sp4", ),
            "kb:windows+b": ("br(alva):sp3+sp4", ),
            "kb:windows": (
                "br(alva):sp1+sp2",
                "br(alva):windows",
            ),
            "kb:alt+tab": ("br(alva):sp2+sp4", ),
            "kb:control+home": ("br(alva):t3+spUp", ),
            "kb:control+end": ("br(alva):t3+spDown", ),
            "kb:home": ("br(alva):t3+spLeft", ),
            "kb:end": ("br(alva):t3+spRight", ),
            "kb:control": ("br(alva):control", ),
        }
    })
Пример #20
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver,ScriptableObject):

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

	@classmethod
	def check(cls):
		return bool(fsbLib)

	@classmethod
	def getPossiblePorts(cls):
		ports = OrderedDict([cls.AUTOMATIC_PORT, ("USB", "USB",)])
		try:
			cls._getBluetoothPorts().next()
			ports["bluetooth"] = "Bluetooth"
		except StopIteration:
			pass
		return ports

	@classmethod
	def _getBluetoothPorts(cls):
		for p in hwPortUtils.listComPorts():
			try:
				btName = p["bluetoothName"]
			except KeyError:
				continue
			if not any(btName == prefix or btName.startswith(prefix + " ") for prefix in bluetoothNames):
				continue
			yield p["port"].encode("mbcs")

	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")),
		# 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")),
	]

	def __init__(self, port="auto"):
		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__()
		self._messageWindowClassAtom=windll.user32.RegisterClassExW(byref(nvdaFsBrlWndCls))
		self._messageWindow=windll.user32.CreateWindowExW(0,self._messageWindowClassAtom,u"nvdaFsBrlWndCls window",0,0,0,0,0,None,None,appInstance,None)
		if port == "auto":
			portsToTry = itertools.chain(["USB"], self._getBluetoothPorts())
		elif port == "bluetooth":
			portsToTry = self._getBluetoothPorts()
		else: # USB
			portsToTry = ["USB"]
		fbHandle=-1
		for port in portsToTry:
			fbHandle=fbOpen(port,self._messageWindow,nvdaFsBrlWm)
			if fbHandle!=-1:
				break
		if fbHandle==-1:
			windll.user32.DestroyWindow(self._messageWindow)
			windll.user32.UnregisterClassW(self._messageWindowClassAtom,appInstance)
			raise RuntimeError("No display found")
		self.fbHandle=fbHandle
		self._configureDisplay()
		numCells=self.numCells
		self.gestureMap.add("br(freedomScientific):topRouting1","globalCommands","GlobalCommands","braille_scrollBack")
		self.gestureMap.add("br(freedomScientific):topRouting%d"%numCells,"globalCommands","GlobalCommands","braille_scrollForward")

	def terminate(self):
		super(BrailleDisplayDriver,self).terminate()
		fbClose(self.fbHandle)
		windll.user32.DestroyWindow(self._messageWindow)
		windll.user32.UnregisterClassW(self._messageWindowClassAtom,appInstance)

	def _get_numCells(self):
		return fbGetCellCount(self.fbHandle)

	def display(self,cells):
		cells="".join([chr(x) for x in cells])
		fbWrite(self.fbHandle,0,len(cells),cells)

	def _configureDisplay(self):
		# See what display we are connected to
		displayName= firmwareVersion=""
		buf = create_string_buffer(16)
		if fbGetDisplayName(self.fbHandle, buf, 16):
			displayName=buf.value
		if fbGetFirmwareVersion(self.fbHandle, buf, 16):
			firmwareVersion=buf.value
		if displayName and firmwareVersion and displayName=="Focus" and ord(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.", displayName, firmwareVersion)
			fbConfigure(self.fbHandle, 0x02)

	def script_toggleLeftWizWheelAction(self,gesture):
		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()
		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",
	}

	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",),
			"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",),
		}
	})
Пример #21
0
class HidBrailleDriver(braille.BrailleDisplayDriver):
    _dev: hwIo.hid.Hid
    name = "hidBrailleStandard"
    # Translators: The name of a series of braille displays.
    description = _("Standard HID Braille Display")
    isThreadSafe = True

    @classmethod
    def check(cls):
        return (isSupportEnabled() and super().check())

    def __init__(self, port="auto"):
        super().__init__()
        self.numCells = 0

        for portType, portId, port, portInfo in self._getTryPorts(port):
            if portType != bdDetect.KEY_HID:
                continue
            # Try talking to the display.
            try:
                self._dev = hwIo.hid.Hid(port, onReceive=self._hidOnReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue  # Couldn't connect.
            if self._dev.usagePage != HID_USAGE_PAGE_BRAILLE:
                log.debug("Not braille")
                continue
            cellValueCaps = self._findCellValueCaps()
            if cellValueCaps:
                self._cellValueCaps = cellValueCaps
                self.numCells = cellValueCaps.ReportCount
                # A display responded.
                log.info(
                    "Found display with {cells} cells connected via {type} ({port})"
                    .format(cells=self.numCells, type=portType, port=port))
                break
            # This device can't be initialized. Move on to the next (if any).
            self._dev.close()
        else:
            raise RuntimeError("No display found")
        self._inputButtonCapsByDataIndex = self._collectInputButtonCapsByDataIndex(
        )
        self._keysDown = set()
        self._ignoreKeyReleases = False

    def _findCellValueCaps(self) -> Optional[hidpi.HIDP_VALUE_CAPS]:
        for valueCaps in self._dev.outputValueCaps:
            if (valueCaps.LinkUsagePage == HID_USAGE_PAGE_BRAILLE
                    and valueCaps.LinkUsage == BraillePageUsageID.BRAILLE_ROW
                    and valueCaps.u1.NotRange.Usage
                    in (BraillePageUsageID.EIGHT_DOT_BRAILLE_CELL,
                        BraillePageUsageID.SIX_DOT_BRAILLE_CELL)
                    and valueCaps.ReportCount > 0):
                return valueCaps
        return None

    def _collectInputButtonCapsByDataIndex(self):
        capsByDataIndex = {}
        relativeIndexInCollection = 0
        lastLinkCollection = None
        # Walk through all the available input buttons
        # storing them in a dictionary, keyed by their data index.
        # Also store the index of each button, relative to the collection it is a part of,
        # As this relative index is used as the routing index for routing keys.
        # We must however walk through the input buttons in reverse order
        # as windows loads the input caps arrays in reverse,
        # See https://docs.microsoft.com/en-us/windows-hardware/drivers/hid/button-capability-arrays
        for buttonCaps in reversed(self._dev.inputButtonCaps):
            if buttonCaps.LinkCollection != lastLinkCollection:
                lastLinkCollection = buttonCaps.LinkCollection
                relativeIndexInCollection = 0
            else:
                relativeIndexInCollection += 1
            if buttonCaps.IsRange:
                r = buttonCaps.u1.Range
                for index in range(r.DataIndexMin, r.DataIndexMax + 1):
                    capsByDataIndex[index] = ButtonCapsInfo(
                        buttonCaps,
                        relativeIndexInCollection=relativeIndexInCollection)
            else:
                nr = buttonCaps.u1.NotRange
                index = nr.DataIndex
                capsByDataIndex[index] = ButtonCapsInfo(
                    buttonCaps,
                    relativeIndexInCollection=relativeIndexInCollection)
        return capsByDataIndex

    def terminate(self):
        try:
            super().terminate()
        finally:
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    def _hidOnReceive(self, data: bytes):
        report = hwIo.hid.HidInputReport(self._dev, data)
        keys = []
        for dataItem in report.getDataItems():
            if dataItem.DataIndex in self._inputButtonCapsByDataIndex and dataItem.u1.On:
                keys.append(dataItem.DataIndex)
        if len(keys) > len(self._keysDown):
            # Press. This begins a new key combination.
            self._ignoreKeyReleases = False
        elif len(keys) < len(self._keysDown):
            self._handleKeyRelease()
        self._keysDown = keys

    def _handleKeyRelease(self):
        if self._ignoreKeyReleases or not self._keysDown:
            return
        try:
            inputCore.manager.executeGesture(InputGesture(
                self, self._keysDown))
        except inputCore.NoInputGestureAction:
            pass
        # Any further releases are just the rest of the keys in the combination being released,
        # so they should be ignored.
        self._ignoreKeyReleases = True

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        cellBytes = b"".join(intToByte(cell) for cell in cells)
        report = hwIo.hid.HidOutputReport(
            self._dev, reportID=self._cellValueCaps.ReportID)
        report.setUsageValueArray(HID_USAGE_PAGE_BRAILLE,
                                  self._cellValueCaps.LinkCollection,
                                  self._cellValueCaps.u1.NotRange.Usage,
                                  cellBytes)
        self._dev.write(report.data)

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": (
                "br(hidBrailleStandard):panLeft",
                "br(hidBrailleStandard):rockerUp",
            ),
            "braille_scrollForward": (
                "br(hidBrailleStandard):panRight",
                "br(hidBrailleStandard):rockerDown",
            ),
            "braille_previousLine": ("br(hidBrailleStandard):space+dot1", ),
            "braille_nextLine": ("br(hidBrailleStandard):space+dot4", ),
            "braille_routeTo":
            ("br(hidBrailleStandard):routerSet1_routerKey", ),
            "braille_toggleTether": ("br(hidBrailleStandard):up+down", ),
            "kb:upArrow": ("br(hidBrailleStandard):joystickUp", ),
            "kb:downArrow": ("br(hidBrailleStandard):joystickDown", ),
            "kb:leftArrow": ("br(hidBrailleStandard):space+dot3",
                             "br(hidBrailleStandard):joystickLeft"),
            "kb:rightArrow": ("br(hidBrailleStandard):space+dot6",
                              "br(hidBrailleStandard):joystickRight"),
            "showGui": ("br(hidBrailleStandard):space+dot1+dot3+dot4+dot5", ),
            "kb:shift+tab": ("br(hidBrailleStandard):space+dot1+dot3", ),
            "kb:tab": ("br(hidBrailleStandard):space+dot4+dot6", ),
            "kb:alt": ("br(hidBrailleStandard):space+dot1+dot3+dot4", ),
            "kb:escape": ("br(hidBrailleStandard):space+dot1+dot5", ),
            "kb:enter": ("br(hidBrailleStandard):joystickCenter"),
            "kb:windows+d": ("br(hidBrailleStandard):Space+dot1+dot4+dot5", ),
            "kb:windows": ("br(hidBrailleStandard):space+dot3+dot4", ),
            "kb:alt+tab":
            ("br(hidBrailleStandard):space+dot2+dot3+dot4+dot5", ),
            "sayAll":
            ("br(hidBrailleStandard):Space+dot1+dot2+dot3+dot4+dot5+dot6", ),
        },
    })
Пример #22
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "brailleNote"
    # Translators: Names of braille displays
    description = _("HumanWare BrailleNote")
    isThreadSafe = True

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts()

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self._serial = None
        for portType, portId, port, portInfo in self._getTryPorts(port):
            log.debug("Checking port %s for a BrailleNote", port)
            try:
                self._serial = hwIo.Serial(port,
                                           baudrate=BAUD_RATE,
                                           timeout=TIMEOUT,
                                           writeTimeout=TIMEOUT,
                                           parity=serial.PARITY_NONE,
                                           onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue
            # Check for cell information
            if self._describe():
                log.debug("BrailleNote found on %s with %d cells", port,
                          self.numCells)
                break
            else:
                self._serial.close()
        else:
            raise RuntimeError("Can't find a braillenote device (port = %s)" %
                               port)

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            self._serial.close()
            self._serial = None

    def _describe(self):
        self.numCells = 0
        log.debug("Writing describe tag")
        self._serial.write(DESCRIBE_TAG)
        self._serial.waitForRead(TIMEOUT)
        # If a valid response was received, _onReceive will have set numCells.
        if self.numCells:
            return True
        log.debug("Not a braillenote")
        return False

    def _onReceive(self, command):
        command = ord(command)
        if command == STATUS_TAG:
            arg = self._serial.read(2)
            self.numCells = ord(arg[1])
            return
        arg = self._serial.read(1)
        if not arg:
            log.debugWarning("Timeout reading argument for command 0x%X" %
                             command)
            return
        # #5993: Read the buffer once more if a BrailleNote QT says it's got characters in its pipeline.
        if command == QT_MOD_TAG:
            key = self._serial.read(2)[-1]
            arg2 = _qtKeys.get(ord(key), key)
        else:
            arg2 = None
        self._dispatch(command, ord(arg), arg2 if arg2 is not None else None)

    def _dispatch(self, command, arg, arg2=None):
        space = False
        if command == THUMB_KEYS_TAG:
            gesture = InputGesture(keys=arg)
        elif command == SCROLL_WHEEL_TAG:
            gesture = InputGesture(wheel=arg)
        elif command == CURSOR_KEY_TAG:
            gesture = InputGesture(routing=arg)
        elif command in (DOTS_TAG, DOTS_SPACE_TAG, DOTS_ENTER_TAG,
                         DOTS_BACKSPACE_TAG):
            if command != DOTS_TAG:
                space = True
            if command == DOTS_ENTER_TAG:
                # Stupid bug in the implementation
                # Force dot8 here, although it should be already there
                arg |= DOT_8
            gesture = InputGesture(dots=arg, space=space)
        elif command == QT_MOD_TAG:
            # BrailleNote QT
            gesture = InputGesture(qtMod=arg, qtData=arg2)
        else:
            log.debugWarning("Unknown command")
            return
        try:
            inputCore.manager.executeGesture(gesture)
        except inputCore.NoInputGestureAction:
            pass

    def display(self, cells):
        # ESCAPE must be quoted because it is a control character
        cells = [chr(cell).replace(ESCAPE, ESCAPE * 2) for cell in cells]
        self._serial.write(DISPLAY_TAG + "".join(cells))

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(braillenote):tback", ),
            "braille_scrollForward": ("br(braillenote):tadvance", ),
            "braille_previousLine": ("br(braillenote):tprevious", ),
            "braille_nextLine": ("br(braillenote):tnext", ),
            "braille_routeTo": ("br(braillenote):routing", ),
            "braille_toggleTether": ("br(braillenote):tprevious+tnext", ),
            "kb:upArrow": (
                "br(braillenote):space+d1",
                "br(braillenote):wUp",
                "br(braillenote):upArrow",
            ),
            "kb:downArrow": (
                "br(braillenote):space+d4",
                "br(braillenote):wDown",
                "br(braillenote):downArrow",
            ),
            "kb:leftArrow": (
                "br(braillenote):space+d3",
                "br(braillenote):wLeft",
                "br(braillenote):leftArrow",
            ),
            "kb:rightArrow": (
                "br(braillenote):space+d6",
                "br(braillenote):wRight",
                "br(braillenote):rightArrow",
            ),
            "kb:pageup": (
                "br(braillenote):space+d1+d3",
                "br(braillenote):function+upArrow",
            ),
            "kb:pagedown": (
                "br(braillenote):space+d4+d6",
                "br(braillenote):function+downArrow",
            ),
            "kb:home": (
                "br(braillenote):space+d1+d2",
                "br(braillenote):function+leftArrow",
            ),
            "kb:end": (
                "br(braillenote):space+d4+d5",
                "br(braillenote):function+rightArrow",
            ),
            "kb:control+home": (
                "br(braillenote):space+d1+d2+d3",
                "br(braillenote):read+T",
            ),
            "kb:control+end": (
                "br(braillenote):space+d4+d5+d6",
                "br(braillenote):read+B",
            ),
            "braille_enter": (
                "br(braillenote):space+d8",
                "br(braillenote):wCenter",
                "br(braillenote):enter",
            ),
            "kb:shift+tab": (
                "br(braillenote):space+d1+d2+d5+d6",
                "br(braillenote):wCounterclockwise",
                "br(braillenote):shift+tab",
            ),
            "kb:tab": (
                "br(braillenote):space+d2+d3+d4+d5",
                "br(braillenote):wClockwise",
                "br(braillenote):tab",
            ),
            "braille_eraseLastCell": (
                "br(braillenote):space+d7",
                "br(braillenote):backspace",
            ),
            "showGui": (
                "br(braillenote):space+d1+d3+d4+d5",
                "br(braillenote):read+N",
            ),
            "kb:windows": (
                "br(braillenote):space+d2+d4+d5+d6",
                "br(braillenote):read+W",
            ),
            "kb:alt": (
                "br(braillenote):space+d1+d3+d4",
                "br(braillenote):read+M",
            ),
            "toggleInputHelp": (
                "br(braillenote):space+d2+d3+d6",
                "br(braillenote):read+1",
            ),
        },
    })
Пример #23
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    """papenmeier_serial braille display driver.
	"""
    name = "papenmeier_serial"
    # Translators: Names of braille displays.
    description = _("Papenmeier BRAILLEX older models")

    @classmethod
    def check(cls):
        """should return false if there is a missing dependency"""
        return True

    @classmethod
    def getPossiblePorts(cls):
        ports = OrderedDict()
        for p in hwPortUtils.listComPorts():
            # Translators: Name of a serial communications port
            ports[p["port"]] = _("Serial: {portName}").format(
                portName=p["friendlyName"])
        return ports

    def initTable(self):
        """do not use braille builtin table"""
        table = [1] * self.numCells
        self._dev.write(brl_out(512 + self._offsetHorizontal, table))

    def __init__(self, port):
        """Initializes braille display driver"""
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._lastkey = ''
        self._dev = None
        self._repeatcount = 0
        self._port = (port)
        log.info("papenmeier_serial using port " + self._port)
        #try to connect to braille display
        for baud in (19200, 38400):
            if (self._dev is None):
                self._dev = serial.Serial(self._port,
                                          baudrate=baud,
                                          timeout=TIMEOUT,
                                          writeTimeout=TIMEOUT)
                self._dev.write(brl_auto_id())
                if (baud == 19200): time.sleep(0.2)
                else: time.sleep(0.03)
                displaytype = brl_poll(self._dev)
                dic = -1
                if len(displaytype) == 10 and displaytype[
                        0] == STX and displaytype[1] == ord(b'I'):
                    dic = displaytype[2]
                    self._eab = (baud == 38400)
                if (dic == -1):
                    self._dev.close()
                    self._dev = None

        #set parameters for display
        if (dic == 2):
            self.numCells = 40
            self._offsetHorizontal = 0
            self._keymap = [
                'l1', 'l2', 'left', 'up', 'r2', 'dn', 'right', 'r1', 'reportf'
            ]
        elif (dic == 1):
            self._offsetHorizontal = 0
            self.numCells = 40
        elif (dic == 3):
            self.numCells = 80
            self._offsetHorizontal = 22
            self._keymap = [
                'l1', 'l2', 'r2', 'reportf', 'up', 'left', 'r1', 'right', 'dn',
                'left2', 'up2', 'dn2', 'right2'
            ]
        elif (dic == 6):
            self.numCells = 80
            self._offsetHorizontal = 0
        elif (dic == 66):  #//BRAILLEX EL 80
            self._offsetHorizontal = 2
            self.numCells = 80
        elif (dic == 67):
            self._offsetHorizontal = 20
            self.numCells = 80
        elif (dic == 64):
            self._offsetHorizontal = 13
            self.numCells = 40
        elif (dic == 65):
            self._offsetHorizontal = 13
            self.numCells = 66
        elif (dic == 68):
            self._offsetHorizontal = 0
            self.numCells = 40
        elif (dic == 69):
            self._offsetHorizontal = 0
            self.numCells = 32
        elif (dic == 70):
            self._offsetHorizontal = 0
            self.numCells = 20
        else:
            raise Exception("No or unknown braille display found")
        #initialize display
        self.initTable()
        #start keyCheckTimer
        self._decodedkeys = []
        self._keyCheckTimer = wx.PyTimer(self._handleKeyPresses)
        self._keyCheckTimer.Start(KEY_CHECK_INTERVAL)

    def terminate(self):
        """free resources"""
        super(BrailleDisplayDriver, self).terminate()
        try:
            if (self._dev != None):
                self._dev.close()
                self._dev = None
                self._keyCheckTimer.Stop()
                self._keyCheckTimer = None
        except:
            pass

    def display(self, cells: List[int]):
        """write data to braille display"""
        if (self._dev != None):
            try:
                self._dev.write(brl_out(self._offsetHorizontal, cells))
            except:
                self._dev = None

    def executeGesture(self, gesture):
        """execute a gesture"""
        #here you can add other gesture types
        try:
            if gesture.id: inputCore.manager.executeGesture(gesture)
        except inputCore.NoInputGestureAction:
            pass

    def _handleKeyPresses(self):  #called by the keycheck timer
        """if a button was pressed an input gesture is executed"""
        if (self._dev != None):
            data = brl_poll(self._dev)
            if len(data) == 10 and data[1] == ord(b'K'):
                pos = data[2] * 256 + data[3]
                pos = (pos - 768) / 3
                pressed = data[6]
                keys = data[8]
                self._repeatcount = 0
                self.executeGesture(InputGesture(pos, pressed, keys, self))
            elif (len(data) == 0):
                if (self._repeatcount == 50):
                    if (len(self._lastkey)):
                        self.executeGesture(
                            InputGesture(None, None, None, self))
                    self._repeatcount = 0
                else:
                    self._repeatcount += 1

    #global gestures
    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(papenmeier_serial):left", ),
            "braille_scrollForward": ("br(papenmeier_serial):right", ),
            "braille_previousLine": ("br(papenmeier_serial):up", ),
            "braille_nextLine": ("br(papenmeier_serial):dn", ),
            "braille_routeTo": ("br(papenmeier_serial):route", ),
            "braille_reportFormatting":
            ("br(papenmeier_serial):upperRouting", ),
            "braille_toggleTether": ("br(papenmeier_serial):r2", ),
            "review_currentCharacter": ("br(papenmeier_serial):l1", ),
            "review_activate": ("br(papenmeier_serial):l2", ),
            "reportFormatting": ("br(papenmeier_serial):reportf", ),
            "navigatorObject_previous":
            ("br(papenmeier_serial):left2", "br(papenmeier_serial):r1,left"),
            "navigatorObject_next":
            ("br(papenmeier_serial):right2", "br(papenmeier_serial):r1,right"),
            "navigatorObject_parent":
            ("br(papenmeier_serial):up2", "br(papenmeier_serial):r1,up"),
            "navigatorObject_firstChild": ("br(papenmeier_serial):dn2",
                                           "br(papenmeier_serial):r1,dn"),
            "title": ("br(papenmeier_serial):l1,up", ),
            "reportStatusLine": ("br(papenmeier_serial):l2,dn", ),
        }
    })
Пример #24
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    """papenmeier braille display driver.
	"""
    name = "papenmeier"
    # Translators: Names of braille displays.
    description = _("Papenmeier BRAILLEX newer models")

    @classmethod
    def check(cls):
        """should return false if there is a missing dependency"""
        return True

    def connectBrxCom(
            self):  #connect to brxcom server (provided by papenmeier)
        try:
            brxcomkey = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,
                                        "SOFTWARE\\FHP\\BrxCom")
            value, vtype = _winreg.QueryValueEx(brxcomkey, "InstallPath")
            _winreg.CloseKey(brxcomkey)
            self._brxnvda = c.cdll.LoadLibrary(str(value + "\\brxnvda.dll"))
            if (self._brxnvda.brxnvda_init(
                    str(value + "\\BrxCom.dll").decode("mbcs")) == 0):
                self._baud = 1  #prevent bluetooth from connecting
                self.numCells = self._brxnvda.brxnvda_numCells()
                self._voffset = self._brxnvda.brxnvda_numVertCells()
                log.info("Found Braille Display connected via BRXCom")
                self.startTimer()
                return None
        except:
            log.debugWarning("BRXCom is not installed")
            self._brxnvda = None

    def connectBluetooth(self):
        """try to connect to bluetooth device first, bluetooth is only supported on Braillex Trio"""
        if (self._baud == 0 and self._dev is None):
            for portInfo in sorted(
                    hwPortUtils.listComPorts(onlyAvailable=True),
                    key=lambda item: "bluetoothName" in item):
                port = portInfo["port"]
                hwID = portInfo["hardwareID"]
                if "bluetoothName" in portInfo:
                    if (portInfo["bluetoothName"] == "braillex trio "):
                        try:
                            self._dev = serial.Serial(
                                port,
                                baudrate=57600,
                                timeout=BLUETOOTH_TIMEOUT,
                                writeTimeout=BLUETOOTH_TIMEOUT)
                            self.numCells = 40
                            self._proto = 'B'
                            self._voffset = 0
                            log.info("connectBluetooth success")
                        except:
                            log.debugWarning("connectBluetooth failed")

    def connectUSB(self, devlist):
        """try to connect to usb device,is triggered when BRXCOM is not installed and bluetooth
connection could not be established"""
        try:
            self._dev = ftdi2.open_ex(devlist[0])
            self._dev.set_baud_rate(self._baud)
            self._dev.inWaiting = self._dev.get_queue_status
            log.info("connectUSB success")
        except:
            log.debugWarning("connectUSB failed")

    def __init__(self):
        """initialize driver"""
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._nlk = 0
        self._nrk = 0
        self._r1next = itertools.cycle(('r1a', 'r1b'))
        self.decodedkeys = []
        self._baud = 0
        self._dev = None
        self._proto = None
        self.connectBrxCom()
        if (self._baud == 1):
            return  #brxcom is running, skip bluetooth and USB

        #try to connect to usb device,
        #if no usb device is found there may be a bluetooth device
        try:
            devlist = ftdi2.list_devices()
        except:
            devlist = []
        if (len(devlist) == 0):
            self.connectBluetooth()
        else:
            self._baud = 57600
            self.connectUSB(devlist)
            if (self._dev is None):
                return None
            try:
                #request type of braille display
                self._dev.write(brl_auto_id())
                time.sleep(
                    0.05
                )  # wait 50 ms in order to get response for further actions
                autoid = brl_poll(self._dev)
                if (autoid == ''):
                    #no response, assume a Trio is connected
                    self._baud = 115200
                    self._dev.set_baud_rate(self._baud)
                    self._dev.purge()
                    self._dev.read(self._dev.inWaiting())
                    self.numCells = 40
                    self._proto = 'B'
                    self._voffset = 0
                    #we don't use autoid here, because the trio might be switched off and other braille displays using the same baud rate do not exist
                else:
                    if (len(autoid) != 8):
                        return None
                    autoid = struct.unpack('BBBBBBBB', autoid)
                    if (autoid[3] == 0x35 and autoid[4] == 0x38):  #EL80s
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL80s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3A):  #EL70s
                        self.numCells = 70
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL70s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x35):  #EL40s
                        self.numCells = 40
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL40s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x37):  #EL66s
                        self.numCells = 66
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL66s")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3E):  #EL20c
                        self.numCells = 20
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL20c")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3F):  #EL40c
                        self.numCells = 40
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL40c")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x30):  #EL60c
                        self.numCells = 60
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL60c")
                    elif (autoid[3] == 0x36 and autoid[4] == 0x31):  #EL80c
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 0
                        log.info("Found EL80c")
                    elif (autoid[3] == 0x35 and autoid[4] == 0x3b):  #EL2D80s
                        self.numCells = 80
                        self._nlk = 1
                        self._nrk = 1
                        self._proto = 'A'
                        self._voffset = 20
                        log.info("Found EL2D80s")
                    else:
                        log.debugWarning('UNKNOWN BRAILLE')

            except:
                log.debugWarning('BROKEN PIPE - THIS SHOULD NEVER HAPPEN')
        if (self.numCells == 0): raise Exception('no device found')

        #start keycheck timer
        self.startTimer()
        self.initmapping()

    def startTimer(self):
        """start timers used by this driver"""
        self._keyCheckTimer = wx.PyTimer(self._handleKeyPresses)
        self._keyCheckTimer.Start(KEY_CHECK_INTERVAL)
        #the keycheck timer polls the braille display for keypresses
        self._bluetoothTimer = wx.PyTimer(self.connectBluetooth)
        self._bluetoothTimer.Start(BLUETOOTH_INTERVAL)
        #the bluetooth timer tries to reconnect if the bluetooth connection is lost

    def stopTimer(self):
        """stop all timers"""
        try:
            self._keyCheckTimer.Stop()
            self._bluetoothTimer.Stop()
        except:
            pass

        self._keyCheckTimer = None
        self._bluetoothTimer = None

    def initmapping(self):
        if (self._proto == 'A'):
            self._keynamesrepeat = {
                20: 'up2',
                21: 'up',
                22: 'dn',
                23: 'dn2',
                24: 'right',
                25: 'left',
                26: 'right2',
                27: 'left2'
            }
            x = self.numCells * 2 + 4
            self._keynames = {
                x + 28: 'r1',
                x + 29: 'r2',
                20: 'up2',
                21: 'up',
                22: 'dn',
                23: 'dn2',
                24: 'right',
                25: 'left',
                26: 'right2',
                27: 'left2',
                28: 'l1',
                29: 'l2'
            }
        else:
            self._keynamesrepeat = {
                16: 'left2',
                17: 'right2',
                18: 'left',
                19: 'right',
                20: 'dn2',
                21: 'dn',
                22: 'up',
                23: 'up2'
            }
            x = self.numCells * 2
            self._keynames = {
                16: 'left2',
                17: 'right2',
                18: 'left',
                19: 'right',
                20: 'dn2',
                21: 'dn',
                22: 'up',
                23: 'up2',
                x + 38: 'r2',
                x + 39: 'r1',
                30: 'l2',
                31: 'l1'
            }
            self._dotNames = {
                32: 'd6',
                1: 'd1',
                2: 'd2',
                4: 'd3',
                8: 'd4',
                64: 'd7',
                128: 'd8',
                16: 'd5'
            }
            self._thumbs = {1: "rt", 2: "space", 4: "lt"}

    def terminate(self):
        """free resources used by this driver"""
        try:
            super(BrailleDisplayDriver, self).terminate()
            self.stopTimer()
            if (self._dev != None): self._dev.close()
            self._dev = None
            if (self._brxnvda): self._brxnvda.brxnvda_close()
        except:
            self._dev = None

    def display(self, cells):
        """write to braille display"""
        if (self._brxnvda):
            newcells = "".join([chr(cell) for cell in cells])
            self._brxnvda.brxnvda_sendToDisplay(newcells)
            return
        if (self._dev is None): return
        try:
            self._dev.write(brl_out(cells, self._nlk, self._nrk,
                                    self._voffset))
        except:
            self._dev.close()
            self._dev = None

    def executeGesture(self, gesture):
        """executes a gesture"""
        if (gesture.id == 'r1'): gesture.id = next(self._r1next)
        if gesture.id or (gesture.dots or gesture.space):
            inputCore.manager.executeGesture(gesture)

    def _handleKeyPresses(self):
        """handles key presses and performs a gesture"""
        try:
            if (self._brxnvda):
                k = self._brxnvda.brxnvda_keyIndex()
                if (k != -1):
                    self.executeGesture(InputGesture(k, self))
                return
            if (self._dev is None and self._baud > 0):
                try:
                    devlist = ftdi2.list_devices()
                    if (len(devlist) > 0):
                        self.connectUSB(devlist)
                except:
                    return
            s = brl_poll(self._dev)
            if s:
                self._repeatcount = 0
                ig = InputGesture(s, self)
                self.executeGesture(ig)
            else:
                if (len(self.decodedkeys)):
                    ig = InputGesture(None, self)
                    self.executeGesture(ig)
        except:
            if (self._dev != None): self._dev.close()
            self._dev = None

    def script_upperRouting(self, gesture):
        globalCommands.commands.script_braille_routeTo(gesture)
        wx.CallLater(50, scriptHandler.executeScript,
                     globalCommands.commands.script_reportFormatting, gesture)

    script_upperRouting.__doc__ = _("Route to and report formatting")

    #global gestures
    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(papenmeier):left", ),
            "braille_scrollForward": ("br(papenmeier):right", ),
            "braille_previousLine": ("br(papenmeier):up", ),
            "braille_nextLine": ("br(papenmeier):dn", ),
            "braille_routeTo": ("br(papenmeier):route", ),
            "navigatorObject_moveToFlatReviewAtObjectPosition":
            ("br(papenmeier):r1a", ),
            "braille_toggleTether": ("br(papenmeier):r2", ),
            "review_currentCharacter": ("br(papenmeier):l1", ),
            "navigatorObject_toFocus": ("br(papenmeier):r1b", ),
            "review_activate": ("br(papenmeier):l2", ),
            "navigatorObject_previous": ("br(papenmeier):left2", ),
            "navigatorObject_next": ("br(papenmeier):right2", ),
            "navigatorObject_parent": ("br(papenmeier):up2", ),
            "navigatorObject_firstChild": ("br(papenmeier):dn2", ),
            "title": ("br(papenmeier):l1,up", ),
            "reportStatusLine": ("br(papenmeier):l2,dn", ),
            "kb:enter": ("br(papenmeier):d8", ),
            "kb:backspace": ("br(papenmeier):d7", ),
            "kb:alt": ("br(papenmeier):lt+d3", ),
            "kb:control": ("br(papenmeier):lt+d2", ),
            "kb:escape": ("br(papenmeier):space+d7", ),
            "kb:control+escape": ("br(papenmeier):lt+d1+d2+d3+d4+d5+d6", ),
            "kb:tab": ("br(papenmeier):space+d3+d7", ),
            "kb:upArrow": ("br(papenmeier):space+d2", ),
            "kb:downArrow": ("br(papenmeier):space+d5", ),
            "kb:leftArrow": ("br(papenmeier):space+d1", ),
            "kb:rightArrow": ("br(papenmeier):space+d4", ),
        }
    })

    __gestures = {
        "br(papenmeier):upperRouting": "upperRouting",
    }
Пример #25
0
            raise

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_scrollBack": ("br(braillenote):tback", ),
            "braille_scrollForward": ("br(braillenote):tadvance", ),
            "braille_previousLine": ("br(braillenote):tprevious", ),
            "braille_nextLine": ("br(braillenote):tnext", ),
            "braille_routeTo": ("br(braillenote):routing", ),
            "braille_toggleTether": ("br(braillenote):tprevious+tnext", ),
            "kb:upArrow": ("br(braillenote):space+d1", ),
            "kb:downArrow": ("br(braillenote):space+d4", ),
            "kb:leftArrow": ("br(braillenote):space+d3", ),
            "kb:rightArrow": ("br(braillenote):space+d6", ),
            "kb:pageup": ("br(braillenote):space+d1+d3", ),
            "kb:pagedown": ("br(braillenote):space+d4+d6", ),
            "kb:home": ("br(braillenote):space+d1+d2", ),
            "kb:end": ("br(braillenote):space+d4+d5", ),
            "kb:control+home": ("br(braillenote):space+d1+d2+d3", ),
            "kb:control+end": ("br(braillenote):space+d4+d5+d6", ),
            "kb:enter": ("br(braillenote):space+d8", ),
            "kb:shift+tab": ("br(braillenote):space+d1+d2+d5+d6", ),
            "kb:tab": ("br(braillenote):space+d2+d3+d4+d5", ),
            "kb:backspace": ("br(braillenote):space+d7", ),
            "showGui": ("br(braillenote):space+d1+d3+d4+d5", ),
            "kb:windows": ("br(braillenote):space+d2+d4+d5+d6", ),
            "kb:alt": ("br(braillenote):space+d1+d3+d4", ),
            "toggleInputHelp": ("br(braillenote):space+d2+d3+d6", ),
        },
    })

Пример #26
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "hims"
    # Translators: The name of a series of braille displays.
    description = _(
        "HIMS Braille Sense/Braille EDGE/Smart Beetle/Sync Braille series")
    isThreadSafe = True
    timeout = 0.2

    @classmethod
    def getManualPorts(cls):
        return braille.getSerialPorts(
            filterFunc=lambda info: "bluetoothName" in info)

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._model = None

        for match in self._getTryPorts(port):
            portType, portId, port, portInfo = match
            self.isBulk = portType == bdDetect.KEY_CUSTOM
            # Try talking to the display.
            try:
                if self.isBulk:
                    # onReceiveSize based on max packet size according to USB endpoint information.
                    self._dev = hwIo.Bulk(port,
                                          0,
                                          1,
                                          self._onReceive,
                                          onReceiveSize=64)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            parity=PARITY,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue
            for i in range(3):
                self._sendCellCountRequest()
                # Wait for an expected response.
                if self.isBulk:
                    # Hims Bulk devices sometimes present themselves to the system while not yet ready.
                    # For example, when switching the connection mode toggle on the Braille EDGE from Bluetooth to USB,
                    # the USB device is connected but not yet ready.
                    # Wait ten times the timeout, which is ugly, but effective.
                    self._dev.waitForRead(self.timeout * 10)
                else:
                    self._dev.waitForRead(self.timeout)
                if self.numCells:
                    break
            if not self.numCells:
                log.debugWarning("No response from potential Hims display")
                self._dev.close()
                continue
            self._sendIdentificationRequests(match)
            if self._model:
                # A display responded.
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self._model.name, type=portType, port=port))
                break

            self._dev.close()
        else:
            raise RuntimeError("No Hims display found")

    def display(self, cells: List[int]):
        # cells will already be padded up to numCells.
        cellBytes = bytes(cells)
        self._sendPacket(b"\xfc", b"\x01", cellBytes)

    def _sendCellCountRequest(self):
        log.debug("Sending cell count request...")
        self._sendPacket(b"\xfb", b"\x01", bytes(32))  # send 32 null bytes

    def _sendIdentificationRequests(self, match: bdDetect.DeviceMatch):
        log.debug("Considering sending identification requests for device %s" %
                  str(match))
        if match.type == bdDetect.KEY_CUSTOM:  # USB Bulk
            matchedModelsMap = [
                modelTuple for modelTuple in modelMap
                if (modelTuple[1].usbId == match.id)
            ]
        elif "bluetoothName" in match.deviceInfo:  # Bluetooth
            matchedModelsMap = [
                modelTuple for modelTuple in modelMap
                if (modelTuple[1].bluetoothPrefix
                    and match.id.startswith(modelTuple[1].bluetoothPrefix))
            ]
        else:  # The only serial device we support which is not bluetooth, is a Sync Braille
            self._model = SyncBraille()
            log.debug(
                "Use %s as model without sending an additional identification request"
                % self._model.name)
            return
        if not matchedModelsMap:
            log.debugWarning(
                "The provided device match to send identification requests didn't yield any results"
            )
            matchedModelsMap = modelMap
        if len(matchedModelsMap) == 1:
            modelCls = matchedModelsMap[0][1]
            numCells = self.numCells or modelCls.numCells
            if numCells:
                # There is only one model matching the criteria, and we have the proper number of cells.
                # There's no point in sending an identification request at all, just use this model
                log.debug(
                    "Use %s as model without sending an additional identification request"
                    % modelCls.name)
                self._model = modelCls()
                self.numCells = numCells
                return
        self._model = None
        for modelId, cls in matchedModelsMap:
            log.debug("Sending request for id %r" % modelId)

            self._dev.write(b"".join([b"\x1c", modelId, b"\x1f"]))
            self._dev.waitForRead(self.timeout)
            if self._model:
                log.debug("%s model has been set" % self._model.name)
                break

    def _handleIdentification(self, recvId: bytes):
        modelCls = None
        models = [
            modelCls for modelId, modelCls in modelMap if (modelId == recvId)
        ]
        log.debug("Identification received, id %s" % recvId)
        if not models:
            raise ValueError("Device identification ID unknown in model map")
        if len(models) == 1:
            modelCls = models[0]
            self.numCells = self.numCells or modelCls.numCells
            log.debug("There is an exact match, %s found with %d cells" %
                      (modelCls.name, self.numCells))
        elif len(models) > 1:
            log.debug("Multiple models match: %s" %
                      ", ".join(modelCls.name for modelCls in models))
            try:
                modelCls = next(cls for cls in models
                                if cls.numCells == self.numCells)
                log.debug(
                    "There is an exact match out of multiple models, %s found with %d cells"
                    % (modelCls.name, self.numCells))
            except StopIteration:
                log.debugWarning(
                    "No exact model match found for the reported %d cells display"
                    % self.numCells)
                try:
                    modelCls = next(cls for cls in models if not cls.numCells)
                except StopIteration:
                    modelCls = Model
        if modelCls:
            self._model = modelCls()

    def _handlePacket(self, packet: bytes):
        mode = packet[1]
        if mode == 0x00:  # Cursor routing
            routingIndex = packet[3]
            try:
                inputCore.manager.executeGesture(
                    RoutingInputGesture(routingIndex))
            except inputCore.NoInputGestureAction:
                pass
        elif mode == 0x01:  # Braille input or function key
            if not self._model:
                return
            _keys = int.from_bytes(packet[4:8], "little", signed=False)
            keys = set()
            for keyHex in self._model.keys:
                if _keys & keyHex:
                    # This key is pressed
                    _keys -= keyHex
                    keys.add(keyHex)
                    if _keys == 0:
                        break
            if _keys:
                log.error("Unknown key(s) 0x%x received from Hims display" %
                          _keys)
                return
            try:
                inputCore.manager.executeGesture(
                    KeyInputGesture(self._model, keys))
            except inputCore.NoInputGestureAction:
                pass
        elif mode == 0x02:  # Cell count
            self.numCells = packet[3]

    def _onReceive(self, data: bytes):
        if self.isBulk:
            # data contains the entire packet.
            stream = BytesIO(data)
            firstByte: bytes = data[0:1]
            stream.seek(1)
        else:
            firstByte = data
            # data only contained the first byte. Read the rest from the device.
            stream = self._dev
        if firstByte == b"\x1c":
            # A device is identifying itself
            deviceId: bytes = stream.read(2)
            # When a device identifies itself, the packets ends with 0x1f
            assert stream.read(1) == b"\x1f"
            self._handleIdentification(deviceId)
        elif firstByte == b"\xfa":
            # Command packets are ten bytes long
            packet = firstByte + stream.read(9)
            assert packet[2] == 0x01  # Fixed value
            CHECKSUM_INDEX = 8
            checksum: int = packet[CHECKSUM_INDEX]
            assert packet[9] == 0xfb  # Command End
            calcCheckSum: int = 0xff & sum(c for index, c in enumerate(packet)
                                           if (index != CHECKSUM_INDEX))
            assert (calcCheckSum == checksum)
            self._handlePacket(packet)
        else:
            log.debug("Unknown first byte received: 0x%x" % ord(firstByte))
            return

    def _sendPacket(self,
                    packetType: bytes,
                    mode: bytes,
                    data1: bytes,
                    data2: bytes = b""):
        d1Len = len(data1)
        d2Len = len(data2)
        # Construct the packet
        packet: List[bytes] = [
            # Packet start
            packetType * 2,
            # Mode
            mode,  # Always "\x01" according to the spec
            # Data block 1 start
            b"\xf0",
            # Data block 1 length
            d1Len.to_bytes(length=2, byteorder="little", signed=False),
            # Data block 1
            data1,
            # Data block 1 end
            b"\xf1",
            # Data block 2 is currently not used, but it is part of the spec
            # Data block 2 start
            b"\xf2",
            # Data block 1 length
            d2Len.to_bytes(length=2, byteorder="little", signed=False),
            # Data block 2
            data2,
            # Data block 2 end
            b"\xf3",
            # Reserved bytes
            b"\x00" * 4,
            # Reserved space for checksum
            # Note that the checksum has the -3rd position in the final packet bytearray,
            # whereas it has the -2nd position in the packet list
            b"\x00",
            # Packet end
            b"\xfd" * 2,
        ]
        packetB = bytearray(b"".join(packet))
        #  checksum is the 3rd index from the end because 'packet end' takes up
        # two bytes and 'packetB' is a bytearray
        checksumIndexInPacketB: int = -3
        checksum: int = 0xff & sum(packetB)
        packetB[checksumIndexInPacketB] = checksum

        # check that the packet is the size we expect:
        ptLen = len(packetType)
        assert (ptLen == 1)
        mLen = len(mode)
        assert (mLen == 1)
        packetLength = ptLen * 2 + mLen + 1 + 2 + d1Len + 1 + 1 + 2 + d2Len + 1 + 4 + 1 + 2
        assert (len(packetB) == packetLength)

        self._dev.write(bytes(packetB))

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # We must sleep before closing the port as not doing this can leave the display in a bad state where it can not be re-initialized.
            time.sleep(self.timeout)
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(hims):routing", ),
            "braille_scrollBack": (
                "br(hims):leftSideScrollUp",
                "br(hims):rightSideScrollUp",
                "br(hims):leftSideScroll",
            ),
            "braille_scrollForward": (
                "br(hims):leftSideScrollDown",
                "br(hims):rightSideScrollDown",
                "br(hims):rightSideScroll",
            ),
            "braille_previousLine":
            ("br(hims):leftSideScrollUp+rightSideScrollUp", ),
            "braille_nextLine":
            ("br(hims):leftSideScrollDown+rightSideScrollDown", ),
            "review_previousLine": ("br(hims):rightSideUpArrow", ),
            "review_nextLine": ("br(hims):rightSideDownArrow", ),
            "review_previousCharacter": ("br(hims):rightSideLeftArrow", ),
            "review_nextCharacter": ("br(hims):rightSideRightArrow", ),
            "braille_toFocus": (
                "br(hims):leftSideScrollUp+leftSideScrollDown",
                "br(hims):rightSideScrollUp+rightSideScrollDown",
                "br(hims):leftSideScroll+rightSideScroll",
            ),
            "kb:control": (
                "br(hims.smartbeetle):f1",
                "br(hims.brailleedge):f3",
            ),
            "kb:windows": (
                "br(hims.smartbeetle):f2",
                "br(hims):f7",
            ),
            "kb:alt": (
                "br(hims):dot1+dot3+dot4+space",
                "br(hims.smartbeetle):f3",
                "br(hims):f2",
                "br(hims.brailleedge):f4",
            ),
            "kb:shift": ("br(hims):f5", ),
            "kb:insert": (
                "br(hims):dot2+dot4+space",
                "br(hims):f6",
            ),
            "kb:applications": (
                "br(hims):dot1+dot2+dot3+dot4+space",
                "br(hims):f8",
            ),
            "kb:capsLock": ("br(hims):dot1+dot3+dot6+space", ),
            "kb:tab": (
                "br(hims):dot4+dot5+space",
                "br(hims):f3",
                "br(hims.brailleedge):f2",
            ),
            "kb:shift+alt+tab": ("br(hims):f2+f3+f1", ),
            "kb:alt+tab": ("br(hims):f2+f3", ),
            "kb:shift+tab": ("br(hims):dot1+dot2+space", ),
            "kb:end": ("br(hims):dot4+dot6+space", ),
            "kb:control+end": ("br(hims):dot4+dot5+dot6+space", ),
            "kb:home": (
                "br(hims):dot1+dot3+space",
                "br(hims.smartbeetle):f4",
            ),
            "kb:control+home": ("br(hims):dot1+dot2+dot3+space", ),
            "kb:alt+f4": ("br(hims):dot1+dot3+dot5+dot6+space", ),
            "kb:leftArrow": (
                "br(hims):dot3+space",
                "br(hims):leftSideLeftArrow",
            ),
            "kb:control+shift+leftArrow": ("br(hims):dot2+dot8+space+f1", ),
            "kb:control+leftArrow": ("br(hims):dot2+space", ),
            "kb:shift+alt+leftArrow": ("br(hims):dot2+dot7+f1", ),
            "kb:alt+leftArrow": ("br(hims):dot2+dot7", ),
            "kb:rightArrow": (
                "br(hims):dot6+space",
                "br(hims):leftSideRightArrow",
            ),
            "kb:control+shift+rightArrow": ("br(hims):dot5+dot8+space+f1", ),
            "kb:control+rightArrow": ("br(hims):dot5+space", ),
            "kb:shift+alt+rightArrow": ("br(hims):dot5+dot7+f1", ),
            "kb:alt+rightArrow": ("br(hims):dot5+dot7", ),
            "kb:pageUp": ("br(hims):dot1+dot2+dot6+space", ),
            "kb:control+pageUp": ("br(hims):dot1+dot2+dot6+dot8+space", ),
            "kb:upArrow": (
                "br(hims):dot1+space",
                "br(hims):leftSideUpArrow",
            ),
            "kb:control+shift+upArrow": ("br(hims):dot2+dot3+dot8+space+f1", ),
            "kb:control+upArrow": ("br(hims):dot2+dot3+space", ),
            "kb:shift+alt+upArrow": ("br(hims):dot2+dot3+dot7+f1", ),
            "kb:alt+upArrow": ("br(hims):dot2+dot3+dot7", ),
            "kb:shift+upArrow": ("br(hims):leftSideScrollDown+space", ),
            "kb:pageDown": ("br(hims):dot3+dot4+dot5+space", ),
            "kb:control+pageDown": ("br(hims):dot3+dot4+dot5+dot8+space", ),
            "kb:downArrow": (
                "br(hims):dot4+space",
                "br(hims):leftSideDownArrow",
            ),
            "kb:control+shift+downArrow":
            ("br(hims):dot5+dot6+dot8+space+f1", ),
            "kb:control+downArrow": ("br(hims):dot5+dot6+space", ),
            "kb:shift+alt+downArrow": ("br(hims):dot5+dot6+dot7+f1", ),
            "kb:alt+downArrow": ("br(hims):dot5+dot6+dot7", ),
            "kb:shift+downArrow": ("br(hims):space+rightSideScrollDown", ),
            "kb:escape": (
                "br(hims):dot1+dot5+space",
                "br(hims):f4",
                "br(hims.brailleedge):f1",
            ),
            "kb:delete": (
                "br(hims):dot1+dot3+dot5+space",
                "br(hims):dot1+dot4+dot5+space",
            ),
            "kb:f1": ("br(hims):dot1+dot2+dot5+space", ),
            "kb:f3": ("br(hims):dot1+dot2+dot4+dot8", ),
            "kb:f4": ("br(hims):dot7+f3", ),
            "kb:windows+b": ("br(hims):dot1+dot2+f1", ),
            "kb:windows+d": ("br(hims):dot1+dot4+dot5+f1", ),
            "kb:control+insert": ("br(hims.smartbeetle):f1+rightSideScroll", ),
            "kb:alt+insert": ("br(hims.smartbeetle):f3+rightSideScroll", ),
        }
    })
Пример #27
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
	"""
	Driver for Freedom Scientific braille displays
	"""
	name = "freedomScientific"
	# Translators: Names of braille displays.
	description = _("Freedom Scientific Focus/PAC Mate series")
	isThreadSafe = True
	receivesAckPackets = True
	timeout = 0.2

	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")),
		# 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")),
	]

	def __init__(self, port="auto"):
		self.numCells = 0
		self._ackPending = False
		self._pendingCells = []
		self._keyBits = 0
		self._extendedKeyBits = 0
		self._ignoreKeyReleases = False
		self._model = None
		self._manufacturer = None
		self._firmwareVersion = None
		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.gestureMap.add("br(freedomScientific):rightWizWheelDown", *action[2])
		super(BrailleDisplayDriver, self).__init__()
		for portType, portId, port, portInfo in self._getTryPorts(port):
			self.isUsb = portType == bdDetect.KEY_CUSTOM
			# Try talking to the display.
			try:
				if self.isUsb:
					self._dev = hwIo.Bulk(
						port,
						epIn=1,
						epOut=0,
						onReceive=self._onReceive,
						writeSize=0,
						onReceiveSize=56
					)
				else:
					self._dev = hwIo.Serial(
						port,
						baudrate=BAUD_RATE,
						parity=PARITY,
						timeout=self.timeout,
						writeTimeout=self.timeout,
						onReceive=self._onReceive
					)
			except EnvironmentError:
				log.debugWarning("", exc_info=True)
				continue

			# Send an identification request
			self._sendPacket(FS_PKT_QUERY)
			for _i in xrange(3):
				self._dev.waitForRead(self.timeout)
				if self.numCells and self._model:
					break

			if self.numCells and self._model:
				# A display responded.
				log.info("Found {device} connected via {type} ({port})".format(
					device=self._model, type=portType, port=port))
				break
			self._dev.close()

		else:
			raise RuntimeError("No Freedom Scientific display found")

		self._configureDisplay()
		self.gestureMap.add("br(freedomScientific):topRouting1",
			"globalCommands", "GlobalCommands", "braille_scrollBack")
		self.gestureMap.add("br(freedomScientific):topRouting%d" % self.numCells,
			"globalCommands", "GlobalCommands", "braille_scrollForward")

	def terminate(self):
		try:
			super(BrailleDisplayDriver, self).terminate()
		finally:
			# Make sure the device gets closed.
			# If it doesn't, we may not be able to re-open it later.
			self._dev.close()

	def _sendPacket(self, packetType, arg1=FS_BYTE_NULL, arg2=FS_BYTE_NULL, arg3=FS_BYTE_NULL, data=FS_DATA_EMPTY):
		"""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 int2byte(arg)
			return arg
		arg1 = handleArg(arg1)
		arg2 = handleArg(arg2)
		arg3 = handleArg(arg3)
		packet = [packetType, arg1, arg2, arg3, data]
		if data:
			packet.append(int2byte(BrailleDisplayDriver._calculateChecksum("".join(packet))))
		self._dev.write("".join(packet))

	def _onReceive(self, data):
		"""Event handler when data from the display is received

		Formats a packet of four bytes in a packet type and three arguments.
		If the packet is known to have a payload, this is also fetched and the checksum is verified.
		The constructed packet is handed off to L{_handlePacket}.
		"""
		if self.isUsb:
			data = BytesIO(data)
			packetType = data.read(1)
		else:
			packetType = data
			data = self._dev

		arg1 = data.read(1)
		arg2 = data.read(1)
		arg3 = data.read(1)
		log.debug("Got packet of type %r with args: %r %r %r", packetType, arg1, arg2, arg3)
		# Info and extended key responses are the only packets with payload and checksum
		if packetType in (FS_PKT_INFO, FS_PKT_EXT_KEY):
			length = ord(arg1)
			payload = data.read(length)
			checksum = ord(data.read(1))
			calculatedChecksum = BrailleDisplayDriver._calculateChecksum(packetType + arg1 + arg2 + arg3 + payload)
			assert calculatedChecksum == checksum, "Checksum mismatch, expected %s but got %s" % (checksum, payload[-1])
		else:
			payload = FS_DATA_EMPTY

		self._handlePacket(packetType, arg1, arg2, arg3, payload)

	def _handlePacket(self, packetType, arg1, arg2, arg3, payload):
		"""Handle a packet from the device"

		The following packet types are handled:

			* FS_PKT_ACK: See L{_handleAck}
			* FS_PKT_NAK: Logged and handled as an ACK
			* FS_PKT_INFO: Manufacturer, model and firmware version are extracted and set as
				properties on the object. Cell count is determined based on L{MODELS}.
				* arg1: length of payload
				* payload: manufacturer, model, firmware version in a fixed width field string
			* FS_PKT_WHEEL: The corresponding L{WheelGesture}s are sent for the wheel events.
				* arg1: movement direction (up/down) and number of clicks moved
					Bits: BBBAAA (least significant)
					* A: (bits 1-3) number of clicks the wheel has moved
					* B: (bits 4-6) which wheel (left/right) and what direction (up/down)
			* FS_PKT_BUTTON: the corresponding L{RoutingGesture} is sent
				* arg1: number of routing button
				* arg2: key press/release
				* arg3: if this is a button on the second row of routing buttons
			* FS_PKT_KEY: a key or button on the display is pressed/released (including the braille keyboard)
				* arg 1, 2, 3, 4:
					These bytes form the value indicating which of the 8 keys are pressed on the device.
					Key releases can be detected by comparing to the previous state, this work is done in L{_handleKeys}.
			* FS_PKT_EXT_KEY: ??
				* payload: The 4 most significant bits from a single byte are used.
					More investigation is required.
		"""
		if packetType == FS_PKT_ACK:
			self._handleAck()
		elif packetType == FS_PKT_NAK:
			log.debugWarning("NAK received!")
			self._handleAck()
		elif packetType == FS_PKT_INFO:
			self._manufacturer = payload[INFO_MANU_START:INFO_MANU_END].replace(FS_BYTE_NULL, "")
			self._model = payload[INFO_MODEL_START:INFO_MODEL_END].replace(FS_BYTE_NULL, "")
			self._firmwareVersion = payload[INFO_VERSION_START:INFO_VERSION_END].replace(FS_BYTE_NULL, "")
			self.numCells = MODELS.get(self._model, 0)
			if self.numCells in FOCUS_1_CELL_COUNTS:
				# Focus first gen: apply custom translation table
				self.translationTable = FOCUS_1_TRANSLATION_TABLE
			log.debug("Device info: manufacturer: %s model: %s, version: %s",
				self._manufacturer, self._model, self._firmwareVersion)
		elif packetType == FS_PKT_WHEEL:
			threeLeastSigBitsMask = 0x7
			count = ord(arg1) & threeLeastSigBitsMask
			wheelNumber = ((ord(arg1) >> 3) & threeLeastSigBitsMask)
			try:
				# There are only two wheels, one on the left, one on the right.
				# Either wheel could have moved up or down.
				isDown, isRight = [
					(False, False),
					(True, False),
					(True, True),
					(False, True)
				][wheelNumber]
			except IndexError:
				log.debugWarning("wheelNumber unknown")
				return
			for _i in xrange(count):
				gesture = WizWheelGesture(self._model, isDown, isRight)
				try:
					inputCore.manager.executeGesture(gesture)
				except inputCore.NoInputGestureAction:
					pass
		elif packetType == FS_PKT_BUTTON:
			key = ord(arg1)
			# the least significant bit is set when the key is pressed
			leastSigBitMask = 0x01
			isPress = bool(ord(arg2) & leastSigBitMask)
			isTopRow = bool(ord(arg3))
			if isPress:
				# Ignore keypresses
				return
			gesture = RoutingGesture(self._model, key, isTopRow)
			try:
				inputCore.manager.executeGesture(gesture)
			except inputCore.NoInputGestureAction:
				pass
		elif packetType == FS_PKT_KEY:
			keyBits = ord(arg1) | (ord(arg2) << 8) | (ord(arg3) << 16)
			self._handleKeys(keyBits)
		elif packetType == FS_PKT_EXT_KEY:
			keyBits = ord(payload[0]) >> 4
			self._handleExtendedKeys(keyBits)
		else:
			log.debugWarning("Unknown packet of type: %r", packetType)

	def _handleAck(self):
		"Displays any queued cells after receiving an ACK"
		super(BrailleDisplayDriver, self)._handleAck()
		if self._pendingCells:
			self.display(self._pendingCells)

	@staticmethod
	def _updateKeyBits(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
		keyBit = 0X1
		keyBits |= oldKeyBits & ~((0X1 << keyCount) - 1)
		while oldKeyBits != keyBits:
			oldKey = oldKeyBits & keyBit
			newKey = keyBits & keyBit

			if oldKey and not newKey:
				# A key has been released
				isRelease = True
				if not keyBitsBeforeRelease:
					keyBitsBeforeRelease = oldKeyBits
				oldKeyBits &= ~keyBit
			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
		self._keyBits = keyBits
		if isRelease and not self._ignoreKeyReleases:
			gesture = KeyGesture(self._model, keyBitsBeforeRelease, self._extendedKeyBits)
			try:
				inputCore.manager.executeGesture(gesture)
			except inputCore.NoInputGestureAction:
				pass
			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
		self._extendedKeyBits = keyBits
		if isRelease and not self._ignoreKeyReleases:
			gesture = KeyGesture(self._model, self._keyBits, keyBitsBeforeRelease)
			try:
				inputCore.manager.executeGesture(gesture)
			except inputCore.NoInputGestureAction:
				pass
			self._ignoreKeyReleases = True

	@staticmethod
	def _calculateChecksum(data):
		"""Calculate the checksum for extended packets"""
		checksum = 0
		for byte in data:
			checksum -= ord(byte)
		checksum = checksum & 0xFF
		return checksum

	def display(self, cells):
		if self.translationTable:
			cells = _translate(cells, FOCUS_1_TRANSLATION_TABLE)
		if not self._awaitingAck:
			cells = b"".join([int2byte(x) for x in cells])
			self._sendPacket(FS_PKT_WRITE, int2byte(self.numCells), FS_BYTE_NULL, FS_BYTE_NULL, cells)
			self._pendingCells = []
		else:
			self._pendingCells = cells

	def _configureDisplay(self):
		"""Enable extended keys on Focus firmware 3 and up"""
		if not self._model or not self._firmwareVersion:
			return
		if 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, FS_CFG_EXTKEY)

	def script_toggleLeftWizWheelAction(self, _gesture):
		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()
		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",
	}

	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",),
			"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:alt+shift+tab": ("br(freedomScientific):dot1+dot2+dot5+dot6+brailleSpaceBar",),
			"kb:windows+tab": ("br(freedomScientific):dot2+dot3+dot4+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",),
		}
	})
Пример #28
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver, ScriptableObject):
    name = "handyTech"
    # Translators: The name of a series of braille displays.
    description = _("Handy Tech braille displays")
    isThreadSafe = True
    receivesAckPackets = True
    timeout = 0.2

    @classmethod
    def check(cls):
        return True

    @classmethod
    def getPossiblePorts(cls):
        ports = OrderedDict()
        comPorts = list(hwPortUtils.listComPorts(onlyAvailable=True))
        try:
            next(cls._getAutoPorts(comPorts))
            ports.update((cls.AUTOMATIC_PORT, ))
        except StopIteration:
            pass
        for portInfo in comPorts:
            # Translators: Name of a serial communications port.
            ports[portInfo["port"]] = _("Serial: {portName}").format(
                portName=portInfo["friendlyName"])
        return ports

    @classmethod
    def _getAutoPorts(cls, comPorts):
        for portInfo in hwPortUtils.listHidDevices():
            if portInfo.get("usbID") in USB_IDS_HID_CONVERTER:
                yield portInfo["devicePath"], "USB HID serial converter"
            if portInfo.get("usbID") in USB_IDS_HID_NATIVE:
                yield portInfo["devicePath"], "USB HID"
        # Try bluetooth ports last.
        for portInfo in sorted(comPorts,
                               key=lambda item: "bluetoothName" in item):
            port = portInfo["port"]
            hwId = portInfo["hardwareID"]
            if hwId.startswith(r"FTDIBUS\COMPORT"):
                # USB.
                # TODO: It seems there is also another chip (Gohubs) used in some models. See if we can autodetect that as well.
                portType = "USB serial"
                try:
                    usbId = hwId.split("&", 1)[1]
                except IndexError:
                    continue
                if usbId not in USB_IDS_SER:
                    continue
            elif "bluetoothName" in portInfo:
                # Bluetooth.
                portType = "bluetooth"
                btName = portInfo["bluetoothName"]
                if not any(
                        btName.startswith(prefix)
                        for prefix in BLUETOOTH_NAMES):
                    continue
            else:
                continue
            yield port, portType

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._model = None
        self._ignoreKeyReleases = False
        self._keysDown = set()
        self._brailleInput = False
        self._hidSerialBuffer = ""

        if port == "auto":
            tryPorts = self._getAutoPorts(
                hwPortUtils.listComPorts(onlyAvailable=True))
        else:
            tryPorts = ((port, "serial"), )
        for port, portType in tryPorts:
            # At this point, a port bound to this display has been found.
            # Try talking to the display.
            self.isHid = portType.startswith("USB HID")
            self.isHidSerial = portType == "USB HID serial converter"
            try:
                if self.isHid:
                    self._dev = hwIo.Hid(port, onReceive=self._onReceive)
                    if self.isHidSerial:
                        # This is either the standalone HID adapter cable for older displays,
                        # or an older display with a HID - serial adapter built in
                        # Send a flush to open the serial channel
                        self._dev.write(HT_HID_RPT_InCommand +
                                        HT_HID_CMD_FlushBuffers)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            parity=PARITY,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue

            self.sendPacket(HT_PKT_RESET)
            for _i in xrange(3):
                # An expected response hasn't arrived yet, so wait for it.
                self._dev.waitForRead(self.timeout)
                if self.numCells and self._model:
                    break

            if self.numCells:
                # A display responded.
                self._model.postInit()
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self._model.name, type=portType, port=port))
                break
            self._dev.close()

        else:
            raise RuntimeError("No Handy Tech display found")

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    def sendPacket(self, packetType, data=""):
        if type(data) == bool or type(data) == int:
            data = chr(data)
        if self._model:
            data = self._model.deviceId + data
        if self.isHid:
            self._sendHidPacket(packetType + data)
        else:
            self._dev.write(packetType + data)

    def sendExtendedPacket(self, packetType, data=""):
        if type(data) == bool or type(data) == int:
            data = chr(data)
        packet = "{length}{extType}{data}\x16".format(
            extType=packetType,
            data=data,
            length=chr(len(data) + len(packetType)))
        self.sendPacket(HT_PKT_EXTENDED, packet)

    def _sendHidPacket(self, packet):
        assert self.isHid
        maxBlockSize = self._dev._writeSize - 3
        # When the packet length exceeds C{writeSize}, the packet is split up into several packets.
        # These packets are of size C{blockSize}.
        # They contain C{HT_HID_RPT_InData}, the length of the data block,
        # the data block itself and a terminating null character.
        bytesRemaining = packet
        while bytesRemaining:
            blockSize = min(maxBlockSize, len(bytesRemaining))
            hidPacket = HT_HID_RPT_InData + chr(
                blockSize) + bytesRemaining[:blockSize] + "\x00"
            self._dev.write(hidPacket)
            bytesRemaining = bytesRemaining[blockSize:]

    def _handleKeyRelease(self):
        if self._ignoreKeyReleases or not self._keysDown:
            return
        # The first key released executes the key combination.
        try:
            inputCore.manager.executeGesture(
                InputGesture(self._model, self._keysDown, self._brailleInput))
        except inputCore.NoInputGestureAction:
            pass
        # Any further releases are just the rest of the keys in the combination
        # being released, so they should be ignored.
        self._ignoreKeyReleases = True

    # pylint: disable=R0912
    # Pylint complains about many branches, might be worth refactoring
    def _onReceive(self, data):
        if self.isHidSerial:
            # The HID serial converter seems to wrap one or two bytes into a single HID packet
            hidLength = ord(data[1])
            self._hidSerialBuffer += data[2:(2 + hidLength)]
            currentBufferLength = len(self._hidSerialBuffer)
            # We only support the extended packet based protocol
            # Thus, the only non-extended packet we expect is the device identification, which is of type HT_PKT_OK and two bytes in size
            serPacketType = self._hidSerialBuffer[0]
            if serPacketType != HT_PKT_EXTENDED:
                if currentBufferLength > 2:
                    stream = StringIO(self._hidSerialBuffer[:2])
                    self._hidSerialBuffer = self._hidSerialBuffer[2:]
                elif currentBufferLength == 2:
                    stream = StringIO(self._hidSerialBuffer)
                    self._hidSerialBuffer = ""
                else:
                    # The packet is not yet complete
                    return
            # Extended packets are at least 5 bytes in size.
            elif serPacketType == HT_PKT_EXTENDED and currentBufferLength >= 5:
                # Check whether our packet is complete
                # The second byte is the model, the third byte is the data length, excluding the terminator
                packet_length = ord(self._hidSerialBuffer[2]) + 4
                if len(self._hidSerialBuffer) < packet_length:
                    # The packet is not yet complete
                    return
                # We have a complete packet, but it must be isolated from another packet that could have landed in the buffer
                if len(self._hidSerialBuffer) > packet_length:
                    stream = StringIO(self._hidSerialBuffer[:packet_length])
                    self._hidSerialBuffer = self._hidSerialBuffer[
                        packet_length:]
                else:
                    assert self._hidSerialBuffer.endswith(
                        "\x16")  # Extended packets are terminated with \x16
                    stream = StringIO(self._hidSerialBuffer)
                    self._hidSerialBuffer = ""
            else:
                # The packet is not yet complete
                return
            stream.seek(1)
        elif self.isHid:
            # data contains the entire packet.
            stream = StringIO(data)
            serPacketType = data[2]
            # Skip the header, so reading the stream will only give the rest of the data
            stream.seek(3)
        else:
            serPacketType = data
            # data only contained the packet type. Read the rest from the device.
            stream = self._dev

        modelId = stream.read(1)
        if not self._model:
            if not modelId in MODELS:
                log.debugWarning("Unknown model: %r" % modelId)
                raise RuntimeError(
                    "The model with ID %r is not supported by this driver" %
                    modelId)
            self._model = MODELS.get(modelId)(self)
            self.numCells = self._model.numCells
        elif self._model.deviceId != modelId:
            # Somehow the model ID of this display changed, probably another display
            # plugged in the same (already open) serial port.
            self.terminate()

        if serPacketType == HT_PKT_OK:
            pass
        elif serPacketType == HT_PKT_ACK:
            # This is unexpected, but we need to make sure that we handle old style ack
            self._handleAck()
        elif serPacketType == HT_PKT_NAK:
            log.debugWarning("NAK received!")
        elif serPacketType == HT_PKT_EXTENDED:
            packet_length = ord(stream.read(1))
            packet = stream.read(packet_length)
            terminator = stream.read(1)
            assert terminator == "\x16"  # Extended packets are terminated with \x16
            extPacketType = packet[0]
            if extPacketType == HT_EXTPKT_CONFIRMATION:
                # Confirmation of a command.
                if packet[1] == HT_PKT_ACK:
                    self._handleAck()
                elif packet[1] == HT_PKT_NAK:
                    log.debugWarning("NAK received!")
            elif extPacketType == HT_EXTPKT_KEY:
                key = ord(packet[1])
                release = (key & KEY_RELEASE) != 0
                if release:
                    key = key ^ KEY_RELEASE
                    self._handleKeyRelease()
                    self._keysDown.discard(key)
                else:
                    # Press.
                    # This begins a new key combination.
                    self._ignoreKeyReleases = False
                    self._keysDown.add(key)
            elif extPacketType == HT_EXTPKT_ATC_INFO:
                # Ignore ATC packets for now
                pass
            elif extPacketType == HT_EXTPKT_GET_PROTOCOL_PROPERTIES:
                pass
            else:
                # Unknown extended packet, log it
                log.debugWarning("Unhandled extended packet of type %r: %r" %
                                 (extPacketType, packet))
        else:
            # Unknown packet type, log it
            log.debugWarning("Unhandled packet of type %r" % serPacketType)

    def display(self, cells):
        # cells will already be padded up to numCells.
        self._model.display(cells)

    scriptCategory = SCRCAT_BRAILLE

    def script_toggleBrailleInput(self, _gesture):
        self._brailleInput = not self._brailleInput
        if self._brailleInput:
            # Translators: message when braille input is enabled
            ui.message(_('Braille input enabled'))
        else:
            # Translators: message when braille input is disabled
            ui.message(_('Braille input disabled'))

    # Translators: description of the script to toggle braille input
    script_toggleBrailleInput.__doc__ = _("Toggle braille input")

    __gestures = {
        'br(handytech):space+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):leftSpace+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):rightSpace+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech.easybraille):left+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech.easybraille):right+b1+b3+b4': 'toggleBrailleInput',
        'br(handytech):space+dot1+dot2+dot7': 'toggleBrailleInput',
    }

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(handyTech):routing", ),
            "braille_scrollBack": (
                "br(handytech):leftSpace",
                "br(handytech):leftTakTop",
                "br(handytech):rightTakTop",
                "br(handytech):b3",
                "br(handytech):left",
            ),
            "braille_previousLine": ("br(handytech):b4", ),
            "braille_nextLine": ("br(handytech):b5", ),
            "braille_scrollForward": (
                "br(handytech):rightSpace",
                "br(handytech):leftTakBottom",
                "br(handytech):rightTakBottom",
                "br(handytech):b6",
                "br(handytech):right",
            ),
            "braille_toggleTether": ("br(handytech):b2", ),
            "braille_toggleFocusContextPresentation": ("br(handytech):b7", ),
            "braille_toggleShowCursor": ("br(handytech):b1", ),
            "kb:shift+tab": (
                "br(handytech):leftTakTop+leftTakBottom",
                "br(handytech):escape",
            ),
            "kb:tab": (
                "br(handytech):rightTakTop+rightTakBottom",
                "br(handytech):return",
            ),
            "kb:enter": (
                "br(handytech):leftTakTop+leftTakBottom+rightTakTop+rightTakBottom",
                "br(handytech):b8",
                "br(handytech):escape+return",
                "br(handytech):joystickAction",
            ),
            "kb:alt": ("br(handytech):b2+b4+b5", ),
            "kb:escape": ("br(handytech):b4+b6", ),
            "kb:upArrow": ("br(handytech):joystickUp", ),
            "kb:downArrow": ("br(handytech):joystickDown", ),
            "kb:leftArrow": ("br(handytech):joystickLeft", ),
            "kb:rightArrow": ("br(handytech):joystickRight", ),
            "kb:1": ("br(handytech):n1", ),
            "kb:2": ("br(handytech):n2", ),
            "kb:3": ("br(handytech):n3", ),
            "kb:4": ("br(handytech):n4", ),
            "kb:5": ("br(handytech):n5", ),
            "kb:6": ("br(handytech):n6", ),
            "kb:7": ("br(handytech):n7", ),
            "kb:8": ("br(handytech):n8", ),
            "kb:9": ("br(handytech):n9", ),
            "kb:0": ("br(handytech):n0", ),
            "showGui": ("br(handytech):b2+b4+b5+b6", ),
        },
    })
Пример #29
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
    name = "hims"
    # Translators: The name of a series of braille displays.
    description = _(
        "HIMS Braille Sense/Braille EDGE/Smart Beetle/Sync Braille series")
    isThreadSafe = True
    timeout = 0.2

    @classmethod
    def check(cls):
        return True

    @classmethod
    def getPossiblePorts(cls):
        ports = OrderedDict()
        comPorts = list(hwPortUtils.listComPorts(onlyAvailable=True))
        try:
            next(cls._getAutoPorts(comPorts))
            ports.update((cls.AUTOMATIC_PORT, ))
        except StopIteration:
            pass
        for portInfo in comPorts:
            if not "bluetoothName" in portInfo:
                continue
            # Translators: Name of a serial communications port.
            ports[portInfo["port"]] = _("Serial: {portName}").format(
                portName=portInfo["friendlyName"])
        return ports

    @classmethod
    def _getAutoPorts(cls, comPorts):
        # USB bulk
        for bulkId in USB_IDS_BULK:
            portType = "USB bulk"
            try:
                rootKey = _winreg.OpenKey(
                    _winreg.HKEY_LOCAL_MACHINE,
                    r"SYSTEM\CurrentControlSet\Enum\USB\%s" % bulkId)
            except WindowsError:
                continue
            else:
                with rootKey:
                    for index in itertools.count():
                        try:
                            keyName = _winreg.EnumKey(rootKey, index)
                        except WindowsError:
                            break
                        try:
                            with _winreg.OpenKey(
                                    rootKey,
                                    os.path.join(
                                        keyName,
                                        "Device Parameters")) as paramsKey:
                                yield _winreg.QueryValueEx(
                                    paramsKey,
                                    "SymbolicName")[0], portType, bulkId
                        except WindowsError:
                            continue
        # Try bluetooth ports last.
        for portInfo in sorted(comPorts,
                               key=lambda item: "bluetoothName" in item):
            port = portInfo["port"]
            hwID = portInfo["hardwareID"]
            if hwID.startswith(r"FTDIBUS\COMPORT"):
                # USB.
                portType = "USB serial"
                try:
                    usbID = hwID.split("&", 1)[1]
                except IndexError:
                    continue
                if usbID != SyncBraille.usbId:
                    continue
                yield portInfo['port'], portType, usbID
            elif "bluetoothName" in portInfo:
                # Bluetooth.
                portType = "bluetooth"
                btName = portInfo["bluetoothName"]
                for prefix in bluetoothPrefixes:
                    if btName.startswith(prefix):
                        btPrefix = prefix
                        break
                else:
                    btPrefix = None
                yield portInfo['port'], portType, btPrefix

    def __init__(self, port="auto"):
        super(BrailleDisplayDriver, self).__init__()
        self.numCells = 0
        self._model = None
        if port == "auto":
            tryPorts = self._getAutoPorts(
                hwPortUtils.listComPorts(onlyAvailable=True))
        else:
            try:
                btName = next(
                    portInfo.get("bluetoothName", "")
                    for portInfo in hwPortUtils.listComPorts()
                    if portInfo.get("port") == port)
                btPrefix = next(prefix for prefix in bluetoothPrefixes
                                if btName.startswith(prefix))
                tryPorts = ((port, "bluetooth", btPrefix), )
            except StopIteration:
                tryPorts = ()

        for port, portType, identifier in tryPorts:
            self.isBulk = portType == "USB bulk"
            # Try talking to the display.
            try:
                if self.isBulk:
                    # onReceiveSize based on max packet size according to USB endpoint information.
                    self._dev = hwIo.Bulk(port,
                                          0,
                                          1,
                                          self._onReceive,
                                          writeSize=0,
                                          onReceiveSize=64)
                else:
                    self._dev = hwIo.Serial(port,
                                            baudrate=BAUD_RATE,
                                            parity=PARITY,
                                            timeout=self.timeout,
                                            writeTimeout=self.timeout,
                                            onReceive=self._onReceive)
            except EnvironmentError:
                log.debugWarning("", exc_info=True)
                continue
            for i in xrange(3):
                self._sendCellCountRequest()
                # Wait for an expected response.
                if self.isBulk:
                    # Hims Bulk devices sometimes present themselves to the system while not yet ready.
                    # For example, when switching the connection mode toggle on the Braille EDGE from Bluetooth to USB,
                    # the USB device is connected but not yet ready.
                    # Wait ten times the timeout, which is ugly, but effective.
                    self._dev.waitForRead(self.timeout * 10)
                else:
                    self._dev.waitForRead(self.timeout)
                if self.numCells:
                    break
            if not self.numCells:
                log.debugWarning("No response from potential Hims display")
                self._dev.close()
                continue
            if portType == "USB serial":
                self._model = SyncBraille()
            elif self.isBulk:
                self._sendIdentificationRequests(usbId=identifier)
            elif portType == "bluetooth" and identifier:
                self._sendIdentificationRequests(bluetoothPrefix=identifier)
            else:
                self._sendIdentificationRequests()
            if self._model:
                # A display responded.
                log.info("Found {device} connected via {type} ({port})".format(
                    device=self._model.name, type=portType, port=port))
                break

            self._dev.close()
        else:
            raise RuntimeError("No Hims display found")

    def display(self, cells):
        # cells will already be padded up to numCells.
        self._sendPacket("\xfc", "\x01", "".join(chr(cell) for cell in cells))

    def _sendCellCountRequest(self):
        log.debug("Sending cell count request...")
        self._sendPacket("\xfb", "\x01", "\x00" * 32)

    def _sendIdentificationRequests(self, usbId=None, bluetoothPrefix=None):
        log.debug(
            "Considering sending identification requests: usbId=%s, bluetoothPrefix=%s"
            % (usbId, bluetoothPrefix))
        if usbId and not bluetoothPrefix:
            map = [
                modelTuple for modelTuple in modelMap
                if modelTuple[1].usbId == usbId
            ]
        elif not usbId and bluetoothPrefix:
            map = [
                modelTuple for modelTuple in modelMap
                if modelTuple[1].bluetoothPrefix == bluetoothPrefix
            ]
        elif usbId and bluetoothPrefix:
            map = [
                modelTuple for modelTuple in modelMap
                if modelTuple[1].usbId == usbId
                and modelCls.bluetoothPrefix == bluetoothPrefix
            ]
        else:  # not usbId and not bluetoothPrefix
            map = modelMap
        if not map:
            raise ValueError(
                "The specified criteria to send identification requests didn't yield any results"
            )
        if len(map) == 1:
            modelCls = map[0][1]
            numCells = self.numCells or modelCls.numCells
            if numCells:
                # There is only one model matching the criteria, and we have the proper number of cells.
                # There's no point in sending an identification request at all, just use this model
                log.debug(
                    "Chose %s as model without sending an additional identification request"
                    % modelCls.name)
                self._model = modelCls()
                self.numCells = numCells
                return
        self._model = None
        for id, cls in map:
            log.debug("Sending request for id %r" % id)
            self._dev.write("\x1c{id}\x1f".format(id=id))
            self._dev.waitForRead(self.timeout)
            if self._model:
                log.debug("%s model has been set" % self._model.name)
                break

    def _handleIdentification(self, id):
        modelCls = None
        models = [modelCls for modelId, modelCls in modelMap if modelId == id]
        log.debug("Identification received, id %s" % id)
        if not models:
            raise ValueError("Device identification ID unknown in model map")
        if len(models) == 1:
            modelCls = models[0]
            self.numCells = self.numCells or modelCls.numCells
            log.debug("There is an exact match, %s found with %d cells" %
                      (modelCls.name, self.numCells))
        elif len(models) > 1:
            log.debug("Multiple models match: %s" %
                      ", ".join(modelCls.name for modelCls in models))
            try:
                modelCls = next(cls for cls in models
                                if cls.numCells == self.numCells)
                log.debug(
                    "There is an exact match out of multiple models, %s found with %d cells"
                    % (modelCls.name, self.numCells))
            except StopIteration:
                log.debugWarning(
                    "No exact model match found for the reported %d cells display"
                    % self.numCells)
                try:
                    modelCls = next(cls for cls in models if not cls.numCells)
                except StopIteration:
                    modelCls = Model
        if modelCls:
            self._model = modelCls()

    def _handlePacket(self, packet):
        mode = packet[1]
        if mode == "\x00":  # Cursor routing
            routingIndex = ord(packet[3])
            try:
                inputCore.manager.executeGesture(
                    RoutingInputGesture(routingIndex))
            except inputCore.NoInputGestureAction:
                pass
        elif mode == "\x01":  # Braille input or function key
            if not self._model:
                return
            _keys = sum(ord(packet[4 + i]) << (i * 8) for i in xrange(4))
            keys = set()
            for keyHex in self._model.keys:
                if _keys & keyHex:
                    # This key is pressed
                    _keys -= keyHex
                    keys.add(keyHex)
                    if _keys == 0:
                        break
            if _keys:
                log.error("Unknown key(s) 0x%x received from Hims display" %
                          _keys)
                return
            try:
                inputCore.manager.executeGesture(
                    KeyInputGesture(self._model, keys))
            except inputCore.NoInputGestureAction:
                pass
        elif mode == "\x02":  # Cell count
            self.numCells = ord(packet[3])

    def _onReceive(self, data):
        if self.isBulk:
            # data contains the entire packet.
            stream = StringIO(data)
            firstByte = data[0]
            stream.seek(1)
        else:
            firstByte = data
            # data only contained the first byte. Read the rest from the device.
            stream = self._dev
        if firstByte == "\x1c":
            # A device is identifying itself
            deviceId = stream.read(2)
            # When a device identifies itself, the packets ends with 0x1f
            assert stream.read(1) == "\x1f"
            self._handleIdentification(deviceId)
        elif firstByte == "\xfa":
            # Command packets are ten bytes long
            packet = firstByte + stream.read(9)
            assert packet[2] == "\x01"  # Fixed value
            checksum = packet[8]
            assert packet[9] == "\xfb"  # Command End
            assert (chr(sum(ord(c) for c in packet[0:8] + packet[9])
                        & 0xff) == checksum)
            self._handlePacket(packet)
        else:
            log.debug("Unknown first byte received: 0x%x" % ord(firstByte))
            return

    def _sendPacket(self, type, mode, data1, data2=""):
        packetLength = 2 + 1 + 1 + 2 + len(data1) + 1 + 1 + 2 + len(
            data2) + 1 + 4 + 1 + 2
        # Construct the packet
        packet = [
            # Packet start
            type * 2,
            # Mode
            mode,  # Always "\x01" according to the spec
            # Data block 1 start
            "\xf0",
            # Data block 1 length
            chr((len(data1) >> 0) & 0xff),
            chr((len(data1) >> 8) & 0xff),
            # Data block 1
            data1,
            # Data block 1 end
            "\xf1",
            # Data block 2 is currently not used, but it is part of the spec
            # Data block 2 start
            "\xf2",
            # Data block 1 length
            chr((len(data2) >> 0) & 0xff),
            chr((len(data2) >> 8) & 0xff),
            # Data block 2
            data2,
            # Data block 2 end
            "\xf3",
            # Reserved bytes
            "\x00" * 4,
            # Reserved space for checksum
            "\x00",
            # Packet end
            "\xfd" * 2,
        ]
        packetStrWithoutCheksum = "".join(s for s in packet)
        packet[-2] = chr(sum(ord(c) for c in packetStrWithoutCheksum) & 0xff)
        packetStrWithCheksum = "".join(s for s in packet)
        assert (len(packetStrWithCheksum) == packetLength)
        self._dev.write(packetStrWithCheksum)

    def terminate(self):
        try:
            super(BrailleDisplayDriver, self).terminate()
        finally:
            # We must sleep before closing the port as not doing this can leave the display in a bad state where it can not be re-initialized.
            time.sleep(self.timeout)
            # Make sure the device gets closed.
            # If it doesn't, we may not be able to re-open it later.
            self._dev.close()

    gestureMap = inputCore.GlobalGestureMap({
        "globalCommands.GlobalCommands": {
            "braille_routeTo": ("br(hims):routing", ),
            "braille_scrollBack": (
                "br(hims):leftSideScrollUp",
                "br(hims):rightSideScrollUp",
                "br(hims):leftSideScroll",
            ),
            "braille_scrollForward": (
                "br(hims):leftSideScrollDown",
                "br(hims):rightSideScrollDown",
                "br(hims):rightSideScroll",
            ),
            "braille_previousLine":
            ("br(hims):leftSideScrollUp+rightSideScrollUp", ),
            "braille_nextLine":
            ("br(hims):leftSideScrollDown+rightSideScrollDown", ),
            "review_previousLine": ("br(hims):rightSideUpArrow", ),
            "review_nextLine": ("br(hims):rightSideDownArrow", ),
            "review_previousCharacter": ("br(hims):rightSideLeftArrow", ),
            "review_nextCharacter": ("br(hims):rightSideRightArrow", ),
            "braille_toFocus": (
                "br(hims):leftSideScrollUp+leftSideScrollDown",
                "br(hims):rightSideScrollUp+rightSideScrollDown",
                "br(hims):leftSideScroll+rightSideScroll",
            ),
            "kb:control": (
                "br(hims.smartbeetle):f1",
                "br(hims.brailleedge):f3",
            ),
            "kb:windows": (
                "br(hims.smartbeetle):f2",
                "br(hims):f7",
            ),
            "kb:alt": (
                "br(hims):dot1+dot3+dot4+space",
                "br(hims.smartbeetle):f3",
                "br(hims):f2",
                "br(hims.brailleedge):f4",
            ),
            "kb:shift": ("br(hims):f5", ),
            "kb:insert": (
                "br(hims):dot2+dot4+space",
                "br(hims):f6",
            ),
            "kb:applications": (
                "br(hims):dot1+dot2+dot3+dot4+space",
                "br(hims):f8",
            ),
            "kb:capsLock": ("br(hims):dot1+dot3+dot6+space", ),
            "kb:tab": (
                "br(hims):dot4+dot5+space",
                "br(hims):f3",
                "br(hims.brailleedge):f2",
            ),
            "kb:shift+alt+tab": ("br(hims):f2+f3+f1", ),
            "kb:alt+tab": ("br(hims):f2+f3", ),
            "kb:shift+tab": ("br(hims):dot1+dot2+space", ),
            "kb:end": ("br(hims):dot4+dot6+space", ),
            "kb:control+end": ("br(hims):dot4+dot5+dot6+space", ),
            "kb:home": (
                "br(hims):dot1+dot3+space",
                "br(hims.smartbeetle):f4",
            ),
            "kb:control+home": ("br(hims):dot1+dot2+dot3+space", ),
            "kb:alt+f4": ("br(hims):dot1+dot3+dot5+dot6+space", ),
            "kb:leftArrow": (
                "br(hims):dot3+space",
                "br(hims):leftSideLeftArrow",
            ),
            "kb:control+shift+leftArrow": ("br(hims):dot2+dot8+space+f1", ),
            "kb:control+leftArrow": ("br(hims):dot2+space", ),
            "kb:shift+alt+leftArrow": ("br(hims):dot2+dot7+f1", ),
            "kb:alt+leftArrow": ("br(hims):dot2+dot7", ),
            "kb:rightArrow": (
                "br(hims):dot6+space",
                "br(hims):leftSideRightArrow",
            ),
            "kb:control+shift+rightArrow": ("br(hims):dot5+dot8+space+f1", ),
            "kb:control+rightArrow": ("br(hims):dot5+space", ),
            "kb:shift+alt+rightArrow": ("br(hims):dot5+dot7+f1", ),
            "kb:alt+rightArrow": ("br(hims):dot5+dot7", ),
            "kb:pageUp": ("br(hims):dot1+dot2+dot6+space", ),
            "kb:control+pageUp": ("br(hims):dot1+dot2+dot6+dot8+space", ),
            "kb:upArrow": (
                "br(hims):dot1+space",
                "br(hims):leftSideUpArrow",
            ),
            "kb:control+shift+upArrow": ("br(hims):dot2+dot3+dot8+space+f1", ),
            "kb:control+upArrow": ("br(hims):dot2+dot3+space", ),
            "kb:shift+alt+upArrow": ("br(hims):dot2+dot3+dot7+f1", ),
            "kb:alt+upArrow": ("br(hims):dot2+dot3+dot7", ),
            "kb:shift+upArrow": ("br(hims):leftSideScrollDown+space", ),
            "kb:pageDown": ("br(hims):dot3+dot4+dot5+space", ),
            "kb:control+pageDown": ("br(hims):dot3+dot4+dot5+dot8+space", ),
            "kb:downArrow": (
                "br(hims):dot4+space",
                "br(hims):leftSideDownArrow",
            ),
            "kb:control+shift+downArrow":
            ("br(hims):dot5+dot6+dot8+space+f1", ),
            "kb:control+downArrow": ("br(hims):dot5+dot6+space", ),
            "kb:shift+alt+downArrow": ("br(hims):dot5+dot6+dot7+f1", ),
            "kb:alt+downArrow": ("br(hims):dot5+dot6+dot7", ),
            "kb:shift+downArrow": ("br(hims):space+rightSideScrollDown", ),
            "kb:escape": (
                "br(hims):dot1+dot5+space",
                "br(hims):f4",
                "br(hims.brailleedge):f1",
            ),
            "kb:delete": (
                "br(hims):dot1+dot3+dot5+space",
                "br(hims):dot1+dot4+dot5+space",
            ),
            "kb:f1": ("br(hims):dot1+dot2+dot5+space", ),
            "kb:f3": ("br(hims):dot1+dot2+dot4+dot8", ),
            "kb:f4": ("br(hims):dot7+f3", ),
            "kb:windows+b": ("br(hims):dot1+dot2+f1", ),
            "kb:windows+d": ("br(hims):dot1+dot4+dot5+f1", ),
            "kb:control+insert": ("br(hims.smartbeetle):f1+rightSideScroll", ),
            "kb:alt+insert": ("br(hims.smartbeetle):f3+rightSideScroll", ),
        }
    })
Пример #30
0
class BrailleDisplayDriver(braille.BrailleDisplayDriver):
	name = "seika"
	# Translators: Names of braille displays.
	description = _("Seika Braille Displays")
	numCells = 0

	@classmethod
	def check(cls):
		return True

	def __init__(self):
		super(BrailleDisplayDriver, self).__init__()
		for portInfo in hwPortUtils.listComPorts(onlyAvailable=True):
			port = portInfo["port"]
			hwID = portInfo["hardwareID"]

			# Seika USB to Serial, in XP it is lowercase, in Win7 uppercase
			if not hwID.upper().startswith(r"USB\VID_10C4&PID_EA60"):
				continue

			# At this point, a port bound to this display has been found.
			# Try talking to the display.
			try:
				self._ser = serial.Serial(
					port,
					baudrate=BAUDRATE,
					timeout=TIMEOUT,
					writeTimeout=TIMEOUT,
					parity=serial.PARITY_ODD,
					bytesize=serial.EIGHTBITS,
					stopbits=serial.STOPBITS_ONE
				)
			except serial.SerialException:
				continue

			log.debug(f"serial port open {port}")

			# get the version information
			VERSION_INFO_REQUEST = b"\x1C"
			self._ser.write(BUF_START + VERSION_INFO_REQUEST)
			self._ser.flush()

			# Read out the input buffer
			versionS = self._ser.read(13)
			log.debug(f"receive {versionS}")

			if versionS.startswith(b"seika80"):
				log.info(f"Found Seika80 connected via {port} Version {versionS}")
				self.numCells = 80
				self._maxCellRead = 20
				# data header for seika 80
				self.sendHeader = (BUF_START + b"s80").ljust(8, b"\x00")
				break

			elif versionS.startswith(b"seika3"):
				log.info(f"Found Seika3/5 connected via {port} Version {versionS}")
				self.numCells = 40
				self._maxCellRead = 10
				# data header for v3, v5
				self.sendHeader = (BUF_START + b"seika").ljust(8, b"\x00")
				break

			# is it a old Seika3?
			log.debug("test if it is a old Seika3")
			LEGACY_VERSION_REQUEST = b"\x0A"
			self._ser.write(BUF_START + LEGACY_VERSION_REQUEST)
			self._ser.flush()

			# Read out the input buffer
			versionS = self._ser.read(12)
			log.debug(f"receive {versionS}")
			if versionS.startswith(prefix=(
				b'\x00\x05(\x08v5.0\x01\x01\x01\x01',
				b'\x00\x05(\x08seika\x00'
			)):
				log.info(f"Found Seika3 old Version connected via {port} Version {versionS}")
				self.numCells = 40
				self._maxCellRead = 10
				self.sendHeader = BUF_START + b"\x04\x00\x63\x00\x50\x00"
				break
			self._ser.close()
		else:
			raise RuntimeError("No SEIKA40/80 display found")
		self._readTimer = wx.PyTimer(self.handleResponses)
		self._readTimer.Start(READ_INTERVAL)

	def terminate(self):
		try:
			super(BrailleDisplayDriver, self).terminate()
			self._readTimer.Stop()
			self._readTimer = None
		finally:
			self._ser.close()

	def display(self, cells: List[int]):
		# every transmitted line consists of the preamble 'sendHeader' and the Cells
		if 80 == self.numCells:
			lineBytes: bytes = self.sendHeader + bytes(cells)
		elif 40 == self.numCells:
			cellData = (b"\x00" + intToByte(cell) for cell in cells)
			lineBytes = self.sendHeader + b"".join(cellData)
		else:
			log.error("Unsupported cell count for seika braille device")
			return
		self._ser.write(lineBytes)

	def handleResponses(self):
		if not self._ser.in_waiting:
			return

		chars: bytes = self._ser.read(2)
		isCursorRoutingBlock = not chars[0] & 0x60
		
		if not isCursorRoutingBlock:  # normal key
			self._handleNormalKey(chars)
		else:  # Cursor Routing Block
			chars: bytes = self._ser.read(self._maxCellRead)
			self._handleCursorRoutingBlock(chars)

	def _handleCursorRoutingBlock(self, chars: bytes) -> None:
		key = 0
		i = 0
		k = self._maxCellRead // 2
		while i < k:
			j = 0
			while j < 8:
				if chars[5 + i] & (1 << j):
					key = i * 8 + j + 1
					break
				j += 1
			i += 1
		if key:  # Routing key is pressed
			try:
				inputCore.manager.executeGesture(InputGestureRouting(key - 1))
			except inputCore.NoInputGestureAction:
				log.debug("No Action for routing command")
				pass

	def _handleNormalKey(self, chars: bytes) -> None:
		keys = set()
		if chars[0] & 1:  # LEFT
			keys.add("left")
		if chars[0] & 4:  # RIGHT
			keys.add("right")
		if chars[1] & 2:  # B1
			keys.add("b1")
		if chars[1] & 8:
			keys.add("b2")
		if chars[1] & 16:
			keys.add("b3")
		if chars[0] & 16:
			keys.add("b4")
		if chars[0] & 8:
			keys.add("b5")
		if chars[0] & 2:
			keys.add("b6")
		data = "+".join(keys)
		try:
			inputCore.manager.executeGesture(InputGestureKeys(data))
		except inputCore.NoInputGestureAction:
			log.debug(f"No Action for keys {data}")
			pass

	gestureMap = inputCore.GlobalGestureMap({
		"globalCommands.GlobalCommands": {
			"braille_scrollBack": ("br(seika):left",),
			"braille_scrollForward": ("br(seika):right",),
			"braille_previousLine": ("br(seika):b3",),
			"braille_nextLine": ("br(seika):b4",),
			"braille_toggleTether": ("br(seika):b5",),
			"sayAll": ("br(seika):b6",),
			"kb:tab": ("br(seika):b1",),
			"kb:shift+tab": ("br(seika):b2",),
			"kb:alt+tab": ("br(seika):b1+b2",),
			"showGui": ("br(seika):left+right",),
			"braille_routeTo": ("br(seika):routing",),
		},
	})