Initial communication with keyboard
parent
8f1123ee77
commit
ed4673dfb8
130
g60.json
130
g60.json
|
|
@ -1,130 +0,0 @@
|
|||
{
|
||||
"name": "g60",
|
||||
"vendorId": "0xFEED",
|
||||
"productId": "0x6464",
|
||||
"lighting": "none",
|
||||
"matrix": {
|
||||
"rows": 5,
|
||||
"cols": 15
|
||||
},
|
||||
"layouts": {
|
||||
"keymap": [
|
||||
[
|
||||
"0,0",
|
||||
"0,1",
|
||||
"0,2",
|
||||
"0,3",
|
||||
"0,4",
|
||||
"0,5",
|
||||
"0,6",
|
||||
"0,7",
|
||||
"0,8",
|
||||
"0,9",
|
||||
"0,10",
|
||||
"0,11",
|
||||
"0,12",
|
||||
"0,13",
|
||||
"0,14"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"1,0",
|
||||
"1,2",
|
||||
"1,3",
|
||||
"1,4",
|
||||
"1,5",
|
||||
"1,6",
|
||||
"1,7",
|
||||
"1,8",
|
||||
"1,9",
|
||||
"1,10",
|
||||
"1,11",
|
||||
"1,12",
|
||||
"1,13",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"1,14"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.75
|
||||
},
|
||||
"2,0",
|
||||
"2,2",
|
||||
"2,3",
|
||||
"2,4",
|
||||
"2,5",
|
||||
"2,6",
|
||||
"2,7",
|
||||
"2,8",
|
||||
"2,9",
|
||||
"2,10",
|
||||
"2,11",
|
||||
"2,12",
|
||||
{
|
||||
"w": 2.25
|
||||
},
|
||||
"2,13"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"3,0",
|
||||
"3,1",
|
||||
"3,2",
|
||||
"3,3",
|
||||
"3,4",
|
||||
"3,5",
|
||||
"3,6",
|
||||
"3,7",
|
||||
"3,8",
|
||||
"3,9",
|
||||
"3,10",
|
||||
"3,11",
|
||||
{
|
||||
"w": 1.75
|
||||
},
|
||||
"3,13",
|
||||
"3,14"
|
||||
],
|
||||
[
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,1",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,3",
|
||||
{
|
||||
"w": 6.25
|
||||
},
|
||||
"4,6",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,10",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,11",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,13",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,14"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
301
plain60.json
301
plain60.json
|
|
@ -1,301 +0,0 @@
|
|||
{
|
||||
"name": "plain60",
|
||||
"vendorId": "0x4705",
|
||||
"productId": "0x0160",
|
||||
"lighting": "none",
|
||||
"matrix": {
|
||||
"rows": 5,
|
||||
"cols": 15
|
||||
},
|
||||
"layouts": {
|
||||
"labels": [
|
||||
[
|
||||
"Enter Key",
|
||||
"ANSI",
|
||||
"ISO"
|
||||
],
|
||||
"Split Left Shift",
|
||||
"Split Right Shift",
|
||||
"Split Backspace",
|
||||
[
|
||||
"Bottom row",
|
||||
"Default",
|
||||
"Tsangan",
|
||||
"WKL",
|
||||
"HHKB"
|
||||
]
|
||||
],
|
||||
"keymap": [
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"c": "#777777"
|
||||
},
|
||||
"0,0",
|
||||
{
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"0,1",
|
||||
"0,2",
|
||||
"0,3",
|
||||
"0,4",
|
||||
"0,5",
|
||||
"0,6",
|
||||
"0,7",
|
||||
"0,8",
|
||||
"0,9",
|
||||
"0,10",
|
||||
"0,11",
|
||||
"0,12",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 2
|
||||
},
|
||||
"0,14\n\n\n3,0",
|
||||
{
|
||||
"x": 0.5,
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"0,13\n\n\n3,1",
|
||||
"0,14\n\n\n3,1"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.5
|
||||
},
|
||||
"1,0",
|
||||
{
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"1,1",
|
||||
"1,2",
|
||||
"1,3",
|
||||
"1,4",
|
||||
"1,5",
|
||||
"1,6",
|
||||
"1,7",
|
||||
"1,8",
|
||||
"1,9",
|
||||
"1,10",
|
||||
"1,11",
|
||||
"1,12",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"1,13\n\n\n0,0",
|
||||
{
|
||||
"x": 1.5,
|
||||
"c": "#777777",
|
||||
"w": 1.25,
|
||||
"h": 2,
|
||||
"w2": 1.5,
|
||||
"h2": 1,
|
||||
"x2": -0.25
|
||||
},
|
||||
"2,13\n\n\n0,1"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.75
|
||||
},
|
||||
"2,0",
|
||||
{
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"2,1",
|
||||
"2,2",
|
||||
"2,3",
|
||||
"2,4",
|
||||
"2,5",
|
||||
"2,6",
|
||||
"2,7",
|
||||
"2,8",
|
||||
"2,09",
|
||||
"2,10",
|
||||
"2,11",
|
||||
{
|
||||
"c": "#777777",
|
||||
"w": 2.25
|
||||
},
|
||||
"2,13\n\n\n0,0",
|
||||
{
|
||||
"x": 0.5,
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"2,12\n\n\n0,1"
|
||||
],
|
||||
[
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.25
|
||||
},
|
||||
"3,0\n\n\n1,1",
|
||||
{
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"3,1\n\n\n1,1",
|
||||
{
|
||||
"x": 0.5,
|
||||
"c": "#aaaaaa",
|
||||
"w": 2.25
|
||||
},
|
||||
"3,0\n\n\n1,0",
|
||||
{
|
||||
"c": "#cccccc"
|
||||
},
|
||||
"3,2",
|
||||
"3,3",
|
||||
"3,4",
|
||||
"3,5",
|
||||
"3,6",
|
||||
"3,7",
|
||||
"3,8",
|
||||
"3,9",
|
||||
"3,10",
|
||||
"3,11",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 2.75
|
||||
},
|
||||
"3,12\n\n\n2,0",
|
||||
{
|
||||
"x": 0.5,
|
||||
"w": 1.75
|
||||
},
|
||||
"3,12\n\n\n2,1",
|
||||
"3,13\n\n\n2,1"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"w": 1.25
|
||||
},
|
||||
"4,0\n\n\n4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,1\n\n\n4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,2\n\n\n4,0",
|
||||
{
|
||||
"c": "#cccccc",
|
||||
"w": 6.25
|
||||
},
|
||||
"4,6\n\n\n4,0",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.25
|
||||
},
|
||||
"4,10\n\n\n4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,11\n\n\n4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,12\n\n\n4,0",
|
||||
{
|
||||
"w": 1.25
|
||||
},
|
||||
"4,13\n\n\n4,0"
|
||||
],
|
||||
[
|
||||
{
|
||||
"y": 0.25,
|
||||
"x": 2.75,
|
||||
"w": 1.5
|
||||
},
|
||||
"4,0\n\n\n4,1",
|
||||
"4,1\n\n\n4,1",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"4,2\n\n\n4,1",
|
||||
{
|
||||
"c": "#cccccc",
|
||||
"w": 7
|
||||
},
|
||||
"4,6\n\n\n4,1",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.5
|
||||
},
|
||||
"4,11\n\n\n4,1",
|
||||
"4,12\n\n\n4,1",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"4,13\n\n\n4,1"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"w": 1.5
|
||||
},
|
||||
"4,0\n\n\n4,2",
|
||||
{
|
||||
"d": true
|
||||
},
|
||||
"\n\n\n4,2",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"4,2\n\n\n4,2",
|
||||
{
|
||||
"c": "#cccccc",
|
||||
"w": 7
|
||||
},
|
||||
"4,6\n\n\n4,2",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.5
|
||||
},
|
||||
"4,11\n\n\n4,2",
|
||||
{
|
||||
"d": true
|
||||
},
|
||||
"\n\n\n4,2",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"4,13\n\n\n4,2"
|
||||
],
|
||||
[
|
||||
{
|
||||
"x": 2.75,
|
||||
"w": 1.5,
|
||||
"d": true
|
||||
},
|
||||
"\n\n\n4,3",
|
||||
"4,1\n\n\n4,3",
|
||||
{
|
||||
"w": 1.5
|
||||
},
|
||||
"4,2\n\n\n4,3",
|
||||
{
|
||||
"c": "#cccccc",
|
||||
"w": 7
|
||||
},
|
||||
"4,6\n\n\n4,3",
|
||||
{
|
||||
"c": "#aaaaaa",
|
||||
"w": 1.5
|
||||
},
|
||||
"4,11\n\n\n4,3",
|
||||
"4,12\n\n\n4,3",
|
||||
{
|
||||
"w": 1.5,
|
||||
"d": true
|
||||
},
|
||||
"\n\n\n4,3"
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +0,0 @@
|
|||
import hid
|
||||
|
||||
VIAL_SERIAL_NUMBER_MAGIC = "vial:f64c2b3c"
|
||||
|
||||
def find_vial_keyboards():
|
||||
for dev in hid.enumerate():
|
||||
print(dev)
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
REPORT_LEN = 32
|
||||
|
||||
def hid_send(dev, msg):
|
||||
if len(report) > REPORT_LEN:
|
||||
raise RuntimeError("report must be less than 64 bytes")
|
||||
msg += b"\x00" * (REPORT_LEN - len(msg))
|
||||
|
||||
# add 00 at start for hidapi report id
|
||||
dev.write(b"\x00" + report)
|
||||
|
||||
return dev.read(REPORT_LEN)
|
||||
|
|
@ -1,12 +1,14 @@
|
|||
from fbs_runtime.application_context.PyQt5 import ApplicationContext
|
||||
from PyQt5.QtCore import Qt
|
||||
from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QPushButton, QLabel
|
||||
from PyQt5.QtWidgets import QWidget, QTabWidget, QVBoxLayout, QPushButton, QLabel, QComboBox, QToolButton, QHBoxLayout
|
||||
|
||||
import sys
|
||||
import json
|
||||
import struct
|
||||
import lzma
|
||||
|
||||
from flowlayout import FlowLayout
|
||||
from util import tr
|
||||
from util import tr, find_vial_keyboards, open_device, hid_send, MSG_LEN
|
||||
from kle_serial import Serial as KleSerial
|
||||
|
||||
class TabbedKeycodes(QTabWidget):
|
||||
|
|
@ -40,9 +42,14 @@ class KeyboardContainer(QWidget):
|
|||
def __init__(self, parent=None):
|
||||
super().__init__(parent)
|
||||
|
||||
self.keys = []
|
||||
|
||||
def rebuild(self, data):
|
||||
for key in self.keys:
|
||||
key.deleteLater()
|
||||
self.keys = []
|
||||
|
||||
serial = KleSerial()
|
||||
data = open("g60.json", "r").read()
|
||||
data = json.loads(data)
|
||||
kb = serial.deserialize(data["layouts"]["keymap"])
|
||||
|
||||
max_w = max_h = 0
|
||||
|
|
@ -60,11 +67,14 @@ class KeyboardContainer(QWidget):
|
|||
|
||||
widget.setFixedSize(w, h)
|
||||
widget.move(x, y)
|
||||
print("{} {}x{}+{}x{}".format(key.labels, key.x, key.y, key.width, key.height))
|
||||
widget.show()
|
||||
# print("{} {}x{}+{}x{}".format(key.labels, key.x, key.y, key.width, key.height))
|
||||
|
||||
max_w = max(max_w, x + w)
|
||||
max_h = max(max_h, y + h)
|
||||
|
||||
|
||||
self.keys.append(widget)
|
||||
|
||||
self.setFixedSize(max_w, max_h)
|
||||
|
||||
|
||||
|
|
@ -72,17 +82,71 @@ class MainWindow(QWidget):
|
|||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.device = None
|
||||
self.devices = []
|
||||
|
||||
self.keyboard_container = KeyboardContainer()
|
||||
|
||||
self.tabbed_keycodes = TabbedKeycodes()
|
||||
|
||||
self.combobox_devices = QComboBox()
|
||||
self.combobox_devices.currentIndexChanged.connect(self.on_device_selected)
|
||||
|
||||
btn_refresh_devices = QToolButton()
|
||||
btn_refresh_devices.setToolButtonStyle(Qt.ToolButtonTextOnly)
|
||||
btn_refresh_devices.setText(tr("MainWindow", "Refresh"))
|
||||
btn_refresh_devices.clicked.connect(self.on_click_refresh)
|
||||
|
||||
layout_combobox = QHBoxLayout()
|
||||
layout_combobox.addWidget(self.combobox_devices)
|
||||
layout_combobox.addWidget(btn_refresh_devices)
|
||||
|
||||
layout = QVBoxLayout()
|
||||
layout.addLayout(layout_combobox)
|
||||
layout.addWidget(self.keyboard_container)
|
||||
layout.setAlignment(self.keyboard_container, Qt.AlignHCenter)
|
||||
layout.addWidget(self.tabbed_keycodes)
|
||||
self.setLayout(layout)
|
||||
|
||||
# make sure initial state is valid
|
||||
self.on_click_refresh()
|
||||
self.on_device_selected()
|
||||
|
||||
def on_click_refresh(self):
|
||||
self.devices = find_vial_keyboards()
|
||||
self.combobox_devices.clear()
|
||||
|
||||
for dev in self.devices:
|
||||
self.combobox_devices.addItem("{} {}".format(dev["manufacturer_string"], dev["product_string"]))
|
||||
|
||||
def on_device_selected(self):
|
||||
self.device = None
|
||||
idx = self.combobox_devices.currentIndex()
|
||||
if idx >= 0:
|
||||
self.device = open_device(self.devices[idx])
|
||||
self.reload_layout()
|
||||
|
||||
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(payload)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
appctxt = ApplicationContext() # 1. Instantiate ApplicationContext
|
||||
|
|
|
|||
|
|
@ -1,3 +1,42 @@
|
|||
from PyQt5.QtCore import QCoreApplication
|
||||
|
||||
import sys
|
||||
|
||||
if sys.platform.startswith("linux"):
|
||||
import hidraw as hid
|
||||
else:
|
||||
import hid
|
||||
|
||||
|
||||
tr = QCoreApplication.translate
|
||||
|
||||
VIAL_SERIAL_NUMBER_MAGIC = "vial:f64c2b3c"
|
||||
|
||||
MSG_LEN = 32
|
||||
|
||||
def hid_send(dev, msg):
|
||||
if len(msg) > MSG_LEN:
|
||||
raise RuntimeError("message must be less than 32 bytes")
|
||||
msg += b"\x00" * (MSG_LEN - len(msg))
|
||||
|
||||
# add 00 at start for hidapi report id
|
||||
dev.write(b"\x00" + msg)
|
||||
|
||||
return bytes(dev.read(MSG_LEN))
|
||||
|
||||
def is_rawhid(dev):
|
||||
# TODO: this is only broken on linux, other platforms should be able to check usage_page
|
||||
return dev["interface_number"] == 1
|
||||
|
||||
def find_vial_keyboards():
|
||||
filtered = []
|
||||
for dev in hid.enumerate():
|
||||
if VIAL_SERIAL_NUMBER_MAGIC in dev["serial_number"] and is_rawhid(dev):
|
||||
filtered.append(dev)
|
||||
return filtered
|
||||
|
||||
def open_device(desc):
|
||||
# TODO: error handling here
|
||||
dev = hid.device()
|
||||
dev.open_path(desc["path"])
|
||||
return dev
|
||||
|
|
|
|||
Loading…
Reference in New Issue