Activate the graphical session and refresh portal services at login
This commit is contained in:
1 parent
41d63f6b18
commit
38067726c2
6 files changed
+127
-7
No files matched your search
@@ -14,7 +14,11 @@ desktop instead of direct access to the graphics device.
|
||||
Build with **Rust 1.95 or newer**, a C/C++ toolchain, CMake, pkg-config, and the
|
||||
native development libraries below. The installer builds Ferese, its shell,
|
||||
Settings, command-line tools, locker, polkit agent, and ScreenCast portal together. It does not install system
|
||||
packages or enable services for you.
|
||||
packages or enable background services at boot. On systemd systems, the installed
|
||||
session helper activates `ferese-session.target` after exporting the compositor's
|
||||
Wayland display, and stops it when the shell exits. This keeps
|
||||
`graphical-session.target` active so desktop portals can start. Nested previews
|
||||
do not alter the host's activation environment or session targets.
|
||||
|
||||
### Fedora
|
||||
|
||||
|
||||
@@ -1,12 +1,47 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
session_target_started=0
|
||||
shell_pid=
|
||||
agent_pid=
|
||||
cleanup() {
|
||||
trap - EXIT INT TERM
|
||||
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 ((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 [[ ${FERESE_SESSION_IMPORT_ENV:-0} == 1 ]]; then
|
||||
dbus-update-activation-environment --systemd \
|
||||
# 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:-}}
|
||||
env WAYLAND_DISPLAY="$public_display" dbus-update-activation-environment --systemd \
|
||||
WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE || \
|
||||
dbus-update-activation-environment \
|
||||
env WAYLAND_DISPLAY="$public_display" dbus-update-activation-environment \
|
||||
WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE || true
|
||||
# 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
|
||||
else
|
||||
echo 'Ferese: graphical session target could not start; portal activation may be unavailable.' >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
release_dir=$(dirname -- "$(readlink -f -- "$0")")
|
||||
env -u WAYLAND_SOCKET WAYLAND_DISPLAY="${FERESE_PUBLIC_WAYLAND_DISPLAY:-${WAYLAND_DISPLAY:-}}" \
|
||||
@@ -14,12 +49,9 @@ env -u WAYLAND_SOCKET WAYLAND_DISPLAY="${FERESE_PUBLIC_WAYLAND_DISPLAY:-${WAYLAN
|
||||
agent_pid=$!
|
||||
"$release_dir/ferese-shell" &
|
||||
shell_pid=$!
|
||||
trap 'kill "$shell_pid" "$agent_pid" 2>/dev/null || true' EXIT INT TERM
|
||||
if wait "$shell_pid"; then
|
||||
status=0
|
||||
else
|
||||
status=$?
|
||||
fi
|
||||
kill "$agent_pid" 2>/dev/null || true
|
||||
wait "$agent_pid" 2>/dev/null || true
|
||||
exit "$status"
|
||||
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
Description=Ferese graphical session
|
||||
BindsTo=graphical-session.target
|
||||
Before=graphical-session.target
|
||||
# Ferese Shell owns the notification service for this session.
|
||||
Conflicts=swaync.service
|
||||
@@ -73,6 +73,11 @@ if [[ -e $service ]] && ! cmp -s "$repo_dir/packaging/portal/$(basename "$servic
|
||||
echo "Unmanaged portal service exists: $service" >&2; exit 1
|
||||
fi
|
||||
|
||||
session_target=/usr/local/lib/systemd/user/ferese-session.target
|
||||
if [[ -e $session_target ]] && ! cmp -s "$repo_dir/packaging/systemd/ferese-session.target" "$session_target"; then
|
||||
echo "Unmanaged session target exists: $session_target" >&2; exit 1
|
||||
fi
|
||||
|
||||
desktop-file-validate "$repo_dir/packaging/ferese.desktop"
|
||||
desktop-file-validate "$repo_dir/packaging/dev.ferese.Settings.desktop"
|
||||
|
||||
@@ -85,6 +90,7 @@ for name in ferese ferese-shell ferese-settings feresectl ferese-lock ferese-pol
|
||||
done
|
||||
install -m 0755 -- "$repo_dir/packaging/ferese-session" "$release_dir/ferese-session"
|
||||
install -m 0755 -- "$repo_dir/packaging/ferese-session-shell" "$release_dir/ferese-session-shell"
|
||||
install -D -m 0644 -- "$repo_dir/packaging/systemd/ferese-session.target" "$session_target"
|
||||
install -m 0755 -- "$repo_dir/packaging/ferese-screenshot" "$release_dir/ferese-screenshot"
|
||||
install -m 0644 -- "$repo_dir/packaging/config.kdl" "$release_dir/config.example.kdl"
|
||||
install -D -m 0644 -- "$repo_dir/assets/fonts/Comfortaa-LICENSE.txt" "$release_dir/licenses/Comfortaa-LICENSE.txt"
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ fi
|
||||
|
||||
cd -- "$repo_dir"
|
||||
if ! $skip_build; then "${build[@]}"; fi
|
||||
for name in ferese ferese-shell ferese-settings feresectl ferese-lock ferese-polkit-agent; do
|
||||
for name in ferese ferese-shell ferese-settings feresectl ferese-lock ferese-polkit-agent xdg-desktop-portal-ferese ferese-record; do
|
||||
[[ -x $repo_dir/target/release/$name ]] || fail "missing release binary: $name"
|
||||
done
|
||||
"${installer[@]}"
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
"""Session startup regressions using fake services; never touches the host bus."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import signal
|
||||
import subprocess
|
||||
import tempfile
|
||||
import time
|
||||
import unittest
|
||||
|
||||
SCRIPT = Path(__file__).resolve().parents[2] / 'packaging/ferese-session-shell'
|
||||
|
||||
|
||||
class SessionStartupTest(unittest.TestCase):
|
||||
def run_session(self, direct=True, fail_target=False, shell_status=0, terminate=False):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
root = Path(directory)
|
||||
helper = root / 'ferese-session-shell'
|
||||
shutil.copyfile(SCRIPT, helper)
|
||||
programs = {
|
||||
'dbus-update-activation-environment': 'printf "import:%s\\n" "$WAYLAND_DISPLAY" >> "$TEST_ROOT/events"\n',
|
||||
'dbus-send': 'echo reload-bus >> "$TEST_ROOT/events"\n',
|
||||
'systemctl': 'echo "$*" >> "$TEST_ROOT/events"\nif [[ $* == "--user start ferese-session.target" && $TEST_FAIL == 1 ]]; then exit 1; fi\n',
|
||||
'ferese-polkit-agent': 'exec sleep 30\n',
|
||||
'ferese-shell': 'printf "shell:%s\\n" "$WAYLAND_SOCKET" >> "$TEST_ROOT/events"\nif [[ $TEST_WAIT == 1 ]]; then exec sleep 30; fi\nexit "$TEST_STATUS"\n',
|
||||
}
|
||||
for name, body in programs.items():
|
||||
path = root / name
|
||||
path.write_text('#!/bin/bash\n' + body)
|
||||
path.chmod(0o755)
|
||||
env = dict(os.environ, PATH=f'{root}:{os.environ["PATH"]}', TEST_ROOT=directory,
|
||||
FERESE_SESSION_IMPORT_ENV=str(int(direct)), WAYLAND_DISPLAY='private-display',
|
||||
FERESE_PUBLIC_WAYLAND_DISPLAY='public-display', WAYLAND_SOCKET='77',
|
||||
TEST_FAIL=str(int(fail_target)), TEST_STATUS=str(shell_status), TEST_WAIT=str(int(terminate)))
|
||||
child = subprocess.Popen(['bash', str(helper)], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
if terminate:
|
||||
deadline = time.monotonic() + 3
|
||||
while not (root / 'events').exists() or 'shell:77' not in (root / 'events').read_text():
|
||||
if time.monotonic() > deadline:
|
||||
child.kill()
|
||||
self.fail('Session shell did not start')
|
||||
time.sleep(.01)
|
||||
child.send_signal(signal.SIGTERM)
|
||||
child.communicate(timeout=4)
|
||||
return child.returncode, (root / 'events').read_text().splitlines()
|
||||
|
||||
def test_direct_session_imports_public_display_before_activation_and_cleans_up(self):
|
||||
status, events = self.run_session(shell_status=7)
|
||||
self.assertEqual(status, 7)
|
||||
self.assertEqual(events, ['import:public-display', 'reload-bus', '--user daemon-reload',
|
||||
'--user start ferese-session.target', 'shell:77',
|
||||
'--user stop ferese-session.target'])
|
||||
|
||||
def test_nested_session_never_changes_host_activation(self):
|
||||
status, events = self.run_session(direct=False)
|
||||
self.assertEqual(status, 0)
|
||||
self.assertEqual(events, ['shell:77'])
|
||||
|
||||
def test_failed_target_does_not_prevent_shell_or_stop_unowned_target(self):
|
||||
status, events = self.run_session(fail_target=True)
|
||||
self.assertEqual(status, 0)
|
||||
self.assertIn('shell:77', events)
|
||||
self.assertNotIn('--user stop ferese-session.target', events)
|
||||
|
||||
def test_termination_stops_the_session_target(self):
|
||||
status, events = self.run_session(terminate=True)
|
||||
self.assertEqual(status, 143)
|
||||
self.assertEqual(events[-1], '--user stop ferese-session.target')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user