From 5266c3d0ce08cc52d30db103f3750822e30a2ef3 Mon Sep 17 00:00:00 2001 From: Sisyphus Agent Date: Tue, 7 Jul 2026 14:05:55 +0300 Subject: [PATCH] redbear-power: wire keybinding config into key dispatch Add KeyBindings::char_for() and replace the 7 hardcoded Key::Char arms in main.rs (quit, cycle_governor, refresh_now, toggle_help, snapshot, benchmark_start, benchmark_stop). Esc remains a quit fallback. All other keys stay hardcoded. Build, 224 tests, clippy, and fmt clean. --- .../system/redbear-power/source/src/config.rs | 21 +++++++++++++++++++ .../system/redbear-power/source/src/main.rs | 19 ++++++++++------- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/local/recipes/system/redbear-power/source/src/config.rs b/local/recipes/system/redbear-power/source/src/config.rs index 86421faa82..a887960872 100644 --- a/local/recipes/system/redbear-power/source/src/config.rs +++ b/local/recipes/system/redbear-power/source/src/config.rs @@ -117,6 +117,27 @@ impl Default for KeyBindings { } } +impl KeyBindings { + /// Return the first character configured for a command, if any. + /// Recognized commands: `quit`, `cycle_governor`, `refresh_now`, + /// `toggle_help`, `snapshot`, `benchmark_start`, `benchmark_stop`. + /// Multi-character strings return only the first char; unrecognized + /// commands return `None`. + pub fn char_for(&self, command: &str) -> Option { + let s = match command { + "quit" => &self.quit, + "cycle_governor" => &self.cycle_governor, + "refresh_now" => &self.refresh_now, + "toggle_help" => &self.toggle_help, + "snapshot" => &self.snapshot, + "benchmark_start" => &self.benchmark_start, + "benchmark_stop" => &self.benchmark_stop, + _ => return None, + }; + s.chars().next() + } +} + #[derive(Clone, Debug, Deserialize)] #[serde(default)] pub struct BenchmarkConfig { diff --git a/local/recipes/system/redbear-power/source/src/main.rs b/local/recipes/system/redbear-power/source/src/main.rs index ad9fea57a9..6723602578 100644 --- a/local/recipes/system/redbear-power/source/src/main.rs +++ b/local/recipes/system/redbear-power/source/src/main.rs @@ -332,6 +332,7 @@ fn main() -> io::Result<()> { POLL_MS }); app.poll_ms = poll.as_millis() as u64; + let kb = &cfg.keybindings; let mut show_help = false; // Tab/BackTab cycles keyboard focus between header / table / controls. let mut focused_panel: usize = 1; @@ -912,7 +913,9 @@ fn main() -> io::Result<()> { Key::Esc if app.pid_detail.is_some() => { app.pid_detail = None; } - Key::Char('q') | Key::Esc => { + _ if k == Key::Esc + || matches!(k, Key::Char(c) if kb.char_for("quit") == Some(c)) => + { if bench.running { bench.stop(); } @@ -960,7 +963,7 @@ fn main() -> io::Result<()> { Key::Char('8') => app.set_tab(app::TabId::Storage), Key::Char('9') => app.set_tab(app::TabId::Process), Key::Char('T') => app.set_tab(app.current_tab.next()), - Key::Char('?') => show_help = !show_help, + Key::Char(c) if kb.char_for("toggle_help") == Some(c) => show_help = !show_help, Key::Down | Key::Char('j') if show_help => { app.help_scroll = app.help_scroll.saturating_add(1); } @@ -979,7 +982,9 @@ fn main() -> io::Result<()> { Key::End | Key::Char('G') if show_help => { app.help_scroll = 200; } - Key::Char('g') => app.cycle_governor(), + Key::Char(c) if kb.char_for("cycle_governor") == Some(c) => { + app.cycle_governor() + } Key::Char('p') => app.step_selected_pstate(-1), Key::Char('P') => app.step_selected_pstate(1), Key::Char('m') => app.force_min_pstate(), @@ -1072,12 +1077,12 @@ fn main() -> io::Result<()> { app.flash_status("no process selected"); } } - Key::Char('r') => { + Key::Char(c) if kb.char_for("refresh_now") == Some(c) => { app.refresh(); last_refresh = Instant::now(); app.flash_status("refreshed"); } - Key::Char('c') => { + Key::Char(c) if kb.char_for("snapshot") == Some(c) => { let path = "/tmp/redbear-power-snapshot.txt"; match fs::write(path, snapshot(&app, 140, 50)) { Ok(_) => app.flash_status(format!("snapshot → {path}")), @@ -1143,7 +1148,7 @@ fn main() -> io::Result<()> { interval_input = None; app.flash_status("refresh interval cancelled"); } - Key::Char('b') => { + Key::Char(c) if kb.char_for("benchmark_start") == Some(c) => { bench.start(app.cpus.len(), 30); let cores = if bench.single_core { 1 @@ -1156,7 +1161,7 @@ fn main() -> io::Result<()> { cores )); } - Key::Char('B') => { + Key::Char(c) if kb.char_for("benchmark_stop") == Some(c) => { if bench.running { bench.stop(); app.flash_status(format!(