- Add optional modifier-only Super binding whose tap is cancelled by other input, focus changes, grabs, locks, and session teardown. - Ship the native ferese-rime user service, Rime defaults, installer inventory, package dependencies, session environment, and docs. - Vendor Smithay 8a27023 with the SHM texture-cache format fix and add GPU regressions for format changes and resize snapshots. - Release reused CRTC owners before staging output modes, then restore changed outputs before former owners when a transaction fails. - Wait for resize-capture GPU completion, expand filtered surface damage, and preserve IME popup origin under fractional scaling. - Mark weekends like holidays in both calendar layouts.
176 lines
7.9 KiB
Bash
Executable File
176 lines
7.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
session_target_started=0
|
|
shell_pid=
|
|
agent_pid=
|
|
autostart_pid=
|
|
rime_unit=
|
|
manage_session=0
|
|
if [[ ${FERESE_SESSION_MODE:-desktop} == desktop && ${FERESE_SESSION_IMPORT_ENV:-0} == 1 ]]; then
|
|
manage_session=1
|
|
fi
|
|
cleanup() {
|
|
trap - EXIT INT TERM
|
|
if [[ -n $autostart_pid ]]; then
|
|
kill "$autostart_pid" 2>/dev/null || true
|
|
wait "$autostart_pid" 2>/dev/null || true
|
|
fi
|
|
if [[ -n $shell_pid ]]; then kill "$shell_pid" 2>/dev/null || true; fi
|
|
if [[ -n $agent_pid ]]; then
|
|
kill "$agent_pid" 2>/dev/null || true
|
|
wait "$agent_pid" 2>/dev/null || true
|
|
fi
|
|
if [[ -n $rime_unit ]]; then
|
|
timeout --kill-after=1s 6s systemctl --user stop "$rime_unit" || true
|
|
fi
|
|
if ((session_target_started)); then
|
|
systemctl --user stop ferese-session.target || true
|
|
fi
|
|
}
|
|
trap cleanup EXIT
|
|
trap 'exit 130' INT
|
|
trap 'exit 143' TERM
|
|
|
|
# The compositor supplies its new Wayland socket before this helper starts.
|
|
# Nested previews must never replace the host session's activation environment.
|
|
if ((manage_session)); then
|
|
# Shell control uses a private socket FD. Activation must use the public
|
|
# socket so portal and other desktop services connect as ordinary clients.
|
|
public_display=${FERESE_PUBLIC_WAYLAND_DISPLAY:-${WAYLAND_DISPLAY:-}}
|
|
# Empty assignments replace stale values; D-Bus cannot remove activation variables.
|
|
activation_vars=(
|
|
"WAYLAND_DISPLAY=$public_display"
|
|
"DISPLAY=${DISPLAY:-}"
|
|
"XAUTHORITY=${XAUTHORITY:-}"
|
|
"FERESE_SOCKET=${FERESE_SOCKET:-}"
|
|
XDG_CURRENT_DESKTOP
|
|
XDG_SESSION_DESKTOP
|
|
XDG_SESSION_TYPE
|
|
)
|
|
if [[ ${FERESE_ENABLE_INPUT_METHOD:-0} == 1 ]]; then
|
|
activation_vars+=("GTK_IM_MODULE=" QT_IM_MODULE QT_IM_MODULES IBUS_ADDRESS FERESE_RIME_CONTROL
|
|
"SDL_IM_MODULE=" "XMODIFIERS=${XMODIFIERS:-}")
|
|
fi
|
|
if command -v dbus-update-activation-environment >/dev/null; then
|
|
if ! env WAYLAND_DISPLAY="$public_display" \
|
|
dbus-update-activation-environment --systemd "${activation_vars[@]}"; then
|
|
env WAYLAND_DISPLAY="$public_display" \
|
|
dbus-update-activation-environment "${activation_vars[@]}" || true
|
|
fi
|
|
fi
|
|
# User buses can outlive logout and may predate a newly installed service directory.
|
|
if command -v dbus-send >/dev/null; then
|
|
dbus-send --session --type=method_call --print-reply \
|
|
--dest=org.freedesktop.DBus /org/freedesktop/DBus \
|
|
org.freedesktop.DBus.ReloadConfig >/dev/null || true
|
|
fi
|
|
if command -v systemctl >/dev/null; then
|
|
systemctl --user daemon-reload || true
|
|
if systemctl --user start ferese-session.target; then
|
|
session_target_started=1
|
|
# A user manager can retain the target across session transitions.
|
|
# Refresh an already-running portal after importing this session's
|
|
# environment so it uses the installed release and current display.
|
|
systemctl --user try-restart xdg-desktop-portal-ferese.service || true
|
|
else
|
|
echo 'Ferese: graphical session target could not start; portal activation may be unavailable.' >&2
|
|
fi
|
|
fi
|
|
fi
|
|
release_dir=$(dirname -- "$(readlink -f -- "$0")")
|
|
# Start the native input method on the public socket, without shell privileges.
|
|
# Pin the executable to this release and let systemd recover both crashes and a
|
|
# stalled input loop. A failed IME must leave the compositor and shell usable.
|
|
start_rime() {
|
|
local user_data=${FERESE_RIME_USER_DATA:-${XDG_DATA_HOME:-$HOME/.local/share}/ferese/rime}
|
|
local shared_data=${FERESE_RIME_SHARED_DATA:-/usr/share/rime-data}
|
|
local identity=${FERESE_SOCKET:-${XDG_RUNTIME_DIR:-}/$public_display}
|
|
local name variable load_state deadline
|
|
local -a environment=()
|
|
name=$(systemd-escape --template=[email protected] "$identity") || return 1
|
|
[[ -x $release_dir/ferese-rime ]] || return 1
|
|
[[ -r $shared_data/luna_pinyin_simp.schema.yaml ]] || {
|
|
echo 'Ferese: install the Rime Luna Pinyin schemas before starting native input.' >&2
|
|
return 1
|
|
}
|
|
mkdir -p -- "$user_data" || return 1
|
|
# noclobber preserves existing customization, including a concurrent setup.
|
|
if [[ ! -e $user_data/default.custom.yaml && ! -L $user_data/default.custom.yaml ]]; then
|
|
(set -o noclobber; cat -- "$release_dir/rime/default.custom.yaml" > "$user_data/default.custom.yaml") || return 1
|
|
fi
|
|
# SIGKILL cannot run the previous helper's trap. The compositor control socket
|
|
# survives helper recovery, so replace only its old IME instance. Bound all
|
|
# manager calls and wait for the transient unit to be collected before reuse.
|
|
load_state=$(timeout --kill-after=1s 2s systemctl --user show "$name" --property=LoadState --value) || return 1
|
|
if [[ $load_state != not-found ]]; then
|
|
timeout --kill-after=1s 6s systemctl --user stop "$name" || return 1
|
|
deadline=$((SECONDS + 3))
|
|
while [[ $load_state != not-found ]]; do
|
|
((SECONDS < deadline)) || return 1
|
|
load_state=$(timeout --kill-after=1s 1s systemctl --user show "$name" --property=LoadState --value) || return 1
|
|
if [[ $load_state != not-found ]]; then sleep .05; fi
|
|
done
|
|
fi
|
|
for variable in HOME PATH LANG XDG_RUNTIME_DIR XDG_CONFIG_HOME XDG_CACHE_HOME XDG_DATA_HOME XDG_STATE_HOME \
|
|
DBUS_SESSION_BUS_ADDRESS XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE \
|
|
FERESE_SOCKET FERESE_RIME_CONTROL; do
|
|
if [[ -v $variable ]]; then environment+=("--setenv=$variable=${!variable}"); fi
|
|
done
|
|
if timeout --kill-after=1s 6s systemd-run --user --collect --quiet --no-block --unit="$name" --service-type=notify \
|
|
--property=Restart=on-failure --property=RestartSec=1 \
|
|
--property=StartLimitIntervalSec=60 --property=StartLimitBurst=5 \
|
|
--property=WatchdogSec=10 --property=WatchdogSignal=SIGKILL \
|
|
--property=TimeoutStartSec=45 --property=TimeoutStopSec=3 \
|
|
--property=BindsTo=ferese-session.target --property=PartOf=ferese-session.target \
|
|
"${environment[@]}" \
|
|
/usr/bin/env -u WAYLAND_SOCKET -u FERESE_SHELL_CONTROL_SOCKET -u FERESE_PRIVATE_X11_COOKIE \
|
|
WAYLAND_DISPLAY="$public_display" "$release_dir/ferese-rime" \
|
|
--user-data "$user_data" --shared-data "$shared_data"; then
|
|
rime_unit=$name
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
# Agent failures must not take down a healthy shell. The compositor supervises
|
|
# this helper (and creates fresh private connections) when the shell itself exits.
|
|
supervise_agent() {
|
|
local child= delay=1 started
|
|
trap 'trap - TERM INT; if [[ -n $child ]]; then kill "$child" 2>/dev/null || true; wait "$child" 2>/dev/null || true; fi; exit 0' TERM INT
|
|
while true; do
|
|
started=$SECONDS
|
|
env -u WAYLAND_SOCKET -u FERESE_SHELL_CONTROL_SOCKET \
|
|
WAYLAND_DISPLAY="${FERESE_PUBLIC_WAYLAND_DISPLAY:-${WAYLAND_DISPLAY:-}}" \
|
|
"$release_dir/ferese-polkit-agent" &
|
|
child=$!
|
|
wait "$child" || true
|
|
child=
|
|
if ((SECONDS - started >= 60)); then delay=1; fi
|
|
sleep "$delay" &
|
|
child=$!
|
|
wait "$child" || true
|
|
child=
|
|
if ((delay < 30)); then delay=$((delay * 2)); fi
|
|
if ((delay > 30)); then delay=30; fi
|
|
done
|
|
}
|
|
supervise_agent &
|
|
agent_pid=$!
|
|
"$release_dir/ferese-shell" &
|
|
shell_pid=$!
|
|
if ((manage_session && session_target_started)) && [[ ${FERESE_ENABLE_INPUT_METHOD:-0} == 1 ]]; then
|
|
if ! start_rime; then
|
|
echo 'Ferese: native Rime could not start; the desktop remains available.' >&2
|
|
fi
|
|
fi
|
|
if ((manage_session)) && [[ ${FERESE_CLIENT_RESTART:-0} != 1 && -x $release_dir/feresectl ]]; then
|
|
env WAYLAND_DISPLAY="${FERESE_PUBLIC_WAYLAND_DISPLAY:-${WAYLAND_DISPLAY:-}}" \
|
|
"$release_dir/feresectl" autostart &
|
|
autostart_pid=$!
|
|
fi
|
|
if wait "$shell_pid"; then
|
|
status=0
|
|
else
|
|
status=$?
|
|
fi
|
|
exit "$status"
|