Files
TUI-WM/src/config.rs
Bjorn Blomberg 339ad9530e Implement Kitty graphics protocol for background image rendering
- Added `kitty_gfx` module to handle loading, scaling, and displaying background images using the Kitty graphics protocol.
- Integrated background image handling into the main application loop, allowing dynamic updates based on configuration.
- Enhanced terminal rendering to support transparent backgrounds when a Kitty image is active.
- Updated IPC server to manage client connections and messages, including handling background image settings.
- Modified `PtyTerminal` to accept additional environment variables during shell spawning.
- Improved rendering logic to support popup dialogs and terminal background color customization.
2026-03-29 04:28:31 +02:00

222 lines
6.4 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

use serde::Deserialize;
use std::fs;
#[derive(Deserialize, Debug, Clone, Default)]
pub struct Config {
/// Sökväg till bakgrundsbild (Kitty graphics protocol)
#[serde(default)]
pub background_image: Option<String>,
/// Bakgrundsfärg för virtuella terminaler. Om None → genomskinlig (Color::Reset).
/// Stödjer: "#RRGGBB", "N" (indexed 0-255), färgnamn.
#[serde(default)]
pub terminal_bg_color: Option<String>,
/// Standard-shell för nya terminaler. Om None → $SHELL eller /bin/bash.
#[serde(default)]
pub default_shell: Option<String>,
#[serde(default, rename = "panel")]
pub panels: Vec<PanelConfig>,
#[serde(default, rename = "keybind")]
pub keybinds: Vec<KeybindConfig>,
}
/// En tangentbords-genväg definierad i config
#[derive(Deserialize, Debug, Clone)]
pub struct KeybindConfig {
/// T.ex. "ctrl+space", "alt+f2", "ctrl+t"
pub key: String,
/// "global" = alltid, "wm" = bara när ingen terminal är fokuserad
#[serde(default)]
pub scope: KeybindScope,
pub action: MenuAction,
/// Visningsnamn som visas i TUI-WM-menyn (valfritt)
#[serde(default)]
pub label: Option<String>,
}
#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum KeybindScope {
/// Aktiveras alltid, oavsett fokus
Global,
/// Aktiveras bara när ingen terminal-ruta är fokuserad
#[default]
Wm,
}
#[derive(Deserialize, Debug, Clone)]
pub struct PanelConfig {
pub position: PanelPosition,
#[serde(default, rename = "item")]
pub items: Vec<MenuItem>,
#[serde(default, rename = "status")]
pub status_widgets: Vec<StatusWidget>,
/// Länk till extern fil med panel-definition
pub file: Option<String>,
}
#[derive(Deserialize, Debug, Clone, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum PanelPosition {
Top,
Bottom,
Left,
Right,
}
#[derive(Deserialize, Debug, Clone)]
pub struct MenuItem {
pub label: String,
pub action: MenuAction,
}
#[derive(Deserialize, Debug, Clone)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum MenuAction {
Exit,
SpawnTerminal {
shell: Option<String>,
},
RunScript {
path: String,
},
RunProgram {
command: String,
#[serde(default)]
args: Vec<String>,
},
Submenu {
#[serde(default, rename = "item")]
items: Vec<MenuItem>,
},
/// Öppnar Tui-run kommandodialog
SpawnRunDialog,
/// Gör ingenting används för display-only rader i menyer
NoOp,
}
/// En status-widget i panelen: kör ett kommando periodiskt och renderar output
#[derive(Deserialize, Debug, Clone)]
pub struct StatusWidget {
/// Shell-kommando som producerar en sträng (t.ex. `date '+%H:%M:%S'`)
pub command: String,
/// Hur ofta kommandot körs, i sekunder
#[serde(default = "default_interval")]
pub interval: u64,
/// Hur många tecken brett blocket är
pub width: u16,
/// Dockas mot höger eller vänster i panelen
#[serde(default)]
pub align: StatusAlign,
}
#[derive(Deserialize, Debug, Clone, Default, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum StatusAlign {
Left,
#[default]
Right,
}
fn default_interval() -> u64 {
10
}
fn default_keybinds() -> Vec<KeybindConfig> {
vec![
KeybindConfig {
key: "ctrl+space".to_string(),
scope: KeybindScope::Global,
action: MenuAction::SpawnRunDialog,
label: Some("Öppna kommandodialog".to_string()),
},
KeybindConfig {
key: "alt+enter".to_string(),
scope: KeybindScope::Global,
action: MenuAction::SpawnTerminal { shell: None },
label: Some("Ny terminal".to_string()),
},
KeybindConfig {
key: "ctrl+q".to_string(),
scope: KeybindScope::Wm,
action: MenuAction::Exit,
label: Some("Avsluta TUI-WM".to_string()),
},
]
}
fn default_panels() -> Vec<PanelConfig> {
vec![PanelConfig {
position: PanelPosition::Top,
items: vec![
MenuItem {
label: "Terminal".to_string(),
action: MenuAction::SpawnTerminal { shell: Some("/bin/bash".to_string()) },
},
MenuItem {
label: "Avsluta".to_string(),
action: MenuAction::Exit,
},
],
status_widgets: vec![],
file: None,
}]
}
impl Config {
pub fn load(path: &str) -> anyhow::Result<Config> {
let content = fs::read_to_string(path)?;
let mut config: Config = toml::from_str(&content)?;
for panel in &mut config.panels {
if let Some(file) = panel.file.clone() {
let c = fs::read_to_string(&file)
.map_err(|e| anyhow::anyhow!("Kunde inte läsa {}: {}", file, e))?;
let ext: ExternalPanelConfig = toml::from_str(&c)
.map_err(|e| anyhow::anyhow!("Parse-fel i {}: {}", file, e))?;
panel.items = ext.items;
panel.status_widgets = ext.status_widgets;
}
}
Ok(config)
}
pub fn load_or_default(path: &str) -> Config {
match Config::load(path) {
Ok(c) => c,
Err(e) => {
eprintln!("Varning: kunde inte ladda {}: {}", path, e);
Config {
background_image: None,
terminal_bg_color: None,
default_shell: None,
panels: default_panels(),
keybinds: default_keybinds(),
}
}
}
}
/// Uppdatera background_image i config-filen (TOML)
pub fn save_background(path: &str, image_path: Option<&str>) -> anyhow::Result<()> {
let content = fs::read_to_string(path).unwrap_or_default();
let mut doc: toml_edit::DocumentMut = content.parse().unwrap_or_default();
match image_path {
Some(p) => {
doc["background_image"] = toml_edit::value(p);
}
None => {
doc.remove("background_image");
}
}
fs::write(path, doc.to_string())?;
Ok(())
}
}
#[derive(Deserialize)]
struct ExternalPanelConfig {
#[serde(default, rename = "item")]
items: Vec<MenuItem>,
#[serde(default, rename = "status")]
status_widgets: Vec<StatusWidget>,
}