Files
TUI-WM/src/log.rs
Bjorn Blomberg 9d637a9378 Screenshot feature (text + PNG) and keybind editor tab in settings
- 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>
2026-07-16 00:30:56 +02:00

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)
}