unfettered-keyboard/src/core/config.rs
Frieder Hannenheim f6405cad69
core: config: Add configuration options for keyboard colors
[richard: prefix commit message with code modified and sign]
2025-05-12 18:09:29 -04:00

236 lines
7.6 KiB
Rust

// SPDX-License-Identifier: GPL-3.0-only
/*
* Copyright (c) 2024, Richard Acayan. All rights reserved.
*/
use std::convert::TryInto;
use std::fs::File;
use rgb::alt::BGR;
use rgb::alt::BGRA;
use rgb::RGB;
use yaml_rust2::yaml::Hash;
use yaml_rust2::yaml::LoadError;
use yaml_rust2::yaml::YamlDecoder;
use yaml_rust2::yaml::Yaml;
#[derive(Debug, Clone)]
pub struct KeyboardColors {
pub background: BGRA<u8>,
pub keycap: BGRA<u8>,
pub keycap_pressed: BGRA<u8>,
pub label: BGR<u8>,
pub label_locked: BGR<u8>,
pub label_pressed: BGR<u8>,
pub sublabel: BGR<u8>,
}
impl Default for KeyboardColors {
fn default() -> Self
{
Self {
background: RGB::new(0, 0, 0).into(),
keycap: RGB::new(0, 0, 0).into(),
keycap_pressed: RGB::new(51,51,51).into(),
label: RGB::new(255, 255, 255).into(),
label_locked: RGB::new(25, 255, 0).into(),
label_pressed: RGB::new(255, 153, 0).into(),
sublabel: RGB::new(178, 178, 178).into(),
}
}
}
fn parse_color(yaml: &Yaml) -> Option<RGB<u8>>
{
let mut color_bytes = yaml
.as_vec()?
.iter()
.map_while(|b| b.as_i64())
.map_while(|b| b.try_into().ok());
Some(RGB::new(
color_bytes.next()?,
color_bytes.next()?,
color_bytes.next()?
))
}
#[derive(Debug)]
pub struct Configuration {
longpress: u64,
repeat: u64,
layout: String,
extra_keys: Vec<String>,
wayland_height: i32,
wayland_im_enable: bool,
colors: KeyboardColors,
}
impl Configuration {
fn load_wayland(&mut self, yaml: &Hash)
{
let height = yaml.get(&Yaml::String(String::from("height")));
if let Some(height) = height {
let height = match height.as_i64() {
Some(h) => h.try_into().ok(),
None => None,
};
let height = height.expect("Wayland height should be a 32-bit signed integer");
self.wayland_height = height;
}
let im_enable = yaml.get(&Yaml::String(String::from("input-method")));
if let Some(im_enable) = im_enable {
let im_enable = im_enable.as_bool();
let im_enable = im_enable.expect("Wayland height should be a 32-bit signed integer");
self.wayland_im_enable = im_enable;
}
}
fn load_colors(&mut self, yaml: &Hash)
{
if let Some(keycap) = yaml.get(&Yaml::String(String::from("keycap"))) {
self.colors.keycap = parse_color(keycap)
.expect("Keycap color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(keycap_pressed) = yaml.get(&Yaml::String(String::from("keycap_pressed"))) {
self.colors.keycap_pressed = parse_color(keycap_pressed)
.expect("Keycap pressed color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(label) = yaml.get(&Yaml::String(String::from("label"))) {
self.colors.label = parse_color(label)
.expect("Label color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(label_pressed) = yaml.get(&Yaml::String(String::from("label_pressed"))) {
self.colors.label_pressed = parse_color(label_pressed)
.expect("Label pressed color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(label_locked) = yaml.get(&Yaml::String(String::from("label_locked"))) {
self.colors.label_locked = parse_color(label_locked)
.expect("Label locked color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(sublabel) = yaml.get(&Yaml::String(String::from("sublabel"))) {
self.colors.sublabel = parse_color(sublabel)
.expect("Sublabel color needs to be a list of 3 8-bit unsigned integers")
.into();
}
if let Some(background) = yaml.get(&Yaml::String(String::from("background"))) {
self.colors.background = parse_color(background)
.expect("Background color needs to be a list of 3 8-bit unsigned integers")
.into();
}
}
pub fn load() -> Result<Self, LoadError>
{
let mut cfg = Configuration {
longpress: 600,
repeat: 25,
layout: String::from("latn_qwerty_us.xml"),
extra_keys: vec![ String::from("alt"), String::from("meta") ],
wayland_height: 185,
wayland_im_enable: true,
colors: KeyboardColors::default(),
};
if let Ok(file) = File::open("/etc/unfettered-keyboard.yaml") {
let yaml = YamlDecoder::read(file).decode()?;
let yaml = yaml[0].as_hash().expect("Top-level configuration should be a YAML mapping");
let longpress = yaml.get(&Yaml::String(String::from("longpress_ms")));
if let Some(longpress) = longpress {
let longpress = match longpress.as_i64() {
Some(l) => l.try_into().ok(),
None => None,
};
let longpress = longpress.expect("Longpress time should be a 64-bit unsigned integer");
cfg.longpress = longpress;
}
let repeat = yaml.get(&Yaml::String(String::from("repeat_ms")));
if let Some(repeat) = repeat {
let repeat = match repeat.as_i64() {
Some(l) => l.try_into().ok(),
None => None,
};
let repeat = repeat.expect("Repeat interval should be a 64-bit unsigned integer");
cfg.repeat = repeat;
}
let layout = yaml.get(&Yaml::String(String::from("layout")));
if let Some(layout) = layout {
let layout = layout.as_str().expect("Layout should be a YAML string");
cfg.layout = layout.to_string();
}
let keys = yaml.get(&Yaml::String(String::from("extra_keys")));
if let Some(keys) = keys {
let keys = keys.as_vec().expect("Extra keys should be a list");
cfg.extra_keys = keys.iter().map(|y| String::from(y.as_str().unwrap())).collect();
cfg.extra_keys.sort_unstable();
}
let wl = yaml.get(&Yaml::String(String::from("wayland")));
if let Some(wl) = wl {
let wl = wl.as_hash().expect("Wayland configuration should be a YAML mapping");
cfg.load_wayland(wl);
}
let colors = yaml.get(&Yaml::String(String::from("colors")));
if let Some(colors) = colors {
let colors = colors.as_hash().expect("Color configuration should be a YAML mapping");
cfg.load_colors(colors);
}
}
Ok(cfg)
}
#[inline(always)]
pub fn longpress_ms(&self) -> u64
{
self.longpress
}
#[inline(always)]
pub fn repeat_ms(&self) -> u64
{
self.repeat
}
#[inline(always)]
pub fn layout(&self) -> &str
{
&self.layout
}
#[inline(always)]
pub fn extra_keys(&self) -> &Vec<String>
{
&self.extra_keys
}
#[inline(always)]
pub fn wayland_height(&self) -> i32
{
self.wayland_height
}
#[inline(always)]
pub fn wayland_im_enable(&self) -> bool
{
self.wayland_im_enable
}
#[inline(always)]
pub fn colors(&self) -> &KeyboardColors
{
&self.colors
}
}