Reliable letter-based default keybinds + kitty keyboard protocol
alt+tab is grabbed by desktop WMs and alt+arrows are re-encoded or swallowed by several terminals, leaking plain arrow keys into the focused window. Primary defaults are now letter chords which every terminal delivers via ESC-prefix: alt+w (switcher), alt+h/alt+l (snap), alt+m (maximize). The alt+tab/arrow variants stay as extra bindings, and standalone/client now enable the kitty keyboard protocol (DISAMBIGUATE_ESCAPE_CODES) when the terminal supports it so even those are delivered unambiguously (Konsole 23.08+, kitty, foot, wezterm, ghostty). 34/34 integration checks. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -7,8 +7,8 @@ En terminal-baserad fönsterhanterare med flytande fönster, full mussuport och
|
||||
- Flytande terminaler med fri positionering och storleksändring
|
||||
- Full mussuport: vänster/höger/mitten-klick, scroll, drag, ANSI-muse-protokoll vidarebefordras till appar
|
||||
- Scrollback-historik med scrollbar + copy-mode med sökning (`alt+c`)
|
||||
- Fönsterväxlare i MRU-ordning (`alt+tab`/`alt+w`)
|
||||
- Snap till halvskärm (`alt+left`/`alt+right` eller dra mot kanten) och maximera (`alt+up`/dubbelklick på titelraden)
|
||||
- Fönsterväxlare i MRU-ordning (`alt+w`, även `alt+tab` där den inte fångas av skrivbordet)
|
||||
- Snap till halvskärm (`alt+h`/`alt+l` eller dra mot kanten) och maximera (`alt+m`/dubbelklick på titelraden) — alt+pilar fungerar också i terminaler med kitty keyboard-protokoll
|
||||
- Teman i egna TOML-filer (`themes/`) + inbyggd inställningsdialog (kugghjulet `≡` i panelen)
|
||||
- Hot-reload: config.toml, panel- och temafiler laddas om live när de ändras
|
||||
- Panel (topp/botten) med klickbara knappar och dynamiska status-widgets
|
||||
|
||||
@@ -45,34 +45,49 @@ label = "Ny terminal"
|
||||
action = { type = "spawn_terminal" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+tab"
|
||||
key = "alt+w"
|
||||
scope = "global"
|
||||
label = "Fönsterväxlare"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+w"
|
||||
key = "alt+tab"
|
||||
scope = "global"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+left"
|
||||
key = "alt+h"
|
||||
scope = "global"
|
||||
label = "Fäst vänster"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+right"
|
||||
key = "alt+left"
|
||||
scope = "global"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+l"
|
||||
scope = "global"
|
||||
label = "Fäst höger"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
key = "alt+right"
|
||||
scope = "global"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+m"
|
||||
scope = "global"
|
||||
label = "Maximera/återställ"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
scope = "global"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+c"
|
||||
scope = "global"
|
||||
|
||||
31
config.toml
31
config.toml
@@ -44,37 +44,54 @@ scope = "global"
|
||||
label = "Ny terminal"
|
||||
action = { type = "spawn_terminal" }
|
||||
|
||||
# Fönsterväxlare (Alt+Tab-overlay i MRU-ordning).
|
||||
# OBS: vissa terminaler/skrivbord fångar alt+tab själva — alt+w är reserv.
|
||||
# Fönsterväxlare (MRU-ordning). alt+w är primär — alt+tab fångas ofta
|
||||
# av skrivbordsmiljön och alt+pilar av vissa terminaler; bokstavs-binds
|
||||
# fungerar överallt. (I terminaler med kitty keyboard-protokoll
|
||||
# levereras även alt+tab/pilar korrekt.)
|
||||
[[keybind]]
|
||||
key = "alt+tab"
|
||||
key = "alt+w"
|
||||
scope = "global"
|
||||
label = "Fönsterväxlare"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+w"
|
||||
key = "alt+tab"
|
||||
scope = "global"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
# Fäst fokuserat fönster som vänster/höger halvskärm
|
||||
[[keybind]]
|
||||
key = "alt+left"
|
||||
key = "alt+h"
|
||||
scope = "global"
|
||||
label = "Fäst vänster"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+right"
|
||||
key = "alt+left"
|
||||
scope = "global"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+l"
|
||||
scope = "global"
|
||||
label = "Fäst höger"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+right"
|
||||
scope = "global"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
# Maximera/återställ fokuserat fönster (även dubbelklick på titelraden)
|
||||
[[keybind]]
|
||||
key = "alt+m"
|
||||
scope = "global"
|
||||
label = "Maximera/återställ"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
scope = "global"
|
||||
label = "Maximera/återställ"
|
||||
action = { type = "maximize" }
|
||||
|
||||
# Copy-mode: bläddra/söka i fokuserat fönsters historik med tangentbordet
|
||||
|
||||
25
dist/linux/config.toml
vendored
25
dist/linux/config.toml
vendored
@@ -45,34 +45,49 @@ label = "Ny terminal"
|
||||
action = { type = "spawn_terminal" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+tab"
|
||||
key = "alt+w"
|
||||
scope = "global"
|
||||
label = "Fönsterväxlare"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+w"
|
||||
key = "alt+tab"
|
||||
scope = "global"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+left"
|
||||
key = "alt+h"
|
||||
scope = "global"
|
||||
label = "Fäst vänster"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+right"
|
||||
key = "alt+left"
|
||||
scope = "global"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+l"
|
||||
scope = "global"
|
||||
label = "Fäst höger"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
key = "alt+right"
|
||||
scope = "global"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+m"
|
||||
scope = "global"
|
||||
label = "Maximera/återställ"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
scope = "global"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+c"
|
||||
scope = "global"
|
||||
|
||||
25
dist/win/config.toml
vendored
25
dist/win/config.toml
vendored
@@ -45,34 +45,49 @@ label = "Ny PowerShell-terminal"
|
||||
action = { type = "spawn_terminal", shell = "powershell.exe" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+tab"
|
||||
key = "alt+w"
|
||||
scope = "global"
|
||||
label = "Fönsterväxlare"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+w"
|
||||
key = "alt+tab"
|
||||
scope = "global"
|
||||
action = { type = "window_switcher" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+left"
|
||||
key = "alt+h"
|
||||
scope = "global"
|
||||
label = "Fäst vänster"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+right"
|
||||
key = "alt+left"
|
||||
scope = "global"
|
||||
action = { type = "snap_left" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+l"
|
||||
scope = "global"
|
||||
label = "Fäst höger"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
key = "alt+right"
|
||||
scope = "global"
|
||||
action = { type = "snap_right" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+m"
|
||||
scope = "global"
|
||||
label = "Maximera/återställ"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+up"
|
||||
scope = "global"
|
||||
action = { type = "maximize" }
|
||||
|
||||
[[keybind]]
|
||||
key = "alt+c"
|
||||
scope = "global"
|
||||
|
||||
@@ -60,9 +60,21 @@ pub fn run(socket_path: &str) -> io::Result<()> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture, crossterm::cursor::Hide)?;
|
||||
let keyboard_enhanced = crossterm::terminal::supports_keyboard_enhancement().unwrap_or(false);
|
||||
if keyboard_enhanced {
|
||||
let _ = execute!(
|
||||
io::stdout(),
|
||||
crossterm::event::PushKeyboardEnhancementFlags(
|
||||
crossterm::event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
let result = client_loop(&mut stdout, &mut writer, &frame_rx, term_w, term_h);
|
||||
|
||||
if keyboard_enhanced {
|
||||
let _ = execute!(io::stdout(), crossterm::event::PopKeyboardEnhancementFlags);
|
||||
}
|
||||
execute!(
|
||||
stdout,
|
||||
crossterm::cursor::Show,
|
||||
|
||||
@@ -173,35 +173,53 @@ fn default_keybinds() -> Vec<KeybindConfig> {
|
||||
label: Some("Ny terminal".to_string()),
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+tab".to_string(),
|
||||
key: "alt+w".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::WindowSwitcher,
|
||||
label: Some("Fönsterväxlare".to_string()),
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+w".to_string(),
|
||||
key: "alt+tab".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::WindowSwitcher,
|
||||
label: None,
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+left".to_string(),
|
||||
key: "alt+h".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::SnapLeft,
|
||||
label: Some("Fäst vänster".to_string()),
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+right".to_string(),
|
||||
key: "alt+left".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::SnapLeft,
|
||||
label: None,
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+l".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::SnapRight,
|
||||
label: Some("Fäst höger".to_string()),
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+up".to_string(),
|
||||
key: "alt+right".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::SnapRight,
|
||||
label: None,
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+m".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::Maximize,
|
||||
label: Some("Maximera/återställ".to_string()),
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+up".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
action: MenuAction::Maximize,
|
||||
label: None,
|
||||
},
|
||||
KeybindConfig {
|
||||
key: "alt+c".to_string(),
|
||||
scope: KeybindScope::Global,
|
||||
|
||||
26
src/main.rs
26
src/main.rs
@@ -170,12 +170,17 @@ KONFIGURATION (./config.toml, annars ~/.config/tui-wm/config.toml):
|
||||
TANGENTER (standard, ändras via [[keybind]] i config.toml):
|
||||
ctrl+space Kommandodialog — skriv t.ex. `htop` och tryck Enter
|
||||
alt+enter Ny terminal
|
||||
alt+tab/alt+w Fönsterväxlare (MRU) — Tab stegar, Enter väljer
|
||||
alt+left/right Fäst fönster som vänster/höger halvskärm
|
||||
alt+up Maximera/återställ (även dubbelklick på titelraden)
|
||||
alt+w Fönsterväxlare (MRU) — Tab stegar, Enter väljer
|
||||
alt+h/alt+l Fäst fönster som vänster/höger halvskärm
|
||||
alt+m Maximera/återställ (även dubbelklick på titelraden)
|
||||
alt+c Copy-mode: bläddra/söka i historiken (/ n N q)
|
||||
ctrl+q Avsluta (från en ansluten klient: kopplar bara ner den)
|
||||
|
||||
alt+tab och alt+pilar är också bundna men fångas ofta av
|
||||
skrivbordsmiljön/terminalen — bokstavsvarianterna fungerar överallt.
|
||||
I terminaler med kitty keyboard-protokoll (Konsole 23.08+, kitty,
|
||||
foot, wezterm, ghostty) levereras även pil-varianterna korrekt.
|
||||
|
||||
TEMA & INSTÄLLNINGAR:
|
||||
theme = \"themes/dark.toml\" i config.toml väljer tema (egna teman =
|
||||
egna TOML-filer). Kugghjulet ≡ i panelen öppnar inbyggda
|
||||
@@ -203,6 +208,18 @@ fn run_standalone() -> io::Result<()> {
|
||||
enable_raw_mode()?;
|
||||
let mut stdout = io::stdout();
|
||||
execute!(stdout, EnterAlternateScreen, EnableMouseCapture, EnableBracketedPaste, EnableFocusChange)?;
|
||||
// Kitty keyboard-protokoll (om terminalen stödjer det): gör att
|
||||
// alt+pilar, alt+tab m.fl. levereras entydigt i stället för att
|
||||
// kollidera med legacy-escape-sekvenser.
|
||||
let keyboard_enhanced = crossterm::terminal::supports_keyboard_enhancement().unwrap_or(false);
|
||||
if keyboard_enhanced {
|
||||
let _ = execute!(
|
||||
io::stdout(),
|
||||
crossterm::event::PushKeyboardEnhancementFlags(
|
||||
crossterm::event::KeyboardEnhancementFlags::DISAMBIGUATE_ESCAPE_CODES
|
||||
)
|
||||
);
|
||||
}
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
|
||||
@@ -212,6 +229,9 @@ fn run_standalone() -> io::Result<()> {
|
||||
|
||||
let result = run_standalone_loop(&mut terminal, &mut app, ipc_rx, &socket_path);
|
||||
|
||||
if keyboard_enhanced {
|
||||
let _ = execute!(io::stdout(), crossterm::event::PopKeyboardEnhancementFlags);
|
||||
}
|
||||
disable_raw_mode()?;
|
||||
execute!(terminal.backend_mut(), LeaveAlternateScreen, DisableMouseCapture, DisableBracketedPaste, DisableFocusChange)?;
|
||||
terminal.show_cursor()?;
|
||||
|
||||
@@ -346,6 +346,14 @@ def main():
|
||||
maxed = next(w for w in wmx["windows"] if w["id"] == sw_open["id"])
|
||||
check("alt+up maximerar fönstret", maxed["width"] >= 95,
|
||||
f"geometri: {maxed}")
|
||||
# Bokstavs-varianten: alt+m ska toggla tillbaka (återställa)
|
||||
disp.send(key_event(ch="m", modifiers="ALT"))
|
||||
time.sleep(0.3)
|
||||
app.send({"type": "list_windows", "request_id": "lrest"})
|
||||
wre = app.recv_until("window_list")
|
||||
restored = next(w for w in wre["windows"] if w["id"] == sw_open["id"])
|
||||
check("alt+m återställer (bokstavs-bind)", 45 <= restored["width"] <= 55,
|
||||
f"geometri: {restored}")
|
||||
app.send({"type": "close_window", "window_id": sw_open["id"]})
|
||||
|
||||
# ── 4f. Copy-mode: alt+c, sök, avsluta ───────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user