mirror of
https://github.com/outbackdingo/hyprcosmic-session.git
synced 2026-08-25 14:53:23 +00:00
Pressing PrtScr said "COSMIC screenshot crashed". cosmic-screenshot was aborting on an unwrap, because org.freedesktop.portal.Screenshot had no implementation behind it. start-hyprcosmic exported XDG_CURRENT_DESKTOP=COSMIC, and the compositor had it. But an export only reaches processes started by this script, and the portal is not one of them: xdg-desktop-portal is launched by `systemd --user`, which is older than the session and carries its own environment block. Its copy had XDG_SESSION_TYPE and nothing else. xdg-desktop-portal chooses a backend by matching the `UseIn=` line of /usr/share/xdg-desktop-portal/portals/*.portal against XDG_CURRENT_DESKTOP. Unset, `UseIn=COSMIC` never matched, no backend loaded, and the Screenshot interface was never exported. Nothing anywhere logged a missing portal. The crash was the only symptom, and it pointed at the screenshot tool. `systemctl --user import-environment` fixes it, which is what start-cosmic has always done and this script had not. Ordering works out: the portal unit is `After=` and `Requisite=graphical-session.target`, which cosmic-session brings up later, so it cannot start ahead of the import. dbus-update-activation-environment as well, for any backend whose .service file has no `SystemdService=` line -- but only when a bus already exists, since in the dbus-run-session branch the bus is created after this point. Everything is `||:`. Losing a portal is bad; refusing to log in is worse. Verified by clearing all three variables out of the user manager, running the script's environment block, restarting xdg-desktop-portal, and watching the Screenshot interface come back and cosmic-screenshot exit 0 with a real PNG.
130 lines
6.2 KiB
Bash
Executable File
130 lines
6.2 KiB
Bash
Executable File
#!/usr/bin/bash
|
|
#
|
|
# Launch a HyprCosmic session: cosmic-comp for window management, HyDE's shell
|
|
# on top.
|
|
#
|
|
# Deliberately separate from /usr/bin/start-cosmic rather than patching it.
|
|
# The stock COSMIC session entry keeps working untouched, so a broken
|
|
# HyprCosmic config is always one logout away from being escaped — which
|
|
# matters when the thing you are replacing is your desktop.
|
|
|
|
set -e
|
|
|
|
# The forked binaries, installed alongside rather than over the stock ones.
|
|
#
|
|
# Pointing at /usr/bin/cosmic-session here would be a silent no-op: the system
|
|
# binary has no profile module, so the session would come up as ordinary COSMIC
|
|
# and the gating would look broken. Installing to a private prefix instead of
|
|
# replacing /usr/bin also means `dnf update cosmic-session` cannot clobber the
|
|
# fork, and the stock session entry keeps working as the escape hatch.
|
|
HYPRCOSMIC_SESSION_BIN="${HYPRCOSMIC_SESSION_BIN:-/usr/libexec/hyprcosmic/cosmic-session}"
|
|
HYPRCOSMIC_COMP_BIN="${HYPRCOSMIC_COMP_BIN:-/usr/libexec/hyprcosmic/cosmic-comp}"
|
|
|
|
if [[ ! -x "$HYPRCOSMIC_SESSION_BIN" ]]; then
|
|
echo "start-hyprcosmic: $HYPRCOSMIC_SESSION_BIN is missing or not executable" >&2
|
|
echo "start-hyprcosmic: log out and choose the stock COSMIC session" >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Capture this session's output to a file that outlives it.
|
|
#
|
|
# A session that fails drops you straight back to the greeter, taking its
|
|
# stderr with it -- and by then you cannot read the journal from inside a
|
|
# session that no longer exists. A plain file in the cache directory is still
|
|
# there afterwards, whatever happened.
|
|
#
|
|
# A plain redirect, not `tee` into a process substitution: tee would be a child
|
|
# process the session writes through, so if it ever died the session would take
|
|
# a SIGPIPE. Nothing about diagnostics should be able to kill the desktop.
|
|
#
|
|
# Every step is guarded. If any of it fails the session starts anyway with no
|
|
# log -- losing diagnostics is an inconvenience, refusing to log in is not.
|
|
#
|
|
# The previous log is kept as .1, so a second attempt does not erase the
|
|
# evidence from the first.
|
|
HYPRCOSMIC_LOG="${XDG_CACHE_HOME:-$HOME/.cache}/hyprcosmic/session.log"
|
|
if mkdir -p "$(dirname "$HYPRCOSMIC_LOG")" 2>/dev/null; then
|
|
if [[ -f "$HYPRCOSMIC_LOG" ]]; then
|
|
mv -f "$HYPRCOSMIC_LOG" "${HYPRCOSMIC_LOG}.1" 2>/dev/null || true
|
|
fi
|
|
if : >>"$HYPRCOSMIC_LOG" 2>/dev/null; then
|
|
exec >>"$HYPRCOSMIC_LOG" 2>&1
|
|
echo "=== hyprcosmic session starting $(date -Is) ==="
|
|
echo " session bin: $HYPRCOSMIC_SESSION_BIN"
|
|
echo " comp bin: $HYPRCOSMIC_COMP_BIN"
|
|
echo " autostart: ${XDG_CONFIG_HOME:-$HOME/.config}/hyprcosmic/autostart"
|
|
fi
|
|
fi
|
|
|
|
# Selects the component set in cosmic-session's profile module: cosmic-panel,
|
|
# cosmic-launcher, cosmic-app-library, cosmic-workspaces, cosmic-bg and
|
|
# cosmic-files-applet are skipped; waybar and friends come from
|
|
# ~/.config/hyprcosmic/autostart.
|
|
export HYPRCOSMIC_PROFILE=hyprcosmic
|
|
|
|
# Keep COSMIC's identity: portals, cosmic-settings and any COSMIC app still
|
|
# key off this, and the compositor underneath really is COSMIC.
|
|
export XDG_CURRENT_DESKTOP="${XDG_CURRENT_DESKTOP:-COSMIC}"
|
|
export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-hyprcosmic}"
|
|
export XDG_SESSION_TYPE="${XDG_SESSION_TYPE:-wayland}"
|
|
|
|
# Same hygiene as start-cosmic: a failed unit left by a previous graphical
|
|
# session will otherwise block this one from starting.
|
|
if command -v systemctl >/dev/null; then
|
|
for unit in $(systemctl --user --no-legend --state=failed --plain list-units | cut -f1 -d' '); do
|
|
partof="$(systemctl --user show -p PartOf --value "$unit")"
|
|
for target in cosmic-session.target graphical-session.target; do
|
|
if [ "$partof" = "$target" ]; then
|
|
systemctl --user reset-failed "$unit"
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
# Hand the session's identity to `systemd --user`, which is what actually
|
|
# launches the portal.
|
|
#
|
|
# Exporting XDG_CURRENT_DESKTOP above only reaches processes started *by
|
|
# this script*. The user manager is older than the session and has its own
|
|
# environment block, so anything it activates -- xdg-desktop-portal, and
|
|
# through it xdg-desktop-portal-cosmic, which is `SystemdService=` in its
|
|
# D-Bus service file -- never saw the variable.
|
|
#
|
|
# xdg-desktop-portal picks a backend by matching the `UseIn=` line of
|
|
# /usr/share/xdg-desktop-portal/portals/*.portal against
|
|
# XDG_CURRENT_DESKTOP. With the variable unset, `UseIn=COSMIC` did not
|
|
# match, no backend loaded, and org.freedesktop.portal.Screenshot was never
|
|
# exported -- so pressing PrtScr made cosmic-screenshot unwrap an error and
|
|
# abort with "COSMIC screenshot crashed". Nothing logged a missing portal;
|
|
# the only symptom was the crash.
|
|
#
|
|
# Ordering is fine: xdg-desktop-portal.service is `After=` and
|
|
# `Requisite=graphical-session.target`, which cosmic-session brings up
|
|
# later, so the portal cannot start before this import.
|
|
#
|
|
# `||:` throughout -- losing a portal is bad, refusing to log in is worse.
|
|
systemctl --user import-environment \
|
|
XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE ||:
|
|
fi
|
|
|
|
# Same variables again for dbus-daemon's own activation environment, which
|
|
# covers any backend whose .service file has no `SystemdService=` line.
|
|
#
|
|
# Only when a bus already exists. In the dbus-run-session branch below the bus
|
|
# is created *after* this point, so calling it here would talk to the wrong bus
|
|
# or none at all. That branch is the fallback path anyway; a normal graphical
|
|
# login arrives with DBUS_SESSION_BUS_ADDRESS already set.
|
|
if [[ -n "${DBUS_SESSION_BUS_ADDRESS}" ]] && command -v dbus-update-activation-environment >/dev/null; then
|
|
dbus-update-activation-environment \
|
|
XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE ||:
|
|
fi
|
|
|
|
# cosmic-session takes the compositor to launch as its first argument
|
|
# (main.rs: `args.next().unwrap_or_else(|| String::from("cosmic-comp"))`),
|
|
# which is how a forked cosmic-comp gets used without replacing the system one.
|
|
if [[ -z "${DBUS_SESSION_BUS_ADDRESS}" ]]; then
|
|
exec /usr/bin/dbus-run-session -- "${HYPRCOSMIC_SESSION_BIN}" "${HYPRCOSMIC_COMP_BIN}"
|
|
else
|
|
exec "${HYPRCOSMIC_SESSION_BIN}" "${HYPRCOSMIC_COMP_BIN}"
|
|
fi
|