Fix hang on click and reconnect after daemon restart

Two root causes shared one symptom: the waybar module froze after a
click and needed a waybar restart to recover.

1. oc-waybar-ctl used socat's UNIX-SENDTO, a bidirectional relay that
   can block on a datagram socket. waybar runs the click command on
   its per-module event thread and stops updating the module until the
   command returns, so a hung socat froze the module. Replace the
   socat/nc chain with a bounded python3 sendto (2s timeout). python3
   is guaranteed present since the daemon needs it.

2. The daemon ran oc raw json with a blocking subprocess.run on the
   main loop, so a slow network fetch (up to 60s) stalled the 5s
   rotation and queued clicks. Move fetch_ocd to a worker thread; the
   main loop adopts the result when it lands and never blocks. Rotation
   and click draining stay live during a fetch.

Reconnect after a daemon restart now works because the click helper no
longer wedges waybar's event thread; the module's interval-1 cat
re-reads state.json as soon as the new daemon rewrites it (verified:
state.json returns within 1s).
This commit is contained in:
ocpway committed 2026-07-30 04:43:45 +03:00
1 parent 189a0e03ae
commit c4647dda6b
2 files changed
+40 -18

No files matched your search

+18 -15
View File
@@ -1,30 +1,33 @@
#!/bin/sh
# ocd-waybar-ctl: send a command to the ocd-waybar daemon.
# Usage: ocd-waybar-ctl {advance|lock|poll|quit}
# wired into the waybar custom/ocd module as on-click / on-click-right.
# Wired into the waybar custom/ocd module as on-click / on-click-right.
#
# This must never hang: waybar runs it on a click and stops updating the
# module until it returns. socat's UNIX-SENDTO relay can block waiting on
# the datagram socket, so we use a small python sendto with a hard timeout
# instead. python3 is guaranteed present because the daemon itself needs it.
set -eu
cmd="${1:-advance}"
sock="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ocd-waybar.sock"
if [ "$#" -gt 0 ]; then
cmd="$1"
fi
case "$cmd" in
advance|lock|poll|quit) ;;
*) echo "ocd-waybar-ctl: unknown command '$cmd'" >&2; exit 2 ;;
esac
if [ ! -S "$sock" ]; then
echo "ocd-waybar-ctl: daemon socket not found at $sock" >&2
exit 1
fi
sock="${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/ocd-waybar.sock"
[ -S "$sock" ] || { echo "ocd-waybar-ctl: daemon socket not found at $sock" >&2; exit 1; }
printf '%s\n' "$cmd" | socat - UNIX-SENDTO:"$sock" 2>/dev/null \
|| printf '%s\n' "$cmd" | nc -U -u -w1 "$sock" 2>/dev/null \
|| python3 - "$sock" "$cmd" <<'PY'
python3 - "$sock" "$cmd" <<'PY'
import socket, sys
sock, cmd = sys.argv[1], sys.argv[2]
s = socket.socket(socket.AF_UNIX, socket.SOCK_DGRAM)
s.sendto((sys.argv[2] + "\n").encode(), sys.argv[1])
s.settimeout(2.0)
try:
s.sendto((cmd + "\n").encode(), sock)
except (OSError, socket.timeout) as e:
print("ocd-waybar-ctl: send failed: %s" % e, file=sys.stderr)
sys.exit(1)
finally:
s.close()
PY