- New screenshot action (default alt+p, bindable like any action): renders the current workspace to a buffer and saves it as plain text and/or PNG (screenshot_format = text|png|both, screenshot_dir configurable, default skärmbilder/ under the config dir). The PNG is rasterized cell by cell with an embedded DejaVu Sans Mono font (vendored, ~340 kB); glyphs missing from the font (color emoji) render as filled boxes. A popup confirms the saved paths - Settings dialog gains a 'Tangenter' row: lists all keybinds with label and scope, Enter captures the next keypress as the new chord (persisted by rewriting [[keybind]] via toml_edit), Delete removes a binding — changes take effect immediately - Integration suite: 48/48 (screenshot files + validity, keybind list, live rebind ctrl+space→alt+z) Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
/// Enkel fil-logger. Loggar till <exe_dir>/logs/<datum>.log
|
|
/// Trådsäker via en global Mutex<File>.
|
|
|
|
use std::fs::{self, OpenOptions};
|
|
use std::io::Write;
|
|
use std::path::PathBuf;
|
|
use std::sync::Mutex;
|
|
use std::time::SystemTime;
|
|
|
|
static LOG: Mutex<Option<std::fs::File>> = Mutex::new(None);
|
|
|
|
/// Initialisera loggern. Anropas en gång från main().
|
|
pub fn init() {
|
|
let dir = log_dir();
|
|
if let Err(e) = fs::create_dir_all(&dir) {
|
|
eprintln!("log::init: kunde inte skapa logg-mapp {:?}: {}", dir, e);
|
|
return;
|
|
}
|
|
let name = today_filename();
|
|
let path = dir.join(name);
|
|
match OpenOptions::new().create(true).append(true).open(&path) {
|
|
Ok(f) => {
|
|
*LOG.lock().unwrap() = Some(f);
|
|
log(&"=== TUI-WM startad ===".to_string());
|
|
}
|
|
Err(e) => eprintln!("log::init: kunde inte öppna {:?}: {}", path, e),
|
|
}
|
|
}
|
|
|
|
/// Skriv en logg-rad med tidsstämpel.
|
|
pub fn log(msg: &str) {
|
|
let ts = timestamp();
|
|
let line = format!("[{}] {}\n", ts, msg);
|
|
if let Ok(mut guard) = LOG.lock() {
|
|
if let Some(f) = guard.as_mut() {
|
|
let _ = f.write_all(line.as_bytes());
|
|
let _ = f.flush();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Makro för bekväm formatering — används som log!("foo {}", bar)
|
|
#[macro_export]
|
|
macro_rules! log {
|
|
($($arg:tt)*) => {
|
|
$crate::log::log(&format!($($arg)*))
|
|
};
|
|
}
|
|
|
|
// ─── Interna hjälpfunktioner ──────────────────────────────────────────────────
|
|
|
|
fn log_dir() -> PathBuf {
|
|
// Lägg logs/ bredvid den körande binären
|
|
let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from("."));
|
|
exe.parent().unwrap_or(std::path::Path::new(".")).join("logs")
|
|
}
|
|
|
|
fn today_filename() -> String {
|
|
// Bygg YYYY-MM-DD från SystemTime utan externa beroenden
|
|
let secs = SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map(|d| d.as_secs())
|
|
.unwrap_or(0);
|
|
let (y, mo, d) = unix_to_ymd(secs);
|
|
format!("{:04}-{:02}-{:02}.log", y, mo, d)
|
|
}
|
|
|
|
pub fn timestamp() -> String {
|
|
let secs = SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map(|d| d.as_secs())
|
|
.unwrap_or(0);
|
|
let subsec_ms = SystemTime::now()
|
|
.duration_since(SystemTime::UNIX_EPOCH)
|
|
.map(|d| d.subsec_millis())
|
|
.unwrap_or(0);
|
|
let (y, mo, d) = unix_to_ymd(secs);
|
|
let h = (secs % 86400) / 3600;
|
|
let m = (secs % 3600) / 60;
|
|
let s = secs % 60;
|
|
format!("{:04}-{:02}-{:02} {:02}:{:02}:{:02}.{:03}", y, mo, d, h, m, s, subsec_ms)
|
|
}
|
|
|
|
/// Konverterar Unix-sekunder till (år, månad, dag). Ingen extern crate nödvändig.
|
|
fn unix_to_ymd(secs: u64) -> (u32, u32, u32) {
|
|
// Algoritm: https://howardhinnant.github.io/date_algorithms.html
|
|
let z = (secs / 86400) as i64 + 719468;
|
|
let era = if z >= 0 { z } else { z - 146096 } / 146097;
|
|
let doe = (z - era * 146097) as u32;
|
|
let yoe = (doe - doe / 1460 + doe / 36524 - doe / 146096) / 365;
|
|
let y = yoe as i64 + era * 400;
|
|
let doy = doe - (365 * yoe + yoe / 4 - yoe / 100);
|
|
let mp = (5 * doy + 2) / 153;
|
|
let d = doy - (153 * mp + 2) / 5 + 1;
|
|
let m = if mp < 10 { mp + 3 } else { mp - 9 };
|
|
let y = if m <= 2 { y + 1 } else { y };
|
|
(y as u32, m, d)
|
|
}
|