use std::{ cell::Cell, process::{Child, Command}, }; use wgui::{ drawing, event::{InternalStateChangeEvent, MouseButton, MouseButtonIndex}, }; use crate::{ backend::input::{HoverResult, PointerHit}, gui::panel::GuiPanel, state::AppState, subsystem::hid::{ALT, CTRL, KeyModifier, META, SHIFT, SUPER, VirtualKey, WheelDelta}, windowing::backend::{FrameMeta, OverlayBackend, RenderResources, ShouldRender}, }; pub mod builder; mod layout; pub const KEYBOARD_NAME: &str = "kbd"; const AUTO_RELEASE_MODS: [KeyModifier; 5] = [SHIFT, CTRL, ALT, SUPER, META]; struct KeyboardBackend { panel: GuiPanel, } impl OverlayBackend for KeyboardBackend { fn init(&mut self, app: &mut AppState) -> anyhow::Result<()> { self.panel.init(app) } fn should_render(&mut self, app: &mut AppState) -> anyhow::Result { self.panel.should_render(app) } fn render(&mut self, app: &mut AppState, rdr: &mut RenderResources) -> anyhow::Result<()> { self.panel.render(app, rdr) } fn frame_meta(&mut self) -> Option { self.panel.frame_meta() } fn pause(&mut self, app: &mut AppState) -> anyhow::Result<()> { self.panel.state.modifiers = 0; app.hid_provider.set_modifiers_routed(0); self.panel.pause(app) } fn resume(&mut self, app: &mut AppState) -> anyhow::Result<()> { self.panel.resume(app)?; self.panel.push_event( app, &wgui::event::Event::InternalStateChange(InternalStateChangeEvent { metadata: 0 }), ); Ok(()) } fn on_pointer(&mut self, app: &mut AppState, hit: &PointerHit, pressed: bool) { self.panel.on_pointer(app, hit, pressed); self.panel.push_event( app, &wgui::event::Event::InternalStateChange(InternalStateChangeEvent { metadata: 0 }), ); } fn on_scroll(&mut self, app: &mut AppState, hit: &PointerHit, delta: WheelDelta) { self.panel.on_scroll(app, hit, delta); } fn on_left(&mut self, app: &mut AppState, pointer: usize) { self.panel.on_left(app, pointer); } fn on_hover(&mut self, app: &mut AppState, hit: &PointerHit) -> HoverResult { self.panel.on_hover(app, hit) } fn get_interaction_transform(&mut self) -> Option { self.panel.get_interaction_transform() } } struct KeyboardState { modifiers: KeyModifier, alt_modifier: KeyModifier, processes: Vec, } const KEY_AUDIO_WAV: &[u8] = include_bytes!("../../res/421581.wav"); fn play_key_click(app: &mut AppState) { app.audio_provider.play(KEY_AUDIO_WAV); } struct KeyState { button_state: KeyButtonData, color: drawing::Color, color2: drawing::Color, border_color: drawing::Color, border: f32, drawn_state: Cell, } #[derive(Debug)] enum KeyButtonData { Key { vk: VirtualKey, pressed: Cell, }, Modifier { modifier: KeyModifier, sticky: Cell, }, Macro { verbs: Vec<(VirtualKey, bool)>, }, Exec { program: String, args: Vec, release_program: Option, release_args: Vec, }, } fn handle_press( app: &mut AppState, key: &KeyState, keyboard: &mut KeyboardState, button: MouseButton, ) { match &key.button_state { KeyButtonData::Key { vk, pressed } => { keyboard.modifiers |= match button.index { MouseButtonIndex::Right => SHIFT, MouseButtonIndex::Middle => keyboard.alt_modifier, _ => 0, }; app.hid_provider.set_modifiers_routed(keyboard.modifiers); app.hid_provider.send_key_routed(*vk, true); pressed.set(true); play_key_click(app); } KeyButtonData::Modifier { modifier, sticky } => { sticky.set(keyboard.modifiers & *modifier == 0); keyboard.modifiers |= *modifier; app.hid_provider.set_modifiers_routed(keyboard.modifiers); play_key_click(app); } KeyButtonData::Macro { verbs } => { for (vk, press) in verbs { app.hid_provider.send_key_routed(*vk, *press); } play_key_click(app); } KeyButtonData::Exec { program, args, .. } => { // Reap previous processes keyboard .processes .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); if let Ok(child) = Command::new(program).args(args).spawn() { keyboard.processes.push(child); } play_key_click(app); } } } fn handle_release(app: &mut AppState, key: &KeyState, keyboard: &mut KeyboardState) -> bool { match &key.button_state { KeyButtonData::Key { vk, pressed } => { pressed.set(false); for m in &AUTO_RELEASE_MODS { if keyboard.modifiers & *m != 0 { keyboard.modifiers &= !*m; } } app.hid_provider.send_key_routed(*vk, false); app.hid_provider.set_modifiers_routed(keyboard.modifiers); true } KeyButtonData::Modifier { modifier, sticky } => { if sticky.get() { false } else { keyboard.modifiers &= !*modifier; app.hid_provider.set_modifiers_routed(keyboard.modifiers); true } } KeyButtonData::Exec { release_program, release_args, .. } => { // Reap previous processes keyboard .processes .retain_mut(|child| !matches!(child.try_wait(), Ok(Some(_)))); if let Some(program) = release_program && let Ok(child) = Command::new(program).args(release_args).spawn() { keyboard.processes.push(child); } true } _ => true, } }