diff --git a/src/main/python/keyboard_comm.py b/src/main/python/keyboard_comm.py index 4b26b10..bea5388 100644 --- a/src/main/python/keyboard_comm.py +++ b/src/main/python/keyboard_comm.py @@ -51,7 +51,7 @@ class Keyboard: self.layout = dict() self.encoder_layout = dict() self.rows = self.cols = self.layers = 0 - self.layouts = None + self.layout_labels = None self.layout_options = -1 self.keys = [] self.encoders = [] @@ -112,7 +112,7 @@ class Keyboard: vial = payload["vial"] self.vibl = vial.get("vibl", False) - self.layouts = payload.get("layouts") + self.layout_labels = payload["layouts"].get("labels") self.rows = payload["matrix"]["rows"] self.cols = payload["matrix"]["cols"] @@ -175,7 +175,7 @@ class Keyboard: self.encoder_layout[(layer, idx, 0)] = struct.unpack(">H", data[0:2])[0] self.encoder_layout[(layer, idx, 1)] = struct.unpack(">H", data[2:4])[0] - if self.layouts: + if self.layout_labels: data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_GET_KEYBOARD_VALUE, VIA_LAYOUT_OPTIONS), retries=20) self.layout_options = struct.unpack(">I", data[2:6])[0] @@ -356,3 +356,71 @@ class Keyboard: return self.usb_send(self.dev, struct.pack("BB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_LOCK), retries=20) + + +class DummyKeyboard(Keyboard): + + def reload_layers(self): + self.layers = 4 + + def reload_keymap(self): + for layer in range(self.layers): + for row, col in self.rowcol.keys(): + self.layout[(layer, row, col)] = 0 + + for layer in range(self.layers): + for idx in self.encoderpos: + self.encoder_layout[(layer, idx, 0)] = 0 + self.encoder_layout[(layer, idx, 1)] = 0 + + if self.layout_labels: + self.layout_options = 0 + + def reload_macros(self): + self.macro_count = 16 + self.macro_memory = 900 + + self.macro = b"\x00" * self.macro_count + + def set_key(self, layer, row, col, code): + if code < 0: + return + self.layout[(layer, row, col)] = code + + def set_encoder(self, layer, index, direction, code): + if code < 0: + return + self.encoder_layout[(layer, index, direction)] = code + + def set_layout_options(self, options): + if self.layout_options != -1 and self.layout_options != options: + self.layout_options = options + + def set_macro(self, data): + if len(data) > self.macro_memory: + raise RuntimeError("the macro is too big: got {} max {}".format(len(data), self.macro_memory)) + self.macro = data + + def reset(self): + pass + + def get_uid(self): + return b"\x00" * 8 + + def get_unlock_status(self): + return 1 + + def get_unlock_in_progress(self): + return 0 + + def get_unlock_keys(self): + return [] + + def unlock_start(self): + return + + def unlock_poll(self): + return b"" + + def lock(self): + return diff --git a/src/main/python/layout_editor.py b/src/main/python/layout_editor.py index 7851c66..9473dea 100644 --- a/src/main/python/layout_editor.py +++ b/src/main/python/layout_editor.py @@ -112,7 +112,7 @@ class LayoutEditor(BasicEditor): choice.delete() self.choices = [] - for item in device.keyboard.layouts["labels"]: + for item in device.keyboard.layout_labels: if isinstance(item, str): choice = BooleanChoice(self.on_changed, self.container, item) else: @@ -124,7 +124,7 @@ class LayoutEditor(BasicEditor): self.blockSignals(False) def valid(self): - return isinstance(self.device, VialKeyboard) and "labels" in self.device.keyboard.layouts + return isinstance(self.device, VialKeyboard) and self.device.keyboard.layout_labels def pack(self): if not self.choices: diff --git a/src/main/python/main_window.py b/src/main/python/main_window.py index 78e39a9..e26b647 100644 --- a/src/main/python/main_window.py +++ b/src/main/python/main_window.py @@ -96,6 +96,9 @@ class MainWindow(QMainWindow): download_via_stack_act = QAction(tr("MenuFile", "Download VIA definitions"), self) download_via_stack_act.triggered.connect(self.load_via_stack_json) + load_dummy_act = QAction(tr("MenuFile", "Load dummy JSON..."), self) + load_dummy_act.triggered.connect(self.on_load_dummy) + exit_act = QAction(tr("MenuFile", "Exit"), self) exit_act.setShortcut("Ctrl+Q") exit_act.triggered.connect(qApp.exit) @@ -106,6 +109,7 @@ class MainWindow(QMainWindow): file_menu.addSeparator() file_menu.addAction(sideload_json_act) file_menu.addAction(download_via_stack_act) + file_menu.addAction(load_dummy_act) file_menu.addSeparator() file_menu.addAction(exit_act) @@ -243,6 +247,18 @@ class MainWindow(QMainWindow): self.sideload_pid = int(self.sideload_json["productId"], 16) self.on_click_refresh() + def on_load_dummy(self): + dialog = QFileDialog() + dialog.setDefaultSuffix("json") + dialog.setAcceptMode(QFileDialog.AcceptOpen) + dialog.setNameFilters(["VIA layout JSON (*.json)"]) + if dialog.exec_() == QDialog.Accepted: + with open(dialog.selectedFiles()[0], "rb") as inf: + data = inf.read() + self.sideload_json = json.loads(data) + self.sideload_vid = self.sideload_pid = 0 + self.on_click_refresh() + def lock_ui(self): self.tabs.setEnabled(False) self.combobox_devices.setEnabled(False) diff --git a/src/main/python/util.py b/src/main/python/util.py index f22b40b..ed23ae6 100644 --- a/src/main/python/util.py +++ b/src/main/python/util.py @@ -53,7 +53,7 @@ def is_rawhid(dev): def find_vial_devices(via_stack_json, sideload_vid=None, sideload_pid=None): - from vial_device import VialBootloader, VialKeyboard + from vial_device import VialBootloader, VialKeyboard, VialDummyKeyboard filtered = [] for dev in hid.enumerate(): @@ -66,6 +66,9 @@ def find_vial_devices(via_stack_json, sideload_vid=None, sideload_pid=None): elif str(dev["vendor_id"] * 65536 + dev["product_id"]) in via_stack_json["definitions"] and is_rawhid(dev): filtered.append(VialKeyboard(dev, via_stack=True)) + if sideload_vid == sideload_pid == 0: + filtered.append(VialDummyKeyboard()) + return filtered diff --git a/src/main/python/vial_device.py b/src/main/python/vial_device.py index 83cc1b9..318f447 100644 --- a/src/main/python/vial_device.py +++ b/src/main/python/vial_device.py @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-or-later from hidproxy import hid -from keyboard_comm import Keyboard +from keyboard_comm import Keyboard, DummyKeyboard from util import MSG_LEN, pad_for_vibl @@ -74,3 +74,22 @@ class VialBootloader(VialDevice): data = self.recv(8, timeout_ms=500) super().close() return data + + +class VialDummyKeyboard(VialKeyboard): + + def __init__(self): + self.sideload = True + + def open(self, override_json=None): + self.keyboard = DummyKeyboard(None, usb_send=self.raise_usb_send) + self.keyboard.reload(override_json) + + def title(self): + return "[Dummy Keyboard]" + + def raise_usb_send(self, *args, **kwargs): + raise RuntimeError("usb_send - should not be called!") + + def close(self): + pass