use anyhow::{Result, anyhow};
use hidapi::{HidDevice, HidError, HidResult};
use lzma_rust2::XzReader;
use serde_json::Value;
use std::cmp::min;
use std::io::Read;
use std::string::FromUtf8Error;
use thiserror::Error;
use crate::keycodes;
mod key_override;
pub use crate::protocol::key_override::{
KeyOverride, key_overrides_to_json, load_key_overrides, load_key_overrides_from_json,
set_key_override,
};
mod alt_repeat;
pub use crate::protocol::alt_repeat::{
AltRepeat, alt_repeats_to_json, load_alt_repeats, load_alt_repeats_from_json, set_alt_repeat,
};
mod tap_dance;
pub use crate::protocol::tap_dance::{
TapDance, load_tap_dances, load_tap_dances_from_json, set_tap_dance, tap_dances_to_json,
};
mod combo;
pub use crate::protocol::combo::{
Combo, combos_to_json, load_combos, load_combos_from_json, set_combo,
};
mod r#macro;
pub use crate::protocol::r#macro::{
Macro, load_macros, load_macros_from_json, macros_to_json, set_macros,
};
mod qmk_settings;
pub use crate::protocol::qmk_settings::{
get_qmk_value, load_qmk_definitions, load_qmk_qsids, load_qmk_settings,
load_qmk_settings_from_json, qmk_settings_to_json, reset_qmk_values, set_qmk_value,
};
mod rgb;
pub use crate::protocol::rgb::{load_rgb_info, persist_rgb, set_leds_direct, set_rgb_mode};
mod layout;
pub use crate::protocol::layout::{LayoutOptions, load_layout_options, set_layout_options};
mod encoder;
pub use crate::protocol::encoder::{
Encoder, encoders_to_json, load_encoder, load_encoders_from_json, set_encoder,
};
pub const USAGE_PAGE: u16 = 0xFF60;
pub const USAGE_ID: u16 = 0x61;
pub const MESSAGE_LENGTH: usize = 32;
pub const HID_LAYERS_IN: u8 = 0x88;
pub const GET_VERSION: u8 = 0x00;
pub const HID_LAYERS_OUT_VERSION: u8 = 0x91;
pub const VIAL_PROTOCOL_DYNAMIC: u32 = 4;
pub const VIAL_PROTOCOL_QMK_SETTINGS: u32 = 4;
pub const CMD_VIA_GET_PROTOCOL_VERSION: u8 = 0x01;
pub const CMD_VIA_GET_KEYBOARD_VALUE: u8 = 0x02;
pub const CMD_VIA_SET_KEYBOARD_VALUE: u8 = 0x03;
pub const CMD_VIA_BOOTLOADER_JUMP: u8 = 0x0B;
pub const CMD_VIA_VIAL_PREFIX: u8 = 0xFE;
pub const CMD_VIAL_GET_KEYBOARD_ID: u8 = 0x00;
pub const CMD_VIAL_GET_SIZE: u8 = 0x01;
pub const CMD_VIAL_GET_DEFINITION: u8 = 0x02;
pub const CMD_VIA_SET_KEYCODE: u8 = 0x05;
pub const CMD_VIA_GET_LAYER_COUNT: u8 = 0x11;
pub const CMD_VIA_KEYMAP_GET_BUFFER: u8 = 0x12;
pub const VIA_UNHANDLED: u8 = 0xFF;
pub const VIA_LAYOUT_OPTIONS: u8 = 0x02;
pub const VIA_SWITCH_MATRIX_STATE: u8 = 0x03;
pub const CMD_VIAL_DYNAMIC_ENTRY_OP: u8 = 0x0D;
pub const DYNAMIC_VIAL_GET_NUMBER_OF_ENTRIES: u8 = 0x00;
pub const DYNAMIC_VIAL_TAP_DANCE_GET: u8 = 0x01;
pub const DYNAMIC_VIAL_TAP_DANCE_SET: u8 = 0x02;
pub const DYNAMIC_VIAL_COMBO_GET: u8 = 0x03;
pub const DYNAMIC_VIAL_COMBO_SET: u8 = 0x04;
pub const DYNAMIC_VIAL_KEY_OVERRIDE_GET: u8 = 0x05;
pub const DYNAMIC_VIAL_KEY_OVERRIDE_SET: u8 = 0x06;
pub const DYNAMIC_VIAL_ALT_REPEAT_KEY_GET: u8 = 0x07;
pub const DYNAMIC_VIAL_ALT_REPEAT_KEY_SET: u8 = 0x08;
pub const CMD_VIAL_QMK_SETTINGS_QUERY: u8 = 0x09;
pub const CMD_VIAL_QMK_SETTINGS_GET: u8 = 0x0A;
pub const CMD_VIAL_QMK_SETTINGS_SET: u8 = 0x0B;
pub const CMD_VIAL_QMK_SETTINGS_RESET: u8 = 0x0C;
pub const CMD_VIA_MACRO_GET_COUNT: u8 = 0x0C;
pub const CMD_VIA_MACRO_GET_BUFFER_SIZE: u8 = 0x0D;
pub const CMD_VIA_MACRO_GET_BUFFER: u8 = 0x0E;
pub const CMD_VIA_MACRO_SET_BUFFER: u8 = 0x0F;
pub const CMD_VIAL_GET_ENCODER: u8 = 0x03;
pub const CMD_VIAL_SET_ENCODER: u8 = 0x04;
pub const CMD_VIAL_GET_UNLOCK_STATUS: u8 = 0x05;
pub const CMD_VIAL_UNLOCK_START: u8 = 0x06;
pub const CMD_VIAL_UNLOCK_POLL: u8 = 0x07;
pub const CMD_VIAL_LOCK: u8 = 0x08;
pub const CMD_VIA_LIGHTING_SET_VALUE: u8 = 0x07;
pub const CMD_VIA_LIGHTING_GET_VALUE: u8 = 0x08;
pub const CMD_VIA_LIGHTING_SAVE: u8 = 0x09;
pub const VIALRGB_GET_INFO: u8 = 0x40;
pub const VIALRGB_GET_MODE: u8 = 0x41;
pub const VIALRGB_SET_MODE: u8 = 0x41;
pub const VIALRGB_GET_SUPPORTED: u8 = 0x42;
pub const VIALRGB_GET_NUMBER_LEDS: u8 = 0x43;
pub const VIALRGB_DIRECT_FASTSET: u8 = 0x42;
const BUFFER_FETCH_CHUNK: u8 = 28;
#[derive(Error, Debug)]
pub enum ProtocolError {
#[error("ViaUnhandledError")]
ViaUnhandledError,
#[error("HidError {0}")]
HidError(#[from] HidError),
#[error("UTF8Error {0}")]
FromUtf8Error(#[from] FromUtf8Error),
#[error("JsonError {0}")]
SerdeJsonError(#[from] serde_json::Error),
#[error("Error {0}")]
General(String),
}
pub fn send(device: &HidDevice, data: &[u8]) -> HidResult<usize> {
let mut buff: [u8; MESSAGE_LENGTH + 1] = [0u8; MESSAGE_LENGTH + 1];
buff[1..(data.len() + 1)].copy_from_slice(data);
device.write(&buff)
}
pub fn recv(device: &HidDevice) -> HidResult<[u8; MESSAGE_LENGTH]> {
let mut buff: [u8; MESSAGE_LENGTH] = [0u8; MESSAGE_LENGTH];
match device.read_timeout(&mut buff, 500) {
Ok(_size) => Ok(buff),
Err(e) => Err(e),
}
}
pub fn send_recv(device: &HidDevice, data_out: &[u8]) -> Result<[u8; MESSAGE_LENGTH]> {
let mut attempts = 5;
loop {
match send(device, data_out) {
Ok(_) => {
}
Err(e) => {
return Err(e.into());
}
}
match recv(device) {
Ok(data) => {
return Ok(data);
}
Err(e) => {
attempts -= 1;
println!("hid recv error {:?}, {:?} attempts remaining", &e, attempts);
if attempts == 0 {
return Err(e.into());
}
}
}
}
}
#[derive(Debug)]
pub struct Capabilities {
pub via_version: u8,
pub vial_version: u32,
pub companion_hid_version: u8,
pub layer_count: u8,
pub tap_dance_count: u8,
pub combo_count: u8,
pub key_override_count: u8,
pub alt_repeat_key_count: u8,
pub macro_count: u8,
pub macro_buffer_size: u16,
pub caps_word: bool,
pub layer_lock: bool,
}
pub fn scan_capabilities(device: &HidDevice) -> Result<Capabilities> {
let vial_version;
let companion_hid_version;
let layer_count;
let macro_count;
let macro_buffer_size;
let via_version = send_recv(device, &[CMD_VIA_GET_PROTOCOL_VERSION])?[2];
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_KEYBOARD_ID]) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
vial_version = ((buff[3] as u32) << 24)
+ ((buff[2] as u32) << 16)
+ ((buff[1] as u32) << 8)
+ buff[0] as u32
} else {
vial_version = 0
}
}
Err(e) => return Err(e),
}
match send_recv(device, &[HID_LAYERS_IN, GET_VERSION]) {
Ok(buff) => {
if buff[0] == HID_LAYERS_OUT_VERSION {
companion_hid_version = buff[1]
} else {
companion_hid_version = 0
}
}
Err(e) => return Err(e),
}
if via_version == 0 {
layer_count = 0;
} else {
match send_recv(device, &[CMD_VIA_GET_LAYER_COUNT]) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
layer_count = buff[1]
} else {
layer_count = 0
}
}
Err(e) => return Err(e),
}
}
match send_recv(device, &[CMD_VIA_MACRO_GET_COUNT]) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
macro_count = buff[1]
} else {
macro_count = 0
}
}
Err(e) => return Err(e),
}
match send_recv(device, &[CMD_VIA_MACRO_GET_BUFFER_SIZE]) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
macro_buffer_size = ((buff[1] as u16) << 8) + (buff[2] as u16);
} else {
macro_buffer_size = 0
}
}
Err(e) => return Err(e),
}
if vial_version < VIAL_PROTOCOL_DYNAMIC {
return Ok(Capabilities {
via_version,
vial_version,
companion_hid_version,
layer_count,
macro_count,
macro_buffer_size,
tap_dance_count: 0,
combo_count: 0,
key_override_count: 0,
alt_repeat_key_count: 0,
caps_word: false,
layer_lock: false,
});
}
match send_recv(
device,
&[
CMD_VIA_VIAL_PREFIX,
CMD_VIAL_DYNAMIC_ENTRY_OP,
DYNAMIC_VIAL_GET_NUMBER_OF_ENTRIES,
],
) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
Ok(Capabilities {
via_version,
vial_version,
companion_hid_version,
layer_count,
macro_count,
macro_buffer_size,
tap_dance_count: buff[0],
combo_count: buff[1],
key_override_count: buff[2],
alt_repeat_key_count: buff[3],
caps_word: buff[31] & 1 != 0,
layer_lock: buff[31] & 2 != 0,
})
} else {
Err(ProtocolError::ViaUnhandledError.into())
}
}
Err(e) => Err(e),
}
}
pub fn load_vial_meta(device: &HidDevice) -> Result<Value> {
let meta_size: u32;
let mut block: u32;
let mut remaining_size: i64;
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_SIZE]) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
meta_size = ((buff[3] as u32) << 24)
+ ((buff[2] as u32) << 16)
+ ((buff[1] as u32) << 8)
+ buff[0] as u32;
} else {
return Err(ProtocolError::ViaUnhandledError.into());
}
}
Err(e) => return Err(e),
}
remaining_size = meta_size as i64;
block = 0;
let mut raw_meta = Vec::new();
while remaining_size > 0 {
let block1 = (block >> 24 & 0xFF) as u8;
let block2 = (block >> 16 & 0xFF) as u8;
let block3 = (block >> 8 & 0xFF) as u8;
let block4 = (block & 0xFF) as u8;
match send_recv(
device,
&[
CMD_VIA_VIAL_PREFIX,
CMD_VIAL_GET_DEFINITION,
block4,
block3,
block2,
block1,
],
) {
Ok(buff) => {
if remaining_size >= MESSAGE_LENGTH as i64 {
raw_meta.extend_from_slice(&buff);
remaining_size -= MESSAGE_LENGTH as i64;
} else {
raw_meta.extend_from_slice(&buff[0..remaining_size as usize]);
remaining_size = 0;
}
}
Err(e) => return Err(e),
}
block += 1;
}
let mut reader = XzReader::new(raw_meta.as_slice(), true);
let mut buf = [0; 1024];
let mut out = Vec::new();
loop {
let n = reader.read(&mut buf)?;
if n == 0 {
break;
}
out.extend_from_slice(&buf[..n]);
}
let meta_str = String::from_utf8(out)?;
let meta: Value = serde_json::from_str(&meta_str)?;
Ok(meta)
}
#[derive(Debug)]
pub struct Keymap {
rows: u8,
cols: u8,
layers: u8,
keys: Vec<u8>,
}
#[allow(dead_code)]
#[derive(Error, Debug)]
#[error("{0}")]
pub struct KeymapError(String);
impl Keymap {
pub fn from_json(
rows: u8,
cols: u8,
layers: u8,
layers_data: &Vec<Value>,
vial_version: u32,
) -> Result<Keymap> {
let mut keys = Vec::<u8>::new();
for layer in layers_data {
for row in layer
.as_array()
.ok_or(anyhow!("layer content should be array of rows"))?
{
for value in row
.as_array()
.ok_or(anyhow!("row content should be array of keycodes"))?
{
let keycode: u16 = match value {
Value::Number(_) => 0,
Value::String(value) => {
if value.starts_with("0x") {
let (_, hex) = value
.split_once("x")
.ok_or(anyhow!("Incorrect hex encoding"))?;
u16::from_str_radix(hex, 16)?
} else {
keycodes::name_to_qid(value, vial_version)?
}
}
_ => {
return Err(KeymapError(
"keycode should be number or string".to_string(),
)
.into());
}
};
keys.push((keycode >> 8) as u8);
keys.push((keycode & 0xFF) as u8);
}
}
}
Ok(Keymap {
rows,
cols,
layers,
keys,
})
}
pub fn to_json(&self, vial_version: u32) -> Result<Value> {
let mut result = Vec::new();
for layer_num in 0..self.layers {
let mut layer = Vec::new();
for row_num in 0..self.rows {
let mut row = Vec::new();
for col_num in 0..self.cols {
row.push(Value::String(self.get_long(
layer_num,
row_num,
col_num,
vial_version,
)?));
}
layer.push(Value::Array(row));
}
result.push(Value::Array(layer));
}
Ok(Value::Array(result))
}
pub fn get_short(&self, layer: u8, row: u8, col: u8, vial_version: u32) -> Result<String> {
if layer >= self.layers {
Err(KeymapError("non existing layer requested".to_string()).into())
} else if row >= self.rows {
Err(KeymapError("non existing row requested".to_string()).into())
} else if col >= self.cols {
Err(KeymapError("non existing col requested".to_string()).into())
} else {
let offset = (layer as usize * self.rows as usize * self.cols as usize * 2)
+ (row as usize * self.cols as usize * 2)
+ (col as usize * 2);
let v1 = self.keys[offset];
let v2 = self.keys[offset + 1];
let kk = ((v1 as u16) << 8) + (v2 as u16);
Ok(keycodes::qid_to_short(kk, vial_version))
}
}
pub fn get_long(&self, layer: u8, row: u8, col: u8, vial_version: u32) -> Result<String> {
if layer >= self.layers {
Err(KeymapError("non existing layer requested".to_string()).into())
} else if row >= self.rows {
Err(KeymapError("non existing row requested".to_string()).into())
} else if col >= self.cols {
Err(KeymapError("non existing col requested".to_string()).into())
} else {
let offset = (layer as usize * self.rows as usize * self.cols as usize * 2)
+ (row as usize * self.cols as usize * 2)
+ (col as usize * 2);
let v1 = self.keys[offset];
let v2 = self.keys[offset + 1];
let kk = ((v1 as u16) << 8) + (v2 as u16);
Ok(keycodes::qid_to_name(kk, vial_version))
}
}
pub fn get(&self, layer: u8, row: u8, col: u8) -> u16 {
let offset = (layer as usize * self.rows as usize * self.cols as usize * 2)
+ (row as usize * self.cols as usize * 2)
+ (col as usize * 2);
let v1 = self.keys[offset];
let v2 = self.keys[offset + 1];
((v1 as u16) << 8) + (v2 as u16)
}
}
pub fn load_layers_keys(device: &HidDevice, layers: u8, rows: u8, cols: u8) -> Result<Keymap> {
let size: u16 = layers as u16 * rows as u16 * cols as u16 * 2;
let mut keys = Vec::new();
let mut offset: u16 = 0;
while offset < size {
let read_size: u8 = min(size - offset, BUFFER_FETCH_CHUNK as u16) as u8;
let offset1 = ((offset >> 8) & 0xFF) as u8;
let offset2 = (offset & 0xFF) as u8;
match send_recv(
device,
&[CMD_VIA_KEYMAP_GET_BUFFER, offset1, offset2, read_size],
) {
Ok(buff) => {
if buff[0] != VIA_UNHANDLED {
keys.extend_from_slice(&buff[4..(read_size + 4) as usize]);
} else {
println!("UNHANDLED");
}
}
Err(e) => return Err(e),
}
offset += read_size as u16;
}
Ok(Keymap {
layers,
rows,
cols,
keys,
})
}
pub fn set_keycode(device: &HidDevice, layer: u8, row: u8, col: u8, keycode: u16) -> Result<()> {
let kk1 = ((keycode >> 8) & 0xFF) as u8;
let kk2 = (keycode & 0xFF) as u8;
match send(device, &[CMD_VIA_SET_KEYCODE, layer, row, col, kk1, kk2]) {
Ok(_) => Ok(()),
Err(e) => Err(ProtocolError::HidError(e).into()),
}
}
pub fn set_keymap(device: &HidDevice, keymap: &Keymap) -> Result<()> {
for layer_num in 0..keymap.layers {
for row_num in 0..keymap.rows {
for col_num in 0..keymap.cols {
let kk = keymap.get(layer_num, row_num, col_num);
set_keycode(device, layer_num, row_num, col_num, kk)?;
}
}
}
Ok(())
}
#[derive(Debug)]
pub struct LockedStatus {
pub locked: bool,
pub unlock_in_progress: bool,
pub unlock_buttons: Vec<(u8, u8)>,
}
pub fn get_locked_status(device: &HidDevice) -> Result<LockedStatus> {
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_GET_UNLOCK_STATUS]) {
Ok(data) => {
let mut unlock_buttons = Vec::new();
let locked = data[0] == 0;
let unlock_in_progress = data[1] == 1;
for i in 0..15 {
let row = data[2 + i * 2];
let col = data[3 + i * 2];
if row != 255 && col != 255 {
unlock_buttons.push((row, col))
}
}
Ok(LockedStatus {
locked,
unlock_in_progress,
unlock_buttons,
})
}
Err(e) => Err(e),
}
}
pub fn start_unlock(device: &HidDevice) -> Result<()> {
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_UNLOCK_START]) {
Ok(_) => {
Ok(())
}
Err(e) => Err(e),
}
}
pub fn unlock_poll(device: &HidDevice) -> Result<(bool, u8)> {
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_UNLOCK_POLL]) {
Ok(data) => {
let unlocked = data[0] == 1;
let seconds_remaining = data[2];
Ok((unlocked, seconds_remaining))
}
Err(e) => Err(e),
}
}
pub fn set_locked(device: &HidDevice) -> Result<()> {
match send_recv(device, &[CMD_VIA_VIAL_PREFIX, CMD_VIAL_LOCK]) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}
pub fn load_uid(device: &HidDevice) -> Result<u64> {
match send_recv(device, &[CMD_VIA_VIAL_PREFIX]) {
Ok(data) => {
let mut uid_bytes: [u8; 8] = [0; 8];
uid_bytes.copy_from_slice(&data[4..12]);
let uid: u64 = u64::from_le_bytes(uid_bytes);
Ok(uid)
}
Err(e) => Err(e),
}
}
#[derive(Debug, PartialEq)]
pub struct MatrixState {
rows: u8,
cols: u8,
data: [u8; 32],
}
impl MatrixState {
pub fn is_pushed(&self, row: u8, col: u8, via_version: u8) -> Result<bool> {
let shift = if via_version < 12 { 2 } else { 3 };
let row_size = (((self.cols as f64) / 8.0).ceil()) as u8;
let row_data_start = shift + (row * row_size);
let row_data_end = row_data_start + row_size;
let row_data: &[u8] = &self.data[row_data_start as usize..row_data_end as usize];
let col_byte = row_data.len() - 1 - (col as f64 / 8.0).floor() as usize;
let col_mod = col % 8;
Ok((row_data[col_byte] >> col_mod) & 1 != 0)
}
}
pub fn matrix_poll(device: &HidDevice, rows: u8, cols: u8) -> Result<MatrixState> {
match send_recv(
device,
&[CMD_VIA_GET_KEYBOARD_VALUE, VIA_SWITCH_MATRIX_STATE],
) {
Ok(data) => Ok(MatrixState { rows, cols, data }),
Err(e) => Err(e),
}
}
pub fn bootloader_jump(device: &HidDevice) -> Result<()> {
match send_recv(device, &[CMD_VIA_BOOTLOADER_JUMP]) {
Ok(_) => Ok(()),
Err(e) => Err(e),
}
}