Files
omarchycn/migrations/1785167800.sh
T
David Heinemeier HanssonandClaude Opus 5 6e07fd0e82 Supervise fcitx5 so XCompose sequences can't silently die
fcitx5 is what turns the CapsLock compose sequences in ~/.XCompose into
text for Wayland clients -- CapsLock m s for an emoji, CapsLock space n
for your name. It was launched fire-and-forget from Hyprland's autostart
via uwsm-app, so nothing supervised it, nothing restarted it, and it
logged nowhere. When it went away, every compose sequence stopped
working for the rest of the session with no visible symptom beyond
"emoji input is busted", and no record of why.

That is not hypothetical: it was found dead on a running machine with no
coredump, no OOM kill, and nothing in the journal to explain it.

Move it to a systemd user service:

- Restart=always, not on-failure. fcitx5 exits 0 when it finds another
  instance already owning org.fcitx.Fcitx5, and a clean exit still
  leaves the user with no input method.
- After=/PartOf=graphical-session.target. It needs WAYLAND_DISPLAY and
  DISPLAY, which uwsm imports into the user manager before reaching the
  target, and its wayland connection dies with the compositor.
- ConditionEnvironment=WAYLAND_DISPLAY. After= is ordering only and does
  not stop the unit from being started while the target is inactive. An
  update over SSH has a live user manager (pam_systemd) and no graphical
  session, and a fcitx5 started there comes up blind -- then stays
  active, so the later target activation won't pull in a working one,
  because Wants= does not restart what is already running. Skipping the
  start leaves the unit enabled and healthy for the next graphical login.

The migration hands over inside a live session only: it enables without
--now, and only when graphical-session.target is active does it drop the
autostart-launched process and start the unit. Because that kills a
fcitx5 that was working a moment ago, a failed start is reported instead
of leaving the session mute with the migration marked complete.

omarchy-restart-xcompose now drives the unit. It still clears any fcitx5
running outside it first: that process owns the bus name, so the unit's
instance would exit on arrival and the stale one would keep serving the
old table -- a restart that reports success and changes nothing.

Side benefit: fcitx5 now logs to the journal under its own unit, so the
next disappearance leaves a record.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-27 09:49:15 -07:00

39 lines
2.0 KiB
Bash

echo "Supervise fcitx5 so CapsLock compose sequences can't silently die"
# fcitx5 was launched fire-and-forget from Hyprland's autostart. Nothing
# restarted it and nothing noticed when it went away, so a single lost start
# left every ~/.XCompose sequence (CapsLock m s -> emoji) dead for the rest of
# the session. It's a systemd user service with Restart=always now.
systemctl --user daemon-reload >/dev/null 2>&1 || true
# Enable without --now, and start by hand further down only when there is a
# session to start into. `systemctl enable` needs a live user manager, which an
# `omarchy update` from a TTY does not have, so fall back to writing exactly the
# symlink it would have written rather than silently leaving this unenabled.
if ! systemctl --user enable omarchy-fcitx5.service >/dev/null 2>&1; then
wants_dir="$HOME/.config/systemd/user/graphical-session.target.wants"
mkdir -p "$wants_dir"
ln -sfn /usr/lib/systemd/user/omarchy-fcitx5.service \
"$wants_dir/omarchy-fcitx5.service"
fi
# Outside a graphical session -- an update over SSH -- there is nothing to hand
# over: no autostart-launched fcitx5 to kill, and the unit's own
# ConditionEnvironment would skip the start anyway. The enablement above is the
# whole job; the next graphical login starts it.
if systemctl --user is-active --quiet graphical-session.target; then
# The service and the autostart-launched process can't coexist: a second
# fcitx5 sees the first owning org.fcitx.Fcitx5 and exits 0, which
# Restart=always would turn into a restart loop. Drop the stray first.
pkill -x fcitx5 >/dev/null 2>&1 || true
# Report what systemctl actually said. This kills a fcitx5 that was working a
# moment ago, so a start failure here has to be loud instead of leaving the
# session with no input method and a migration marked complete.
if ! error=$(systemctl --user start omarchy-fcitx5.service 2>&1); then
echo "Could not start omarchy-fcitx5.service: $error"
echo "Compose sequences (CapsLock m s) will not work until the next login."
fi
fi