6.6 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-> callsexwm-input-grab-keyboard(switches window to full "line-mode": Emacs captures all keys)C-c C-k(EXWM's own default binding inexwm-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, viaexwm-input--global-prefix-keys)
Desktop notifications: EDNC replaces dunst
Under EXWM, services.dunst.enable = lib.mkForce false; and EDNC (Emacs
Desktop Notification Center, azos-core/features/exwm/emacs/config.org) runs
instead, owning org.freedesktop.Notifications on the session bus directly
from the Emacs process (ednc-mode). Arbitrary non-Emacs senders (scripts,
notify-send, GUI apps like zapzap) reach it the normal way — a plain D-Bus
Notify call — no dunst-specific glue needed. azos/ednc-popup--present adds
a posframe toast on top of EDNC's built-in *ednc-log* buffer logging.
Confirmed working 2026-10-11: busctl --user list showed the Emacs process
owning the bus name; a synthetic Notify call shaped like zapzap's real one
(icon file path, category=im.received hint) landed correctly in
*ednc-log*. See azos/nextcloud
for the investigation that prompted this (zapzap WhatsApp notifications
reported missing — pipeline turned out fine; likely just WhatsApp Web's
normal "don't notify for the focused chat" behavior).
Station renamed to desktop; vestigial Emacs dependency removed (2026-10-13)
The home-manager station suite was renamed to desktop
(azos-core/suites/desktop/, module config.flake.modules.homeManager.desktop,
option azos.suites.desktop.enable) rather than split further — its apps
(browser, gimp, libreoffice, spotify, whatsapp, zoom) turned out to be
wanted on any GUI machine, work included. exwm still depends on it by
default (azos.suites.desktop.enable = lib.mkDefault true) since exwm's
quick-launch keybindings for chromium/qutebrowser/gimp need those
binaries. Only discord was dropped from azos-core entirely, now living
as standalone azos/features/discord — personal-only apps live in azos,
not azos-core, going forward.
exwm's _pkg.nix and config.org no longer depend on
azos-emacs-station (renamed azos-emacs-desktop) at all – confirmed
via grep that none of exwm's own config used its symbols
(mu4e/elfeed/wttrin/kubernetes-/pass); the require was vestigial. Full
task history in azos/todos.
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 fromexwm-input-global-keys), or - it's in
exwm-input-prefix-keys, or (lookup-key (current-local-map) (vector event))is non-nil — butcurrent-local-maponly sees the buffer's own local map (effectivelyexwm-mode-map), NOT minor-mode keymaps orglobal-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 ... :beforeonexwm-input--on-KeyPress/-on-KeyPress-line-modeto 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, unlikexdotool key --window IDwhich uses SendEvent and can behave differently.