11
57
Fork
You've already forked river-classic
7

contrib: add systemd graphical-session.target support #21

Open
dawidpotocki wants to merge 1 commit from dawidpotocki/river-classic:systemd-target into main
pull from: dawidpotocki/river-classic:systemd-target
merge into: river:main
river:main
river:next-zig
First-time contributor
Copy link

Newest xdg-desktop-portal won't start without the graphical-session.target started.
https://github.com/flatpak/xdg-desktop-portal/issues/1983

Creating a custom target binding to the graphical-session.target seems to be the solution. I imagine it makes sense to at least make those targets part of the repo so that people don't write 50 versions of the same thing. I see that e.g. Arch already had to add their own targets for sway as sway doesn't seem to want to have them in their repo.

Those targets still need to be started by something but I don't know if you would like to carry this, let alone some startup script setting everything up.

I'm still on river-classic but I imagine that river 0.4 also needs it.

Newest xdg-desktop-portal won't start without the `graphical-session.target` started. https://github.com/flatpak/xdg-desktop-portal/issues/1983 Creating a custom target binding to the `graphical-session.target` seems to be the solution. I imagine it makes sense to at least make those targets part of the repo so that people don't write 50 versions of the same thing. I see that e.g. Arch already had to add their own targets for sway as sway doesn't seem to want to have them in their repo. Those targets still need to be started by something but I don't know if you would like to carry this, let alone some startup script setting everything up. I'm still on `river-classic` but I imagine that `river` 0.4 also needs it.
contrib: add systemd graphical-session.target support
All checks were successful
builds.sr.ht/alpine Job completed
builds.sr.ht/archlinux Job completed
builds.sr.ht/freebsd Job completed
02b1f8726b
First-time contributor
Copy link

Indeed. As a workaround, I've got this in my .config/river/init:

systemd-run --user -u river-session -p BindsTo=graphical-session.target -p After=graphical-session.target --service-type oneshot -r -q /usr/bin/true
Indeed. As a workaround, I've got this in my ```.config/river/init```: ``` systemd-run --user -u river-session -p BindsTo=graphical-session.target -p After=graphical-session.target --service-type oneshot -r -q /usr/bin/true ```
dawidpotocki force-pushed systemd-target from 02b1f8726b
All checks were successful
builds.sr.ht/alpine Job completed
builds.sr.ht/archlinux Job completed
builds.sr.ht/freebsd Job completed
to 5ccea2639d
All checks were successful
builds.sr.ht/alpine Job completed
builds.sr.ht/archlinux Job completed
builds.sr.ht/freebsd Job completed
2026年06月14日 13:47:36 +02:00
Compare

Hrm, that's rather annoying. I see that in the sway thread emersion suggested starting the compositor with e.g.

systemd-run --scope --property=BindsTo=graphical-session.target river

That's already 3 different solutions and I'm sure there are more as well. I don't currently use systemd myself and don't consider myself able to judge which of these solutions is the most sensible/least likely to cause problems for end-users down the line.

Would someone be able to explain the trade-offs involved?

Hrm, that's rather annoying. I see that in the sway thread emersion suggested starting the compositor with e.g. ``` systemd-run --scope --property=BindsTo=graphical-session.target river ``` That's already 3 different solutions and I'm sure there are more as well. I don't currently use systemd myself and don't consider myself able to judge which of these solutions is the most sensible/least likely to cause problems for end-users down the line. Would someone be able to explain the trade-offs involved?
Author
First-time contributor
Copy link

I don't think that command from emersion is correct as I tried it earlier and it asked me for my password and then got

Failed to start transient scope unit: Unit graphical-session.target not found.

It will start however when you add --user but then I had an issue where I couldn't run brightnessctl as it complained about not having permissions, so...

What @voegelas posted is kinda hacky and basically just replicates the file river-session.target I have made and just starts it.

The files I have provided are pretty close to what GNOME Shell does (and KDE Plasma I believe):

The river-session.target will need to be started with systemctl start --user river-session.target and when it is killed, it will need the systemctl start --user river-session-shutdown.target. I don't think river has any way to execute commands on riverctl exit or when it is getting SIGINTed, so some kind of a wrapper startup script would be needed (at least in the current state).

The shutdown target is preferred over stopping the river-session.target for the following reason:

$ systemctl status --user river-session.target
 Active: active
$ systemctl status --user graphical-session.target
 Active: active
$ systemctl stop --user river-session.target
$ systemctl status --user river-session.target
 Active: inactive (dead)
$ systemctl status --user graphical-session.target
 Active: active
$ systemctl status --user river-session.target
 Active: active
$ systemctl status --user graphical-session.target
 Active: active
$ systemctl start --user river-session-shutdown.target
$ systemctl status --user river-session.target
 Active: inactive (dead)
$ systemctl status --user graphical-session.target
 Active: inactive (dead)

The reason why we even want it to stop is so that things like xdg-desktop-portal don't think we have a working graphical environment while the user might just be sitting in a TTY.

I don't think that command from emersion is correct as I tried it earlier and it asked me for my password and then got ``` Failed to start transient scope unit: Unit graphical-session.target not found. ``` It will start however when you add `--user` but then I had an issue where I couldn't run `brightnessctl` as it complained about not having permissions, so... What @voegelas posted is kinda hacky and basically just replicates the file `river-session.target` I have made and just starts it. The files I have provided are pretty close to what GNOME Shell does (and KDE Plasma I believe): - https://gitlab.gnome.org/GNOME/gnome-session/-/blob/main/data/gnome-session.target - https://gitlab.gnome.org/GNOME/gnome-session/-/blob/main/data/gnome-session-shutdown.target The `river-session.target` will need to be started with `systemctl start --user river-session.target` and when it is killed, it will need the `systemctl start --user river-session-shutdown.target`. I don't think river has any way to execute commands on `riverctl exit` or when it is getting `SIGINT`ed, so some kind of a wrapper startup script would be needed (at least in the current state). The shutdown target is preferred over stopping the `river-session.target` for the following reason: ``` $ systemctl status --user river-session.target Active: active $ systemctl status --user graphical-session.target Active: active $ systemctl stop --user river-session.target $ systemctl status --user river-session.target Active: inactive (dead) $ systemctl status --user graphical-session.target Active: active ``` ``` $ systemctl status --user river-session.target Active: active $ systemctl status --user graphical-session.target Active: active $ systemctl start --user river-session-shutdown.target $ systemctl status --user river-session.target Active: inactive (dead) $ systemctl status --user graphical-session.target Active: inactive (dead) ``` The reason why we even want it to stop is so that things like `xdg-desktop-portal` don't think we have a working graphical environment while the user might just be sitting in a TTY.
First-time contributor
Copy link

You do not need the shutdown target, PropagatesStopTo= solves this. This is a working river-session.target:

[Unit]
Description=Current river user session
Documentation=man:systemd.special(7)
BindsTo=graphical-session.target
Before=graphical-session.target
PropagatesStopTo=graphical-session.target

I describe this in much more detail at https://devork.be/blog/2025/07/river-as-systemd/ but this one unit is probably all that is needed to resolve the xdg-portal issue. Over there there's also RefuseManualStart= and StopWhenUnneeded= but those require an additional river.service target to exist which is probably much more setup than you'd want to cover here.

The single-line systemd-run solution is also very neat though if you only want the minimal version. It'll need --user and likewise need PropagatesStopTo= but could be a nicer way of documenting this rather than having to add unit files.

I agree that there are so many ways of doing things, and many ways of subtly getting it wrong. This makes it hard to include something like this as an example. Plus an example like this will need some docs.

You do not need the shutdown target, `PropagatesStopTo=` solves this. This is a working river-session.target: ``` [Unit] Description=Current river user session Documentation=man:systemd.special(7) BindsTo=graphical-session.target Before=graphical-session.target PropagatesStopTo=graphical-session.target ``` I describe this in much more detail at https://devork.be/blog/2025/07/river-as-systemd/ but this one unit is probably all that is needed to resolve the xdg-portal issue. Over there there's also `RefuseManualStart=` and `StopWhenUnneeded=` but those require an additional river.service target to exist which is probably much more setup than you'd want to cover here. The single-line systemd-run solution is also very neat though if you only want the minimal version. It'll need --user and likewise need PropagatesStopTo= but could be a nicer way of documenting this rather than having to add unit files. I agree that there are so many ways of doing things, and many ways of subtly getting it wrong. This makes it hard to include something like this as an example. Plus an example like this will need some docs.
All checks were successful
builds.sr.ht/alpine Job completed
builds.sr.ht/archlinux Job completed
builds.sr.ht/freebsd Job completed
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u systemd-target:dawidpotocki-systemd-target
git switch dawidpotocki-systemd-target

Merge

Merge the changes and update on Forgejo.
git switch main
git merge --no-ff dawidpotocki-systemd-target
git switch dawidpotocki-systemd-target
git rebase main
git switch main
git merge --ff-only dawidpotocki-systemd-target
git switch dawidpotocki-systemd-target
git rebase main
git switch main
git merge --no-ff dawidpotocki-systemd-target
git switch main
git merge --squash dawidpotocki-systemd-target
git switch main
git merge --ff-only dawidpotocki-systemd-target
git switch main
git merge dawidpotocki-systemd-target
git push origin main
Sign in to join this conversation.
No reviewers
No labels
bug
upstream
xwayland
Milestone
Clear milestone
No items
No milestone
Projects
Clear projects
No items
No project
Assignees
Clear assignees
No assignees
4 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
river/river-classic!21
Reference in a new issue
river/river-classic
No description provided.
Delete branch "dawidpotocki/river-classic:systemd-target"

Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?