Files
roam/azosexwm.org
T
2026-10-11 00:08:44 +03:00

4.5 KiB

azos/exwm azos/exwm

Backlink: azos

Architecture

EXWM feature: azos-core/features/exwm/emacs/config.org (literate), packaged via _pkg.nix (azos-emacs-exwm), enabled by azos.suites.exwm.enable.

The feature registers two independent flake modules:

  • config.flake.modules.homeManager.exwm — packages, .xinitrc=/.login.sh=, udiskie. Pure home-manager, no NixOS dependency — can be applied via standalone home-manager on a non-NixOS distro (e.g. nix-in-userspace on Fedora).
  • config.flake.modules.nixos.exwm + nixos-base — services.xserver, greetd/startx wiring, services.udisks2, system packages (tuigreet, xinit). NixOS-only; on a non-NixOS host this half must be replaced with distro-native equivalents (Fedora's own Xorg/display-manager/udisks2 setup).

Key custom keys:

  • s-r -> exwm-reset -> calls exwm-input-grab-keyboard (switches window to full "line-mode": Emacs captures all keys)
  • C-c C-k (EXWM's own default binding in exwm-mode-map) -> exwm-input-release-keyboard (switches to "char-mode": app captures keys)
  • exwm-input-prefix-keys (config.org ~230): keys that always reach Emacs even in char-mode: C-x C-u C-h M-x M-& M-: M-\ s-d s-m s-r s-s s-q H-l C-w C-c C-@
  • exwm-input-global-keys: s-f2, s-tab, s-w, s-0..9, s-&, always grabbed regardless of mode (system-wide, via exwm-input--global-prefix-keys)

Gotchas

Line-mode does NOT mean "all keys reach Emacs unconditionally"

Even when an EXWM window is in full line-mode (grabbed via exwm-input-grab-keyboard, an actual XGrabKey Modifiers:Any Key:Any on that window), EXWM's dispatcher (exwm-input--on-KeyPress-line-mode -> exwm-input--event-passthrough-p) still filters every keypress. A key is only sent to Emacs's command loop if:

  • it's in exwm-input--global-prefix-keys (derived from exwm-input-global-keys), or
  • it's in exwm-input-prefix-keys, or
  • (lookup-key (current-local-map) (vector event)) is non-nil — but current-local-map only sees the buffer's own local map (effectively exwm-mode-map), NOT minor-mode keymaps or global-map.

Consequence: any key bound ONLY via a global minor mode (e.g. this repo's azos/global-minor-mode/keymap, which is how M-\ -> split-window-horizontally is bound in azos-core/features/base/emacs/config.org) will silently fail inside EXWM windows — the keypress gets bounced back to the underlying app instead of running the Emacs command — even though (key-binding (kbd "M-\\")) correctly resolves it when checked directly (that function walks the full keymap stack, unlike the local-map-only check EXWM uses).

Fix: add the key to exwm-input-prefix-keys in azos-core/features/exwm/emacs/config.org (~230). This is unrelated to EXWM package version — confirmed the relevant dispatch functions (exwm-input--event-passthrough-p, -on-KeyPress-line-mode, -grab-keyboard=/-release-keyboard=, exwm-reset) are byte-identical across a nix flake update that bumped emacs-exwm 0.34.0.20250919 -> 0.34.0.20260204.

C-h k "not working" inside EXWM is often just mid-sequence

C-h k is correctly wired (C-h is in exwm-input-prefix-keys). If it looks inert, it's because describe-key is waiting for a third keypress (the key to describe) — the minibuffer prompt is there, just easy to miss if focus/echo isn't visible.

Debugging EXWM keybinding issues live

exwm-debug-mode does NOT enable exwm's internal logging (exwm--log checks the special var exwm-debug at macro-EXPANSION time, so it's compiled out entirely if the package was byte-compiled with it off — toggling the mode at runtime is a no-op for logging). Instead, use emacsclient to:

  • check real state: (with-current-buffer BUF (list exwm--input-mode exwm--id))
  • check real X input focus: (slot-value (xcb:+request-unchecked+reply exwm--connection (make-instance 'xcb:GetInputFocus)) 'focus)
  • add temporary advice-add ... :before on exwm-input--on-KeyPress / -on-KeyPress-line-mode to log which keys actually reach the dispatcher
  • simulate real physical keypresses (not just synthetic events aimed at a window) with nix shell nixpkgs#xdotool --command xdotool key --clearmodifiers <keys> — this uses XTEST and goes through the same X input pipeline as real hardware, unlike xdotool key --window ID which uses SendEvent and can behave differently.