Merge pull request #6398 from basecamp/supervise-fcitx5
Supervise fcitx5 so XCompose sequences can't silently die
This commit is contained in:
@@ -2,4 +2,9 @@
|
|||||||
|
|
||||||
# omarchy:summary=Restart the XCompose input method service (fcitx5) to apply new compose key settings.
|
# omarchy:summary=Restart the XCompose input method service (fcitx5) to apply new compose key settings.
|
||||||
|
|
||||||
omarchy-restart-app fcitx5 --disable notificationitem
|
# A fcitx5 running outside the unit owns org.fcitx.Fcitx5, so the unit's
|
||||||
|
# instance would exit on arrival and the stale process would keep serving the
|
||||||
|
# old ~/.XCompose -- a restart that reports success and changes nothing.
|
||||||
|
systemctl --user stop omarchy-fcitx5.service
|
||||||
|
pkill -x fcitx5 >/dev/null 2>&1 || true
|
||||||
|
systemctl --user start omarchy-fcitx5.service
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ hl.on("hyprland.start", function()
|
|||||||
hl.exec_cmd("dbus-update-activation-environment --systemd --all")
|
hl.exec_cmd("dbus-update-activation-environment --systemd --all")
|
||||||
|
|
||||||
hl.exec_cmd("quickshell -n -p $OMARCHY_PATH/shell")
|
hl.exec_cmd("quickshell -n -p $OMARCHY_PATH/shell")
|
||||||
hl.exec_cmd(o.launch("fcitx5 --disable notificationitem"))
|
|
||||||
hl.exec_cmd("omarchy-first-run")
|
hl.exec_cmd("omarchy-first-run")
|
||||||
hl.exec_cmd("omarchy-powerprofiles-init")
|
hl.exec_cmd("omarchy-powerprofiles-init")
|
||||||
hl.exec_cmd(o.launch("omarchy-hyprland-monitor-watch"))
|
hl.exec_cmd(o.launch("omarchy-hyprland-monitor-watch"))
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=Fcitx5 input method (XCompose sequences)
|
||||||
|
# fcitx5 turns the CapsLock compose sequences in ~/.XCompose into text for
|
||||||
|
# Wayland clients.
|
||||||
|
#
|
||||||
|
# Wait for the compositor: fcitx5 needs WAYLAND_DISPLAY and DISPLAY, which uwsm
|
||||||
|
# imports into the user manager before it reaches graphical-session.target.
|
||||||
|
After=graphical-session.target
|
||||||
|
# The wayland connection dies with the compositor, so follow the session rather
|
||||||
|
# than linger against a socket that is gone.
|
||||||
|
PartOf=graphical-session.target
|
||||||
|
# After= is ordering only -- it does not stop anything from starting this unit
|
||||||
|
# while the target is inactive. An `omarchy update` over SSH has a live user
|
||||||
|
# manager (pam_systemd) and no graphical session, and a fcitx5 started there
|
||||||
|
# comes up with no WAYLAND_DISPLAY and no way to reach any client. Worse, it
|
||||||
|
# stays active, so the later graphical-session.target activation won't pull in a
|
||||||
|
# working one -- Wants= does not restart what is already running. Skip the start
|
||||||
|
# instead; the unit stays enabled and starts for real at graphical login.
|
||||||
|
ConditionEnvironment=WAYLAND_DISPLAY
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
# notificationitem duplicates the tray entry omarchy already renders itself.
|
||||||
|
ExecStart=/usr/bin/fcitx5 --disable notificationitem
|
||||||
|
# always, not on-failure: fcitx5 exits 0 when it detects another instance owning
|
||||||
|
# its bus name, and a clean exit still leaves the user with no input method.
|
||||||
|
Restart=always
|
||||||
|
RestartSec=2
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=graphical-session.target
|
||||||
+2
-1
@@ -228,7 +228,8 @@ systemd instance:
|
|||||||
Voxtype post-update hook.
|
Voxtype post-update hook.
|
||||||
- `install/user/first-run/enable-user-units.sh` — `systemctl --user enable`
|
- `install/user/first-run/enable-user-units.sh` — `systemctl --user enable`
|
||||||
the shipped user units (`bt-agent`, `omarchy-sleep-lock`,
|
the shipped user units (`bt-agent`, `omarchy-sleep-lock`,
|
||||||
`omarchy-recover-internal-monitor`, `omarchy-migrate-notify.service`).
|
`omarchy-recover-internal-monitor`, `omarchy-migrate-notify.service`,
|
||||||
|
`omarchy-fcitx5.service`).
|
||||||
Done here, not at finalize, because
|
Done here, not at finalize, because
|
||||||
the user manager isn't reachable from the ISO chroot; `ConditionPath*`
|
the user manager isn't reachable from the ISO chroot; `ConditionPath*`
|
||||||
in the unit files keeps services inert when they don't apply.
|
in the unit files keeps services inert when they don't apply.
|
||||||
|
|||||||
@@ -16,4 +16,5 @@ systemctl --user enable --now \
|
|||||||
bt-agent.service \
|
bt-agent.service \
|
||||||
omarchy-recover-internal-monitor.service \
|
omarchy-recover-internal-monitor.service \
|
||||||
omarchy-sleep-lock.service \
|
omarchy-sleep-lock.service \
|
||||||
omarchy-migrate-notify.service
|
omarchy-migrate-notify.service \
|
||||||
|
omarchy-fcitx5.service
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
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
|
||||||
@@ -139,6 +139,7 @@ package_defaults = [
|
|||||||
("default/systemd/user/omarchy-recover-internal-monitor.service", "/usr/lib/systemd/user/omarchy-recover-internal-monitor.service", "systemd/user/omarchy-recover-internal-monitor.service"),
|
("default/systemd/user/omarchy-recover-internal-monitor.service", "/usr/lib/systemd/user/omarchy-recover-internal-monitor.service", "systemd/user/omarchy-recover-internal-monitor.service"),
|
||||||
("default/systemd/user/omarchy-migrate-notify.service", "/usr/lib/systemd/user/omarchy-migrate-notify.service", "systemd/user/omarchy-migrate-notify.service"),
|
("default/systemd/user/omarchy-migrate-notify.service", "/usr/lib/systemd/user/omarchy-migrate-notify.service", "systemd/user/omarchy-migrate-notify.service"),
|
||||||
("default/systemd/user/omarchy-tailscale-receive.service", "/usr/lib/systemd/user/omarchy-tailscale-receive.service", "systemd/user/omarchy-tailscale-receive.service"),
|
("default/systemd/user/omarchy-tailscale-receive.service", "/usr/lib/systemd/user/omarchy-tailscale-receive.service", "systemd/user/omarchy-tailscale-receive.service"),
|
||||||
|
("default/systemd/user/omarchy-fcitx5.service", "/usr/lib/systemd/user/omarchy-fcitx5.service", "systemd/user/omarchy-fcitx5.service"),
|
||||||
("default/systemd/zram-generator.conf.d/90-omarchy.conf", "/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf", "systemd/zram-generator.conf.d/90-omarchy.conf"),
|
("default/systemd/zram-generator.conf.d/90-omarchy.conf", "/usr/lib/systemd/zram-generator.conf.d/90-omarchy.conf", "systemd/zram-generator.conf.d/90-omarchy.conf"),
|
||||||
("default/fonts/omarchy/omarchy.ttf", "/usr/share/fonts/omarchy/omarchy.ttf", "omarchy.ttf"),
|
("default/fonts/omarchy/omarchy.ttf", "/usr/share/fonts/omarchy/omarchy.ttf", "omarchy.ttf"),
|
||||||
("default/snapper/root", "/etc/snapper/config-templates/omarchy", "snapper/root"),
|
("default/snapper/root", "/etc/snapper/config-templates/omarchy", "snapper/root"),
|
||||||
|
|||||||
@@ -46,3 +46,33 @@ grep -F 'omarchy-migrate-notify.service' "$first_run_units" >/dev/null ||
|
|||||||
grep -F 'omarchy-update-user-notify' "$first_run_units" >/dev/null &&
|
grep -F 'omarchy-update-user-notify' "$first_run_units" >/dev/null &&
|
||||||
fail "first-run still enables the retired notifier units"
|
fail "first-run still enables the retired notifier units"
|
||||||
pass "first-run enables the login-only migration notifier"
|
pass "first-run enables the login-only migration notifier"
|
||||||
|
|
||||||
|
fcitx_service="$ROOT/default/systemd/user/omarchy-fcitx5.service"
|
||||||
|
grep -Fx 'ExecStart=/usr/bin/fcitx5 --disable notificationitem' "$fcitx_service" >/dev/null
|
||||||
|
grep -Fx 'Restart=always' "$fcitx_service" >/dev/null ||
|
||||||
|
fail "fcitx5 exits 0 on a duplicate bus name, so on-failure would leave the user with no input method"
|
||||||
|
grep -Fx 'After=graphical-session.target' "$fcitx_service" >/dev/null ||
|
||||||
|
fail "fcitx5 needs WAYLAND_DISPLAY, which uwsm imports before it reaches graphical-session.target"
|
||||||
|
grep -Fx 'PartOf=graphical-session.target' "$fcitx_service" >/dev/null ||
|
||||||
|
fail "fcitx5 must stop with the compositor instead of lingering against a dead wayland socket"
|
||||||
|
grep -Fx 'WantedBy=graphical-session.target' "$fcitx_service" >/dev/null ||
|
||||||
|
fail "fcitx5 is never pulled in at login without a WantedBy"
|
||||||
|
grep -Fx 'ConditionEnvironment=WAYLAND_DISPLAY' "$fcitx_service" >/dev/null ||
|
||||||
|
fail "an update over SSH has a live user manager and no display; starting fcitx5 there wedges the unit active-but-blind, and Wants= will not replace it at graphical login"
|
||||||
|
|
||||||
|
fcitx_migration="$ROOT/migrations/1785167800.sh"
|
||||||
|
grep -F 'is-active --quiet graphical-session.target' "$fcitx_migration" >/dev/null ||
|
||||||
|
fail "migration kills fcitx5 and starts the unit outside a graphical session"
|
||||||
|
grep -F 'systemctl --user enable omarchy-fcitx5.service' "$fcitx_migration" >/dev/null ||
|
||||||
|
fail "migration must enable without --now; --now starts the unit before the session-gate check"
|
||||||
|
grep -F 'Could not start omarchy-fcitx5.service' "$fcitx_migration" >/dev/null ||
|
||||||
|
fail "migration pkills a working fcitx5, so a failed handover must be reported instead of marked complete"
|
||||||
|
|
||||||
|
grep -F 'pkill -x fcitx5' "$ROOT/bin/omarchy-restart-xcompose" >/dev/null ||
|
||||||
|
fail "restart-xcompose cannot reload a fcitx5 running outside the unit, so it silently keeps serving the old table"
|
||||||
|
|
||||||
|
grep -F 'omarchy-fcitx5.service' "$first_run_units" >/dev/null ||
|
||||||
|
fail "first-run does not enable the input method, so ~/.XCompose sequences never resolve"
|
||||||
|
grep -F 'fcitx5' "$ROOT/default/hypr/autostart.lua" >/dev/null &&
|
||||||
|
fail "fcitx5 is autostarted from Hyprland; an unsupervised launch dies silently and takes every compose sequence with it"
|
||||||
|
pass "fcitx5 runs supervised, so a lost input method comes back instead of killing XCompose until logout"
|
||||||
|
|||||||
Reference in New Issue
Block a user