127 lines
6.6 KiB
Org Mode
127 lines
6.6 KiB
Org Mode
:PROPERTIES:
|
|
:ID: c52036a9-25ec-4b8b-8b72-90fcd9ea65dd
|
|
:END:
|
|
|
|
#+title: azos/exwm
|
|
#+filetags: :project: :knowledge: :exwm:
|
|
|
|
#+title: azos/exwm
|
|
#+filetags: :project: :knowledge: :exwm:
|
|
|
|
Backlink: [[id:bf4dde56-0967-4374-94ed-771301e47c66][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=)
|
|
|
|
** 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 [[id:f4196b35-5438-4da3-9d34-511291f482b5][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 [[id:939a9fa3-ddc8-4cc1-8130-771cc5de000f][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 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.
|