split low-level communication away from gui
parent
36eb6b2fd4
commit
40ec4fa95a
|
|
@ -0,0 +1,76 @@
|
||||||
|
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||||
|
|
||||||
|
import struct
|
||||||
|
import json
|
||||||
|
import lzma
|
||||||
|
|
||||||
|
from kle_serial import Serial as KleSerial
|
||||||
|
from util import MSG_LEN, hid_send
|
||||||
|
|
||||||
|
|
||||||
|
class Keyboard:
|
||||||
|
""" Low-level communication with a vial-enabled keyboard """
|
||||||
|
|
||||||
|
def __init__(self, dev):
|
||||||
|
self.dev = dev
|
||||||
|
|
||||||
|
def reload(self):
|
||||||
|
""" Load information about the keyboard: number of layers, physical key layout """
|
||||||
|
|
||||||
|
self.rowcol = set()
|
||||||
|
self.layout = dict()
|
||||||
|
|
||||||
|
self.reload_layers()
|
||||||
|
self.reload_layout()
|
||||||
|
self.reload_keymap()
|
||||||
|
|
||||||
|
def reload_layers(self):
|
||||||
|
""" Get how many layers the keyboard has """
|
||||||
|
|
||||||
|
self.layers = hid_send(self.dev, b"\x11")[1]
|
||||||
|
|
||||||
|
def reload_layout(self):
|
||||||
|
""" Requests layout data from the current device """
|
||||||
|
|
||||||
|
# get the size
|
||||||
|
data = hid_send(self.dev, b"\xFE\x01")
|
||||||
|
sz = struct.unpack("<I", data[0:4])[0]
|
||||||
|
|
||||||
|
# get the payload
|
||||||
|
payload = b""
|
||||||
|
block = 0
|
||||||
|
while sz > 0:
|
||||||
|
data = hid_send(self.dev, b"\xFE\x02" + struct.pack("<I", block))
|
||||||
|
if sz < MSG_LEN:
|
||||||
|
data = data[:sz]
|
||||||
|
payload += data
|
||||||
|
block += 1
|
||||||
|
sz -= MSG_LEN
|
||||||
|
|
||||||
|
payload = json.loads(lzma.decompress(payload))
|
||||||
|
serial = KleSerial()
|
||||||
|
kb = serial.deserialize(payload["layouts"]["keymap"])
|
||||||
|
|
||||||
|
self.keys = kb.keys
|
||||||
|
|
||||||
|
for key in self.keys:
|
||||||
|
key.row = key.col = None
|
||||||
|
if key.labels[0] and "," in key.labels[0]:
|
||||||
|
row, col = key.labels[0].split(",")
|
||||||
|
row, col = int(row), int(col)
|
||||||
|
key.row = row
|
||||||
|
key.col = col
|
||||||
|
self.rowcol.add((row, col))
|
||||||
|
|
||||||
|
def reload_keymap(self):
|
||||||
|
""" Load current key mapping from the keyboard """
|
||||||
|
|
||||||
|
for layer in range(self.layers):
|
||||||
|
for row, col in self.rowcol:
|
||||||
|
data = hid_send(self.dev, b"\x04" + struct.pack("<BBB", layer, row, col))
|
||||||
|
keycode = struct.unpack(">H", data[4:6])[0]
|
||||||
|
self.layout[(layer, row, col)] = keycode
|
||||||
|
|
||||||
|
def set_key(self, layer, row, col, code):
|
||||||
|
hid_send(self.dev, struct.pack(">BBBBH", 5, layer, row, col, code))
|
||||||
|
self.layout[(layer, row, col)] = code
|
||||||
|
|
@ -11,10 +11,10 @@ import lzma
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
|
||||||
from flowlayout import FlowLayout
|
from flowlayout import FlowLayout
|
||||||
from util import tr, find_vial_keyboards, open_device, hid_send, MSG_LEN
|
from util import tr, find_vial_keyboards, open_device
|
||||||
from kle_serial import Serial as KleSerial
|
|
||||||
from clickable_label import ClickableLabel
|
from clickable_label import ClickableLabel
|
||||||
from keycodes import keycode_label, keycode_tooltip, recreate_layer_keycodes, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_SPECIAL
|
from keycodes import keycode_label, keycode_tooltip, recreate_layer_keycodes, KEYCODES_BASIC, KEYCODES_ISO, KEYCODES_MACRO, KEYCODES_LAYERS, KEYCODES_SPECIAL
|
||||||
|
from keyboard import Keyboard
|
||||||
|
|
||||||
|
|
||||||
class TabbedKeycodes(QTabWidget):
|
class TabbedKeycodes(QTabWidget):
|
||||||
|
|
@ -95,15 +95,15 @@ class KeyboardContainer(QWidget):
|
||||||
self.keys = []
|
self.keys = []
|
||||||
self.layer_labels = []
|
self.layer_labels = []
|
||||||
self.rowcol = defaultdict(list)
|
self.rowcol = defaultdict(list)
|
||||||
self.layout = dict()
|
|
||||||
self.selected_key = None
|
self.selected_key = None
|
||||||
self.selected_row = -1
|
self.selected_row = -1
|
||||||
self.selected_col = -1
|
self.selected_col = -1
|
||||||
|
|
||||||
def rebuild_layers(self, dev):
|
def rebuild_layers(self):
|
||||||
self.layers = hid_send(dev, b"\x11")[1]
|
self.layers = self.keyboard.layers
|
||||||
self.number_layers_changed.emit()
|
self.number_layers_changed.emit()
|
||||||
|
|
||||||
|
# delete old layer labels
|
||||||
for label in self.layer_labels:
|
for label in self.layer_labels:
|
||||||
label.deleteLater()
|
label.deleteLater()
|
||||||
self.layer_labels = []
|
self.layer_labels = []
|
||||||
|
|
@ -116,40 +116,28 @@ class KeyboardContainer(QWidget):
|
||||||
self.layout_layers.addWidget(label)
|
self.layout_layers.addWidget(label)
|
||||||
self.layer_labels.append(label)
|
self.layer_labels.append(label)
|
||||||
|
|
||||||
def rebuild_layout(self, dev):
|
def rebuild(self, keyboard):
|
||||||
""" Load current key mapping from the keyboard """
|
self.keyboard = keyboard
|
||||||
|
|
||||||
for layer in range(self.layers):
|
|
||||||
for row, col in self.rowcol.keys():
|
|
||||||
data = hid_send(dev, b"\x04" + struct.pack("<BBB", layer, row, col))
|
|
||||||
keycode = struct.unpack(">H", data[4:6])[0]
|
|
||||||
self.layout[(layer, row, col)] = keycode
|
|
||||||
|
|
||||||
def rebuild(self, dev, data):
|
|
||||||
# delete current layout
|
# delete current layout
|
||||||
for key in self.keys:
|
for key in self.keys:
|
||||||
key.deleteLater()
|
key.deleteLater()
|
||||||
self.keys = []
|
self.keys = []
|
||||||
|
|
||||||
# get number of layers
|
# get number of layers
|
||||||
self.rebuild_layers(dev)
|
self.rebuild_layers()
|
||||||
|
|
||||||
# prepare for fetching keymap
|
# prepare for fetching keymap
|
||||||
self.rowcol = defaultdict(list)
|
self.rowcol = defaultdict(list)
|
||||||
|
|
||||||
serial = KleSerial()
|
|
||||||
kb = serial.deserialize(data["layouts"]["keymap"])
|
|
||||||
|
|
||||||
max_w = max_h = 0
|
max_w = max_h = 0
|
||||||
|
|
||||||
for key in kb.keys:
|
for key in keyboard.keys:
|
||||||
widget = ClickableLabel()
|
widget = ClickableLabel()
|
||||||
widget.clicked.connect(lambda w=widget: self.select_key(w))
|
widget.clicked.connect(lambda w=widget: self.select_key(w))
|
||||||
|
|
||||||
if key.labels[0] and "," in key.labels[0]:
|
if key.row is not None:
|
||||||
row, col = key.labels[0].split(",")
|
self.rowcol[(key.row, key.col)].append(widget)
|
||||||
row, col = int(row), int(col)
|
|
||||||
self.rowcol[(row, col)].append(widget)
|
|
||||||
|
|
||||||
widget.setParent(self.container)
|
widget.setParent(self.container)
|
||||||
widget.setAlignment(Qt.AlignCenter)
|
widget.setAlignment(Qt.AlignCenter)
|
||||||
|
|
@ -169,7 +157,6 @@ class KeyboardContainer(QWidget):
|
||||||
self.keys.append(widget)
|
self.keys.append(widget)
|
||||||
|
|
||||||
self.container.setFixedSize(max_w, max_h)
|
self.container.setFixedSize(max_w, max_h)
|
||||||
self.rebuild_layout(dev)
|
|
||||||
self.current_layer = 0
|
self.current_layer = 0
|
||||||
self.refresh_layer_display()
|
self.refresh_layer_display()
|
||||||
|
|
||||||
|
|
@ -181,7 +168,7 @@ class KeyboardContainer(QWidget):
|
||||||
self.layer_labels[self.current_layer].setStyleSheet("border: 1px solid black; padding: 5px; background-color: black; color: white")
|
self.layer_labels[self.current_layer].setStyleSheet("border: 1px solid black; padding: 5px; background-color: black; color: white")
|
||||||
|
|
||||||
for (row, col), widgets in self.rowcol.items():
|
for (row, col), widgets in self.rowcol.items():
|
||||||
code = self.layout[(self.current_layer, row, col)]
|
code = self.keyboard.layout[(self.current_layer, row, col)]
|
||||||
text = keycode_label(code)
|
text = keycode_label(code)
|
||||||
tooltip = keycode_tooltip(code)
|
tooltip = keycode_tooltip(code)
|
||||||
for widget in widgets:
|
for widget in widgets:
|
||||||
|
|
@ -202,9 +189,7 @@ class KeyboardContainer(QWidget):
|
||||||
""" Change currently selected key to provided keycode """
|
""" Change currently selected key to provided keycode """
|
||||||
|
|
||||||
if self.selected_row >= 0 and self.selected_col >= 0:
|
if self.selected_row >= 0 and self.selected_col >= 0:
|
||||||
hid_send(self.dev, struct.pack(">BBBBH", 5, self.current_layer, self.selected_row, self.selected_col, keycode))
|
self.keyboard.set_key(self.current_layer, self.selected_row, self.selected_col, keycode)
|
||||||
self.layout[(self.current_layer, self.selected_row, self.selected_col)] = keycode
|
|
||||||
|
|
||||||
self.refresh_layer_display()
|
self.refresh_layer_display()
|
||||||
|
|
||||||
def select_key(self, widget):
|
def select_key(self, widget):
|
||||||
|
|
@ -262,33 +247,11 @@ class MainWindow(QWidget):
|
||||||
|
|
||||||
def on_device_selected(self):
|
def on_device_selected(self):
|
||||||
self.device = None
|
self.device = None
|
||||||
self.keyboard_container.dev = None
|
|
||||||
idx = self.combobox_devices.currentIndex()
|
idx = self.combobox_devices.currentIndex()
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
self.device = open_device(self.devices[idx])
|
keyboard = Keyboard(open_device(self.devices[idx]))
|
||||||
self.keyboard_container.dev = self.device
|
keyboard.reload()
|
||||||
self.reload_layout()
|
self.keyboard_container.rebuild(keyboard)
|
||||||
|
|
||||||
def reload_layout(self):
|
|
||||||
""" Requests layout data from the current device """
|
|
||||||
|
|
||||||
# get the size
|
|
||||||
data = hid_send(self.device, b"\xFE\x01")
|
|
||||||
sz = struct.unpack("<I", data[0:4])[0]
|
|
||||||
|
|
||||||
# get the payload
|
|
||||||
payload = b""
|
|
||||||
block = 0
|
|
||||||
while sz > 0:
|
|
||||||
data = hid_send(self.device, b"\xFE\x02" + struct.pack("<I", block))
|
|
||||||
if sz < MSG_LEN:
|
|
||||||
data = data[:sz]
|
|
||||||
payload += data
|
|
||||||
block += 1
|
|
||||||
sz -= MSG_LEN
|
|
||||||
|
|
||||||
payload = json.loads(lzma.decompress(payload))
|
|
||||||
self.keyboard_container.rebuild(self.device, payload)
|
|
||||||
|
|
||||||
def on_number_layers_changed(self):
|
def on_number_layers_changed(self):
|
||||||
recreate_layer_keycodes(self.keyboard_container.layers)
|
recreate_layer_keycodes(self.keyboard_container.layers)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue