1 Commits
Author SHA1 Message Date
Lukasz Kasprzak 9610f461c2 keychord: bind a sequence of keys, and dispatch events while waiting
Original keychord patch by Hai Nguyen <hhai2105@gmail.com>, updated for
dwm 6.4+ grabkeys() by Aaron Zueger <contact@azureorange.xyz>. This
revision rebases onto dwm 6.8 and fixes an event-loss bug in the chord
wait loop.

The wait loop used to discard every event that was not a KeyPress:

    while (running && !XNextEvent(dpy, &event) && !ran)
            if(event.type == KeyPress)
                    break;

Two consequences, both reproduced on Xephyr:

1. Unrecoverable input freeze. grabbuttons() grabs unfocused clients
   GrabModeSync/GrabModeSync, so clicking one freezes pointer AND
   keyboard until the grabbing client calls XAllowEvents. dwm does that
   in buttonpress() -- but if the ButtonPress is discarded here,
   buttonpress() never runs. The grab stays frozen forever and the loop
   can never exit, because a frozen keyboard cannot deliver the KeyPress
   it is waiting for. Only killing dwm ends it; XSendEvent is discarded
   by this same loop and XTEST queues behind the frozen device.
   Repro: press a chord prefix, then click any unfocused client.
   Observed: XGrabKeyboard -> GrabFrozen, XQueryPointer -> Button1 held.

2. Orphaned windows. A MapRequest arriving mid-chord is dropped, so a
   window opening during a chord is never managed. DestroyNotify,
   Expose and ConfigureRequest are lost the same way.

Dispatch through handler[] as run() does, breaking on KeyPress before
dispatch so keypress() cannot recurse. The `&& !ran` term is dropped:
ran is always 0 on entry and nothing in the body can set it.
2026-10-19 09:10:49 +02:00