10aab84

Pinchord · pinchord.git · Tickets · Changelog

Branches > serial-mods > 10aab84 > files

Add copy of Plover HID plugin

Grahp, 2026-10-26

Commit: 10aab84c5a18cd2c0c64e362cf59026079daa81c
Parent: cc501de

7 changed files:

MANIFEST.in modified (view file)

@@ -1 +1,2 @@ include plover_pinchord_31/dictionaries/*+include plover_pinchord_machine_hid/LICENSE

plover_pinchord_31/system.py modified (view file)

@@ -99,5 +99,31 @@     "-*": ("-S"),     "-e": ("-D", "-Z"),     "-s": ("X1")+  },+  "Pinchord Plover HID": {+    "#-": ("S1-"),+    "+-": ("S2-"),+    "T-": ("T-"),+    "S-": ("K-"),+    "P-": ("P-"),+    "W-": ("W-"),+    "H-": ("H-"),+    "R-": ("R-"),+    "&-": ("*1", "*2"),+    "A": ("A-"),+    "O": ("O-"),+    "I": ("-E"),+    "U": ("-U"),+    "^": ("*3", "*4"),+    "-L": ("-F"),+    "-R": ("-R"),+    "-P": ("-P"),+    "-K": ("-B"),+    "-F": ("-L"),+    "-S": ("-G"),+    "-T": ("-T"),+    "-*": ("-S"),+    "-e": ("-D", "-Z"),+    "-s": ("X1")   } }

plover_pinchord_machine_hid/LICENSE added (view file)

@@ -0,0 +1,21 @@+MIT License++Copyright (c) 2021 dnaq++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.

plover_pinchord_machine_hid/__init__.py added (view file)

No content changes.

plover_pinchord_machine_hid/hid_option.py added (view file)

@@ -0,0 +1,93 @@+# Taken from https://github.com/dnaq/plover-machine-hid/blob/main/hid_option.py+# Copyright dnaq, see ./LICENSE++from copy import copy++from PySide6.QtCore import Signal+from PySide6.QtGui import QIntValidator+from PySide6.QtWidgets import (+    QGroupBox,+    QHBoxLayout,+    QVBoxLayout,+    QCheckBox,+    QLabel,+    QLineEdit,+)+++class HidOption(QGroupBox):++    valueChanged = Signal('QVariant')++    def checkbox(self, name, label, tooltip):+        checkbox = QCheckBox(label)+        checkbox.setToolTip(tooltip)++        def on_changed(value):+            self._value[name] = value+            self.valueChanged.emit(self._value)++        checkbox.stateChanged.connect(on_changed)+        self.setters.append(lambda value: checkbox.setChecked(value[name]))+        return checkbox++    def number(self, name, label, tooltip, minimum, maximum):+        edit = QLineEdit("")+        edit.setToolTip(tooltip)+        edit.setValidator(QIntValidator(minimum, maximum, edit))++        def on_changed(value):+            self._value[name] = int(value)+            self.valueChanged.emit(self._value)++        edit.textChanged.connect(on_changed)+        row = QHBoxLayout()+        row.addWidget(QLabel(label))+        row.addWidget(edit)+        self.setters.append(lambda value: edit.setText(str(value[name])))+        return row++    def __init__(self):+        super().__init__()+        self.setters = []+        self._value = {}+        vbox = QVBoxLayout()+        vbox.addWidget(+            self.checkbox(+                name="first_up_chord_send",+                label="Send chord on first key release",+                tooltip="When the first key in a chord is released, the chord is sent.\n"+                "If the key is pressed and released again, another chord is sent.",+            )+        )+        vbox.addWidget(+            self.checkbox(+                name="double_tap_repeat",+                label="Double tap to repeat",+                tooltip="Tap and then hold a chord to send it repeatedly.",+            )+        )+        vbox.addLayout(+            self.number(+                name="repeat_delay_ms",+                label="Repeat delay (ms)",+                tooltip="Delay before chord starts repeating.",+                minimum=10,+                maximum=10000,+            )+        )+        vbox.addLayout(+            self.number(+                name="repeat_interval_ms",+                label="Repeat interval (ms)",+                tooltip="Interval between chord repetitions.",+                minimum=10,+                maximum=10000,+            )+        )+        self.setLayout(vbox)++    def setValue(self, value):+        self._value = copy(value)+        for setter in self.setters:+            setter(value)

plover_pinchord_machine_hid/plover_pinchord_machine_hid.py added (view file)

@@ -0,0 +1,174 @@+'''+A plover machine plugin for supporting the Plover HID protocol.++This protocol is a simple HID-based protocol that sends the current state+of the steno machine every time that state changes.++See the README for more details on the protocol.++The order of the buttons (from left to right) is the same as in `KEYS_LAYOUT`.+Most buttons have the same names as in GeminiPR, except for the extra buttons+which are called X1-X26.+'''++# Taken from https://github.com/dnaq/plover-machine-hid/blob/main/plover_machine_hid.py+# Copyright dnaq, see ./LICENSE++from plover.machine.base import ThreadedStenotypeBase+from plover import log+from plover.misc import boolean++import hid+import platform+import time++# This is a hack to not open the hid device in exclusive mode on+# darwin, if the version of hidapi installed is current enough+if platform.system() == "Darwin":+    import ctypes+    try:+        hid.hidapi.hid_darwin_set_open_exclusive.argtypes = (ctypes.c_int, )+        hid.hidapi.hid_darwin_set_open_exclusive.restype = None+        hid.hidapi.hid_darwin_set_open_exclusive(0)+    except AttributeError as e:+        log.error("hidapi < 0.12 in use, plover-pinchord-hid will not work correctly")++USAGE_PAGE: int = 0xFF50+USAGE: int = 0x4C56++N_LEVERS: int = 64++# A simple report contains the report id 1 and one bit+# for each of the 64 buttons in the report.+SIMPLE_REPORT_TYPE: int = 0x01+SIMPLE_REPORT_LEN: int = N_LEVERS // 8++class InvalidReport(Exception):+    pass++STENO_KEY_CHART = ("S1-", "T-", "K-", "P-", "W-", "H-",+                   "R-", "A-", "O-", "*1", "-E", "-U",+                   "-F", "-R", "-P", "-B", "-L", "-G",+                   "-T", "-S", "-D", "-Z", "#1",+                   "S2-", "*2", "*3", "*4", "#2", "#3",+                   "#4", "#5", "#6", "#7", "#8", "#9",+                   "#A", "#B", "#C", "X1", "X2", "X3",+                   "X4", "X5", "X6", "X7", "X8", "X9",+                   "X10", "X11", "X12", "X13", "X14", "X15",+                   "X16", "X17", "X18", "X19", "X20", "X21",+                   "X22", "X23", "X24", "X25", "X26")++class HidMachine(ThreadedStenotypeBase):+    KEYS_LAYOUT: str = '''+        #1 #2 #3 #4 #5 #6 #7 #8 #9 #A #B #C+        S1- T- P- H- *1 *3 -F -P -L -T -D+        S2- K- W- R- *2 *4 -R -B -G -S -Z+               A- O-       -E -U+     X1  X2  X3  X4  X5  X6  X7  X8  X9  X10+     X11 X12 X13 X14 X15 X16 X17 X18 X19 X20+     X21 X22 X23 X24 X25 X26+    '''+    def __init__(self, params):+        super().__init__()+        self._params = params+        self._hid = None++    def _parse(self, report):+        # The first byte is the report id, and due to idiosynchrasies+        # in how HID-apis work on different operating system we can't+        # map the report id to the contents in a good way, so we force+        # compliant devices to always use a report id of 0x50 ('P').+        if len(report) > SIMPLE_REPORT_LEN and report[0] == 0x50:+            return int.from_bytes(report[1:SIMPLE_REPORT_LEN+1], 'big')+        else:+            raise InvalidReport()++    def send(self, keystate):+        steno_actions = self.keymap.keys_to_actions(+            [key for i, key in enumerate(STENO_KEY_CHART) if keystate >> (63 - i) & 1]+        )+        if steno_actions:+            self._notify(steno_actions)++    def run(self):+        self._ready()+        keystate = 0+        current = 0+        last_sent = 0+        press_started = time.time()+        sent_first_up = False

Show all 175 lines

setup.cfg modified (view file)

@@ -19,9 +19,11 @@ install_requires = 	plover>=4.0.0 	plover-python-dictionary+	hid include_package_data = True packages = 	plover_pinchord_31+	plover_pinchord_machine_hid  # 'Pinchord' is the latest version for compatibility [options.entry_points]@@ -29,3 +31,7 @@   Pinchord = plover_pinchord_31.system plover.extension =   Pinchord = plover_pinchord_31.extension:Extension+plover.machine =+  Pinchord Plover HID = plover_pinchord_machine_hid.plover_pinchord_machine_hid:HidMachine+plover.gui.qt.machine_option =+  plover_pinchord_machine_hid.plover_pinchord_machine_hid:HidMachine = plover_pinchord_machine_hid.hid_option:HidOption