From 9e10eb82d51ae1dfd0790f030312f92e445532e0 Mon Sep 17 00:00:00 2001 From: Mikhail Trishchenkov Date: Thu, 8 Feb 2024 11:52:06 +0700 Subject: [PATCH] Check for restrictions of 3x1+1 keyboard --- README.md | 8 +++++++- src/config.rs | 45 +++++++++++++++++++++++++++++++++++++++++++-- src/keyboard.rs | 11 ++++++++++- 3 files changed, 60 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 340b693..dde27de 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ Utility was reported to work with: * 3×4 with 2 knobs (Bluetooth version) * 3×3 with 2 knobs * 3x2 with 1 knob - * 3x1 with 1 knob + * 3x1 with 1 knob (but [read about it's limitations](#3x1-keys--1-knob-keyboard-limitations)) All these keyboards share same vendor/product IDs: 1189:8890 (hexadecimal). It is possible to override used vendor/product ID, but it is usually not needed. @@ -95,6 +95,12 @@ character that will be produced. So use QWERTY-letter of keyboard key you want to press. +## 3x1 keys + 1 knob keyboard limitations + +This modification does support key modifiers (like ctrl-, alt-) for the first key in sequence only. + +So you can use: `ctrl-alt-del,1,2`, but not `ctrl-alt-del,alt-1,2`. + # Diagnostics If you have any troubles using this software, please provide diagnostics. diff --git a/src/config.rs b/src/config.rs index fab80ed..2b2f042 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,4 +1,4 @@ -use anyhow::{Result, ensure}; +use anyhow::{bail, ensure, Result}; use serde::Deserialize; use crate::keyboard::Macro; @@ -17,6 +17,9 @@ impl Config { /// Validates config and renders it to flat list of macros for buttons /// and knobs taking orientation into account. pub fn render(self) -> Result> { + // 3x1 keys + 1 knob keyboard has some limitations we need to check. + let is_limited = (self.rows == 1 || self.columns == 1) && self.knobs == 1; + self.layers.into_iter().enumerate().map(|(i, layer)| { let (orows, ocols) = if self.orientation.is_horizontal() { (self.rows, self.columns) @@ -30,6 +33,18 @@ impl Config { let buttons = reorient_grid(self.orientation, self.rows as usize, self.columns as usize, layer.buttons); let knobs = reorient_row(self.orientation, layer.knobs); + if is_limited { + let macro_with_modifiers_beside_first_key = buttons.iter().flatten().find(|macro_| { + match macro_ { + Macro::Keyboard(accords) => accords.iter().skip(1).any(|accord| !accord.modifiers.is_empty()), + _ => false, + } + }); + if let Some(macro_) = macro_with_modifiers_beside_first_key { + bail!("1-row keyboard with 1 knob can handle modifiers for first key in sequence only: {}", macro_); + } + } + Ok(FlatLayer { buttons, knobs }) }).collect() } @@ -97,7 +112,9 @@ fn reorient_row(orientation: Orientation, mut data: Vec) -> Vec { #[cfg(test)] mod tests { - use super::{Config, reorient_grid, Orientation}; + use crate::config::Layer; + + use super::{reorient_grid, Config, Knob, Orientation}; use std::path::PathBuf; @@ -146,4 +163,28 @@ mod tests { vec![5, 3, 1, 6, 4, 2], ); } + + #[test] + #[should_panic(expected="can handle modifiers for first key in sequence only")] + fn test_limited_keyboard() { + let config = Config { + orientation: Orientation::Normal, + rows: 1, + columns: 3, + knobs: 1, + layers: vec![ + Layer { + buttons: vec![ + vec![ + Some("a,alt-b".parse().unwrap()), + None, + None + ], + ], + knobs: vec![Knob { ccw: None, press: None, cw: None }], + }, + ], + }; + config.render().unwrap(); + } } diff --git a/src/keyboard.rs b/src/keyboard.rs index 5590589..a67476c 100644 --- a/src/keyboard.rs +++ b/src/keyboard.rs @@ -176,12 +176,21 @@ pub enum MediaCode { ScreenLock = 0x19e, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Display)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Code { WellKnown(WellKnownCode), Custom(u8), } +impl Display for Code { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + Code::WellKnown(code) => write!(f, "{}", code), + Code::Custom(code) => write!(f, "<{}>", code), + } + } +} + impl From for Code { fn from(code: WellKnownCode) -> Self { Self::WellKnown(code)