diff --git a/src/main/python/keyboard_comm.py b/src/main/python/keyboard_comm.py index 0eaf7ff..2ddc704 100644 --- a/src/main/python/keyboard_comm.py +++ b/src/main/python/keyboard_comm.py @@ -41,6 +41,12 @@ QMK_RGBLIGHT_EFFECT = 0x81 QMK_RGBLIGHT_EFFECT_SPEED = 0x82 QMK_RGBLIGHT_COLOR = 0x83 +VIALRGB_GET_INFO = 0x40 +VIALRGB_GET_MODE = 0x41 +VIALRGB_GET_SUPPORTED = 0x42 + +VIALRGB_SET_MODE = 0x41 + CMD_VIAL_GET_KEYBOARD_ID = 0x00 CMD_VIAL_GET_SIZE = 0x01 CMD_VIAL_GET_DEFINITION = 0x02 @@ -194,10 +200,17 @@ class Keyboard: self.vibl = False self.custom_keycodes = None - self.lighting_qmk_rgblight = self.lighting_qmk_backlight = False + self.lighting_qmk_rgblight = self.lighting_qmk_backlight = self.lighting_vialrgb = False + + # underglow self.underglow_brightness = self.underglow_effect = self.underglow_effect_speed = -1 - self.backlight_brightness = self.backlight_effect = -1 self.underglow_color = (0, 0) + # backlight + self.backlight_brightness = self.backlight_effect = -1 + # vialrgb + self.rgb_mode = self.rgb_speed = self.rgb_version = self.rgb_maximum_brightness = -1 + self.rgb_hsv = (0, 0, 0) + self.rgb_supported_effects = set() self.via_protocol = self.vial_protocol = self.keyboard_id = -1 @@ -213,6 +226,7 @@ class Keyboard: self.reload_layers() self.reload_keymap() self.reload_macros() + self.reload_persistent_rgb() self.reload_rgb() self.reload_settings() self.reload_dynamic() @@ -360,11 +374,38 @@ class Keyboard: macros = self.macro.split(b"\x00") + [b""] * self.macro_count self.macro = b"\x00".join(macros[:self.macro_count]) + b"\x00" - def reload_rgb(self): + def reload_persistent_rgb(self): + """ + Reload RGB properties which are slow, and do not change while keyboard is plugged in + e.g. VialRGB supported effects list + """ + if "lighting" in self.definition: self.lighting_qmk_rgblight = self.definition["lighting"] in ["qmk_rgblight", "qmk_backlight_rgblight"] self.lighting_qmk_backlight = self.definition["lighting"] in ["qmk_backlight", "qmk_backlight_rgblight"] + self.lighting_vialrgb = self.definition["lighting"] == "vialrgb" + if self.lighting_vialrgb: + data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_LIGHTING_GET_VALUE, VIALRGB_GET_INFO), + retries=20)[2:] + self.rgb_version = data[0] | (data[1] << 8) + if self.rgb_version != 1: + raise RuntimeError("Unsupported VialRGB protocol ({}), update your Vial version to latest" + .format(self.rgb_version)) + self.rgb_maximum_brightness = data[2] + + self.rgb_supported_effects = {0} + max_effect = 0 + while max_effect < 0xFFFF: + data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_LIGHTING_GET_VALUE, QMK_RGBLIGHT_BRIGHTNESS), retries=20)[2] @@ -383,6 +424,13 @@ class Keyboard: self.backlight_effect = self.usb_send( self.dev, struct.pack(">BB", CMD_VIA_LIGHTING_GET_VALUE, QMK_BACKLIGHT_EFFECT), retries=20)[2] + if self.lighting_vialrgb: + data = self.usb_send(self.dev, struct.pack("BB", CMD_VIA_LIGHTING_GET_VALUE, VIALRGB_GET_MODE), + retries=20)[2:] + self.rgb_mode = int.from_bytes(data[0:2], byteorder="little") + self.rgb_speed = data[2] + self.rgb_hsv = (data[3], data[4], data[5]) + def reload_settings(self): self.settings = dict() self.supported_settings = set() @@ -782,6 +830,27 @@ class Keyboard: self.usb_send(self.dev, struct.pack("BBBB", CMD_VIA_VIAL_PREFIX, CMD_VIAL_DYNAMIC_ENTRY_OP, DYNAMIC_VIAL_COMBO_SET, idx) + serialized, retries=20) + def _vialrgb_set_mode(self): + self.usb_send(self.dev, struct.pack("BBHBBBB", CMD_VIA_LIGHTING_SET_VALUE, VIALRGB_SET_MODE, + self.rgb_mode, self.rgb_speed, + self.rgb_hsv[0], self.rgb_hsv[1], self.rgb_hsv[2])) + + def set_vialrgb_brightness(self, value): + self.rgb_hsv = (self.rgb_hsv[0], self.rgb_hsv[1], value) + self._vialrgb_set_mode() + + def set_vialrgb_speed(self, value): + self.rgb_speed = value + self._vialrgb_set_mode() + + def set_vialrgb_mode(self, value): + self.rgb_mode = value + self._vialrgb_set_mode() + + def set_vialrgb_color(self, h, s, v): + self.rgb_hsv = (h, s, v) + self._vialrgb_set_mode() + class DummyKeyboard(Keyboard): diff --git a/src/main/python/rgb_configurator.py b/src/main/python/rgb_configurator.py index 33475b5..d50cf53 100644 --- a/src/main/python/rgb_configurator.py +++ b/src/main/python/rgb_configurator.py @@ -60,32 +60,92 @@ QMK_RGBLIGHT_EFFECTS = [ ] +class VialRGBEffect: + + def __init__(self, idx, name): + self.idx = idx + self.name = name + + +VIALRGB_EFFECTS = [ + VialRGBEffect(0, "Disable"), + VialRGBEffect(1, "Direct Control"), + VialRGBEffect(2, "Solid Color"), + VialRGBEffect(3, "Alphas Mods"), + VialRGBEffect(4, "Gradient Up Down"), + VialRGBEffect(5, "Gradient Left Right"), + VialRGBEffect(6, "Breathing"), + VialRGBEffect(7, "Band Sat"), + VialRGBEffect(8, "Band Val"), + VialRGBEffect(9, "Band Pinwheel Sat"), + VialRGBEffect(10, "Band Pinwheel Val"), + VialRGBEffect(11, "Band Spiral Sat"), + VialRGBEffect(12, "Band Spiral Val"), + VialRGBEffect(13, "Cycle All"), + VialRGBEffect(14, "Cycle Left Right"), + VialRGBEffect(15, "Cycle Up Down"), + VialRGBEffect(16, "Rainbow Moving Chevron"), + VialRGBEffect(17, "Cycle Out In"), + VialRGBEffect(18, "Cycle Out In Dual"), + VialRGBEffect(19, "Cycle Pinwheel"), + VialRGBEffect(20, "Cycle Spiral"), + VialRGBEffect(21, "Dual Beacon"), + VialRGBEffect(22, "Rainbow Beacon"), + VialRGBEffect(23, "Rainbow Pinwheels"), + VialRGBEffect(24, "Raindrops"), + VialRGBEffect(25, "Jellybean Raindrops"), + VialRGBEffect(26, "Hue Breathing"), + VialRGBEffect(27, "Hue Pendulum"), + VialRGBEffect(28, "Hue Wave"), + VialRGBEffect(29, "Typing Heatmap"), + VialRGBEffect(30, "Digital Rain"), + VialRGBEffect(31, "Solid Reactive Simple"), + VialRGBEffect(32, "Solid Reactive"), + VialRGBEffect(33, "Solid Reactive Wide"), + VialRGBEffect(34, "Solid Reactive Multiwide"), + VialRGBEffect(35, "Solid Reactive Cross"), + VialRGBEffect(36, "Solid Reactive Multicross"), + VialRGBEffect(37, "Solid Reactive Nexus"), + VialRGBEffect(38, "Solid Reactive Multinexus"), + VialRGBEffect(39, "Splash"), + VialRGBEffect(40, "Multisplash"), + VialRGBEffect(41, "Solid Splash"), + VialRGBEffect(42, "Solid Multisplash"), +] + + class BasicHandler(QObject): update = pyqtSignal() def __init__(self, container): super().__init__() - self.device = None + self.device = self.keyboard = None + self.widgets = [] def set_device(self, device): self.device = device if self.valid(): + self.keyboard = self.device.keyboard self.show() else: self.hide() def show(self): - raise NotImplementedError + for w in self.widgets: + w.show() def hide(self): - raise NotImplementedError + for w in self.widgets: + w.hide() def block_signals(self): - raise NotImplementedError + for w in self.widgets: + w.blockSignals(True) def unblock_signals(self): - raise NotImplementedError + for w in self.widgets: + w.blockSignals(False) def update_from_keyboard(self): raise NotImplementedError @@ -124,31 +184,8 @@ class QmkRgblightHandler(BasicHandler): self.underglow_effect.currentIndexChanged.connect(self.on_underglow_effect_changed) - def show(self): - self.lbl_underglow_effect.show() - self.underglow_effect.show() - self.lbl_underglow_brightness.show() - self.underglow_brightness.show() - self.lbl_underglow_color.show() - self.underglow_color.show() - - def hide(self): - self.lbl_underglow_effect.hide() - self.underglow_effect.hide() - self.lbl_underglow_brightness.hide() - self.underglow_brightness.hide() - self.lbl_underglow_color.hide() - self.underglow_color.hide() - - def block_signals(self): - self.underglow_brightness.blockSignals(True) - self.underglow_effect.blockSignals(True) - self.underglow_color.blockSignals(True) - - def unblock_signals(self): - self.underglow_brightness.blockSignals(False) - self.underglow_effect.blockSignals(False) - self.underglow_color.blockSignals(False) + self.widgets = [self.lbl_underglow_effect, self.underglow_effect, self.lbl_underglow_brightness, + self.underglow_brightness, self.lbl_underglow_color, self.underglow_color] def update_from_keyboard(self): self.underglow_brightness.setValue(self.device.keyboard.underglow_brightness) @@ -206,25 +243,8 @@ class QmkBacklightHandler(BasicHandler): self.backlight_breathing.stateChanged.connect(self.on_backlight_breathing_changed) container.addWidget(self.backlight_breathing, row + 1, 1) - def show(self): - self.lbl_backlight_brightness.show() - self.backlight_brightness.show() - self.lbl_backlight_breathing.show() - self.backlight_breathing.show() - - def hide(self): - self.lbl_backlight_brightness.hide() - self.backlight_brightness.hide() - self.lbl_backlight_breathing.hide() - self.backlight_breathing.hide() - - def block_signals(self): - self.backlight_brightness.blockSignals(True) - self.backlight_breathing.blockSignals(True) - - def unblock_signals(self): - self.backlight_brightness.blockSignals(False) - self.backlight_breathing.blockSignals(False) + self.widgets = [self.lbl_backlight_brightness, self.backlight_brightness, self.lbl_backlight_breathing, + self.backlight_breathing] def update_from_keyboard(self): self.backlight_brightness.setValue(self.device.keyboard.backlight_brightness) @@ -240,6 +260,100 @@ class QmkBacklightHandler(BasicHandler): self.device.keyboard.set_qmk_backlight_effect(int(checked)) +class VialRGBHandler(BasicHandler): + + def __init__(self, container): + super().__init__(container) + + row = container.rowCount() + + self.lbl_rgb_effect = QLabel(tr("RGBConfigurator", "RGB Effect")) + container.addWidget(self.lbl_rgb_effect, row, 0) + self.rgb_effect = QComboBox() + self.rgb_effect.addItem("0") + self.rgb_effect.addItem("1") + self.rgb_effect.addItem("2") + self.rgb_effect.addItem("3") + self.rgb_effect.currentIndexChanged.connect(self.on_rgb_effect_changed) + container.addWidget(self.rgb_effect, row, 1) + + self.lbl_rgb_color = QLabel(tr("RGBConfigurator", "RGB Color")) + container.addWidget(self.lbl_rgb_color, row + 1, 0) + self.rgb_color = ClickableLabel(" ") + self.rgb_color.clicked.connect(self.on_rgb_color) + container.addWidget(self.rgb_color, row + 1, 1) + + self.lbl_rgb_brightness = QLabel(tr("RGBConfigurator", "RGB Brightness")) + container.addWidget(self.lbl_rgb_brightness, row + 2, 0) + self.rgb_brightness = QSlider(QtCore.Qt.Horizontal) + self.rgb_brightness.setMinimum(0) + self.rgb_brightness.setMaximum(255) + self.rgb_brightness.valueChanged.connect(self.on_rgb_brightness_changed) + container.addWidget(self.rgb_brightness, row + 2, 1) + + self.lbl_rgb_speed = QLabel(tr("RGBConfigurator", "RGB Speed")) + container.addWidget(self.lbl_rgb_speed, row + 3, 0) + self.rgb_speed = QSlider(QtCore.Qt.Horizontal) + self.rgb_speed.setMinimum(0) + self.rgb_speed.setMaximum(255) + self.rgb_speed.valueChanged.connect(self.on_rgb_speed_changed) + container.addWidget(self.rgb_speed, row + 3, 1) + + self.widgets = [self.lbl_rgb_effect, self.rgb_effect, self.lbl_rgb_brightness, self.rgb_brightness, + self.lbl_rgb_color, self.rgb_color, self.lbl_rgb_speed, self.rgb_speed] + + self.effects = [] + + def on_rgb_brightness_changed(self, value): + self.keyboard.set_vialrgb_brightness(value) + + def on_rgb_speed_changed(self, value): + self.keyboard.set_vialrgb_speed(value) + + def on_rgb_effect_changed(self, index): + self.keyboard.set_vialrgb_mode(self.effects[index].idx) + + def on_rgb_color(self): + color = QColorDialog.getColor(self.current_color()) + if not color.isValid(): + return + self.rgb_color.setStyleSheet("QWidget { background-color: %s}" % color.name()) + h, s, v, a = color.getHsvF() + if h < 0: + h = 0 + self.keyboard.set_vialrgb_color(int(255 * h), int(255 * s), self.keyboard.rgb_hsv[2]) + self.update.emit() + + def current_color(self): + return QColor.fromHsvF(self.keyboard.rgb_hsv[0] / 255.0, + self.keyboard.rgb_hsv[1] / 255.0, + 1.0) + + def rebuild_effects(self): + self.effects = [] + for effect in VIALRGB_EFFECTS: + if effect.idx in self.keyboard.rgb_supported_effects: + self.effects.append(effect) + + self.rgb_effect.clear() + for effect in self.effects: + self.rgb_effect.addItem(effect.name) + + def update_from_keyboard(self): + self.rebuild_effects() + for x, effect in enumerate(self.effects): + if effect.idx == self.keyboard.rgb_mode: + self.rgb_effect.setCurrentIndex(x) + break + self.rgb_brightness.setMaximum(self.keyboard.rgb_maximum_brightness) + self.rgb_brightness.setValue(self.keyboard.rgb_hsv[2]) + self.rgb_speed.setValue(self.keyboard.rgb_speed) + self.rgb_color.setStyleSheet("QWidget { background-color: %s}" % self.current_color().name()) + + def valid(self): + return isinstance(self.device, VialKeyboard) and self.device.keyboard.lighting_vialrgb + + class RGBConfigurator(BasicEditor): def __init__(self): @@ -258,7 +372,9 @@ class RGBConfigurator(BasicEditor): self.handler_backlight.update.connect(self.update_from_keyboard) self.handler_rgblight = QmkRgblightHandler(self.container) self.handler_rgblight.update.connect(self.update_from_keyboard) - self.handlers = [self.handler_backlight, self.handler_rgblight] + self.handler_vialrgb = VialRGBHandler(self.container) + self.handler_vialrgb.update.connect(self.update_from_keyboard) + self.handlers = [self.handler_backlight, self.handler_rgblight, self.handler_vialrgb] self.addStretch() buttons = QHBoxLayout() @@ -273,7 +389,8 @@ class RGBConfigurator(BasicEditor): def valid(self): return isinstance(self.device, VialKeyboard) and \ - (self.device.keyboard.lighting_qmk_rgblight or self.device.keyboard.lighting_qmk_backlight) + (self.device.keyboard.lighting_qmk_rgblight or self.device.keyboard.lighting_qmk_backlight + or self.device.keyboard.lighting_vialrgb) def block_signals(self): for h in self.handlers: