- 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.
222 lines
6.4 KiB
Rust
222 lines
6.4 KiB
Rust
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>,
|
||
}
|