encoders: initial support

main
Ilya Zhuravlev 2020-12-06 19:24:34 -05:00
parent ffd2d3746f
commit 3fe4dad968
3 changed files with 100 additions and 27 deletions

View File

@ -17,6 +17,8 @@ CMD_VIA_VIAL_PREFIX = 0xFE
CMD_VIAL_GET_KEYBOARD_ID = 0x00
CMD_VIAL_GET_SIZE = 0x01
CMD_VIAL_GET_DEFINITION = 0x02
CMD_VIAL_GET_ENCODER = 0x03
CMD_VIAL_SET_ENCODER = 0x04
class Keyboard:
@ -28,9 +30,12 @@ class Keyboard:
# n.b. using OrderedDict here to make order of layout requests consistent for tests
self.rowcol = OrderedDict()
self.encoderpos = OrderedDict()
self.layout = dict()
self.encoder_layout = dict()
self.rows = self.cols = self.layers = 0
self.keys = []
self.encoders = []
self.vial_protocol = self.keyboard_id = -1
@ -38,7 +43,9 @@ class Keyboard:
""" Load information about the keyboard: number of layers, physical key layout """
self.rowcol = OrderedDict()
self.encoderpos = OrderedDict()
self.layout = dict()
self.encoder_layout = dict()
self.reload_layout(sideload_json)
self.reload_layers()
@ -82,16 +89,26 @@ class Keyboard:
serial = KleSerial()
kb = serial.deserialize(payload["layouts"]["keymap"])
self.keys = kb.keys
self.keys = []
self.encoders = []
for key in self.keys:
for key in kb.keys:
key.row = key.col = None
if key.labels[0] and "," in key.labels[0]:
key.encoder_idx = key.encoder_dir = None
if key.labels[4] == "e":
idx, direction = key.labels[0].split(",")
idx, direction = int(idx), int(direction)
key.encoder_idx = idx
key.encoder_dir = direction
self.encoderpos[idx] = True
self.encoders.append(key)
elif 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[(row, col)] = True
self.keys.append(key)
def reload_keymap(self):
""" Load current key mapping from the keyboard """
@ -101,6 +118,10 @@ class Keyboard:
data = self.usb_send(self.dev, struct.pack("BBBB", CMD_VIA_GET_KEYCODE, layer, row, col))
keycode = struct.unpack(">H", data[4:6])[0]
self.layout[(layer, row, col)] = keycode
for idx in self.encoderpos:
data = self.usb_send(self.dev, struct.pack("BBBB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_ENCODER, layer, idx))
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]
def set_key(self, layer, row, col, code):
key = (layer, row, col)
@ -108,6 +129,13 @@ class Keyboard:
self.usb_send(self.dev, struct.pack(">BBBBH", CMD_VIA_SET_KEYCODE, layer, row, col, code))
self.layout[key] = code
def set_encoder(self, layer, index, direction, code):
key = (layer, index, direction)
if self.encoder_layout[key] != code:
self.usb_send(self.dev, struct.pack(">BBBBBH", CMD_VIA_VIAL_PREFIX, CMD_VIAL_SET_ENCODER,
layer, index, direction, code))
self.encoder_layout[key] = code
def save_layout(self):
""" Serializes current layout to a binary """

View File

@ -6,7 +6,7 @@ from PyQt5.QtCore import pyqtSignal, Qt
from PyQt5.QtWidgets import QWidget, QHBoxLayout, QLabel, QVBoxLayout
from clickable_label import ClickableLabel
from keyboard_widget import KeyboardWidget
from keyboard_widget import KeyboardWidget, EncoderWidget
from keycodes import keycode_label, keycode_tooltip, keycode_is_mask
from constants import LAYER_BTN_STYLE, ACTIVE_LAYER_BTN_STYLE
from util import tr
@ -39,9 +39,8 @@ class KeyboardContainer(QWidget):
self.keys = []
self.layer_labels = []
self.rowcol = defaultdict(list)
self.selected_row = -1
self.selected_col = -1
self.widgets = []
self.selected_widget = None
self.selected_mask = False
self.keyboard = None
self.current_layer = 0
@ -74,12 +73,12 @@ class KeyboardContainer(QWidget):
self.rebuild_layers()
# prepare for fetching keymap
self.rowcol = defaultdict(list)
self.widgets = []
self.container.set_keys(keyboard.keys)
self.container.set_keys(keyboard.keys, keyboard.encoders)
for key in self.container.keys:
if key.desc.row is not None:
self.rowcol[(key.desc.row, key.desc.col)].append(key)
if key.desc.row is not None or key.desc.encoder_idx is not None:
self.widgets.append(key)
self.current_layer = 0
self.refresh_layer_display()
@ -91,33 +90,54 @@ class KeyboardContainer(QWidget):
label.setStyleSheet(LAYER_BTN_STYLE)
self.layer_labels[self.current_layer].setStyleSheet(ACTIVE_LAYER_BTN_STYLE)
for (row, col), widgets in self.rowcol.items():
code = self.keyboard.layout[(self.current_layer, row, col)]
for widget in self.widgets:
if widget.desc.row is not None:
code = self.keyboard.layout[(self.current_layer, widget.desc.row, widget.desc.col)]
else:
code = self.keyboard.encoder_layout[(self.current_layer, widget.desc.encoder_idx,
widget.desc.encoder_dir)]
text = keycode_label(code)
tooltip = keycode_tooltip(code)
mask = keycode_is_mask(code)
mask_text = keycode_label(code & 0xFF)
if mask:
text = text.split("\n")[0]
for widget in widgets:
widget.masked = mask
widget.setText(text)
widget.setMaskText(mask_text)
widget.setToolTip(tooltip)
widget.masked = mask
widget.setText(text)
widget.setMaskText(mask_text)
widget.setToolTip(tooltip)
self.container.update()
self.container.updateGeometry()
def switch_layer(self, idx):
self.container.deselect()
self.current_layer = idx
self.selected_row = -1
self.selected_col = -1
self.selected_widget = None
self.refresh_layer_display()
def set_key(self, keycode):
""" Change currently selected key to provided keycode """
l, r, c = self.current_layer, self.selected_row, self.selected_col
if isinstance(self.selected_widget, EncoderWidget):
self.set_key_encoder(keycode)
else:
self.set_key_matrix(keycode)
def set_key_encoder(self, keycode):
l, i, d = self.current_layer, self.selected_widget.desc.encoder_idx,\
self.selected_widget.desc.encoder_dir
# if masked, ensure that this is a byte-sized keycode
if self.selected_mask:
if keycode > 0xFF:
return
keycode = (self.keyboard.encoder_layout[(l, i, d)] & 0xFF00) | keycode
self.keyboard.set_encoder(l, i, d, keycode)
self.refresh_layer_display()
def set_key_matrix(self, keycode):
l, r, c = self.current_layer, self.selected_widget.desc.row, self.selected_widget.desc.col
if r >= 0 and c >= 0:
# if masked, ensure that this is a byte-sized keycode
@ -133,12 +153,8 @@ class KeyboardContainer(QWidget):
""" Change which key is currently selected """
self.selected_mask = is_mask
self.selected_widget = widget
for (row, col), widgets in self.rowcol.items():
if widget in widgets:
self.selected_row = row
self.selected_col = col
break
self.refresh_layer_display()
def save_layout(self):

View File

@ -34,6 +34,7 @@ class KeyWidget:
self.bbox = self.calculate_bbox(QRectF(self.x, self.y, self.w, self.h))
self.polygon = QPolygonF(self.bbox + [self.bbox[0]])
self.draw_path = self.calculate_draw_path()
self.draw_path2 = self.calculate_draw_path2()
# calculate areas where the inner keycode will be located
# nonmask = outer (e.g. Rsft_T)
@ -73,6 +74,9 @@ class KeyWidget:
return path
def calculate_draw_path2(self):
return QPainterPath()
def setText(self, text):
self.text = text
@ -83,6 +87,28 @@ class KeyWidget:
self.tooltip = tooltip
class EncoderWidget(KeyWidget):
def calculate_draw_path(self):
path = QPainterPath()
path.addEllipse(int(self.x), int(self.y), int(self.w), int(self.h))
return path
def calculate_draw_path2(self):
path = QPainterPath()
if self.desc.encoder_dir == 0:
path.moveTo(int(self.x), int(self.y + self.h / 2))
path.lineTo(int(self.x - self.w / 5), int(self.y + self.h / 3))
path.moveTo(int(self.x), int(self.y + self.h / 2))
path.lineTo(int(self.x + self.w / 5), int(self.y + self.h / 3))
else:
path.moveTo(int(self.x), int(self.y + self.h / 2))
path.lineTo(int(self.x - self.w / 5), int(self.y + self.h - self.h / 3))
path.moveTo(int(self.x), int(self.y + self.h / 2))
path.lineTo(int(self.x + self.w / 5), int(self.y + self.h - self.h / 3))
return path
class KeyboardWidget(QWidget):
clicked = pyqtSignal(KeyWidget, bool)
@ -96,10 +122,12 @@ class KeyboardWidget(QWidget):
self.active_key = None
self.active_mask = False
def set_keys(self, keys):
def set_keys(self, keys, encoders):
self.keys = []
for key in keys:
self.keys.append(KeyWidget(key))
for key in encoders:
self.keys.append(EncoderWidget(key))
self.calculate_size()
self.update()
@ -155,6 +183,7 @@ class KeyboardWidget(QWidget):
# draw the keycap
qp.drawPath(key.draw_path)
qp.strokePath(key.draw_path2, regular_pen)
# if this is a mask key, draw the inner key
if key.masked: