// 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, pub keycap: BGRA, pub keycap_pressed: BGRA, pub label: BGR, pub label_locked: BGR, pub label_pressed: BGR, pub sublabel: BGR, } 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(0, 255, 25).into(), label_pressed: RGB::new(0, 153, 255).into(), sublabel: RGB::new(178, 178, 178).into(), } } } fn parse_color(yaml: &Yaml) -> Option> { 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, evfb_height: f64, wayland_height: i32, wayland_im_enable: bool, colors: KeyboardColors, } impl Configuration { fn get_configuration_file() -> Result> { let xdg_dirs = xdg::BaseDirectories::new()?; let config_file = xdg_dirs.find_config_file("unfettered-keyboard.yaml") .ok_or(std::io::Error::new(std::io::ErrorKind::NotFound, "Config file not found"))?; println!("test{:?}",config_file); Ok(File::open(config_file)?) } fn load_evfb(&mut self, yaml: &Hash) { let height = yaml.get(&Yaml::String(String::from("height"))); if let Some(height) = height { let height = match height.as_f64() { Some(h) => h.try_into().ok(), None => None, }; let height = height.expect("Linux evdev-fbdev height should be a fraction from 0 to 1"); self.evfb_height = height; } } 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 { 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, evfb_height: 0.25, colors: KeyboardColors::default(), }; if let Ok(file) = Self::get_configuration_file() { 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 evfb = yaml.get(&Yaml::String(String::from("evfb"))); if let Some(evfb) = evfb { let evfb = evfb.as_hash().expect("Linux evdev-fbdev configuration should be a YAML mapping"); cfg.load_evfb(evfb); } 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 { &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 evfb_height(&self) -> f64 { self.evfb_height } #[inline(always)] pub fn colors(&self) -> &KeyboardColors { &self.colors } }