17 KiB
title | slug | date | draft | authors | tags | categories | |||
---|---|---|---|---|---|---|---|---|---|
A minimal graphical session with River, PipeWire and OpenRC | minimal-graphical-session-with-river-pipewire-and-openrc | 2025-08-08 | false |
|
|
|
Graphical sessions are often full of unnecessary features, increasing (unnecessarily) the overall complexity of a desktop system. In this blog entry we intend to go back to the bare minimum, which defines itself as a reasonable step forward in practicality compared the default tty
. Like being able to run graphical applications in windows and being able to manage these windows efficiently. We will meet but not exceed these requirements with River as our tiling window manager, PipeWire as our multimedia framework, and OpenRC as our user-service manager.
User services with OpenRC
As of openrc-0.60
user services are supported by openrc
(1). To enable a openrc
user session we require a runtime dir (XDG_RUNTIME_DIR
), which can be supplied by turnstile (2). Therefore emerge turnstile
:
{ .annotate }
-
Presently the versions
>=0.60
ofopenrc
are unstable. Enable unstable releases ofopenrc
with:sys-apps/openrc ~amd64
-
The
turnstile
ebuild can be found in the portage-ample repository.
sh# emerge -a sys-auth/turnstile
and enable runtime dir management by turnstile
by setting manage_rundir
to yes in turnstiled.conf
:
manage_rundir = yes
Add the turnstile
daemon to the default runlevel:
sh# rc-update add turnstiled default
pam
enables us to generate this rundir
and start the openrc
user session when the user is logged in by enabling the turnstile
and openrc
pam
modules in pam.d/system-login
:
session optional pam_turnstile.so
session optional pam_openrc.so
This user session contains the usual runlevels (boot
, default
, shutdown
) that are run in their respective regimes (1). One approach is to add the window manager in the default
runlevel, such that it will be started at login. But there are particular use cases where the graphical session is not required or wanted (only taking up precious resources), it is therefore wise to configure the graphical session in its own runlevel, to have the freedom to enable it any time you want with openrc -U <runlevel-graphical-session>
(2).
{ .annotate }
- Though
boot
andshutdown
should rather be interpreted aslogin
andlogout
of the user session in this particular case. - Or to link it to the
default
runlevel.
Besides the <runlevel-graphical-session>
, denoted with g
, we will also define a <runlevel-post-graphical-session>
, denoted with h
, for the services depending on the graphical-session. The creation of these runlevels can be simply performed with mkdir
:
sh$ mkdir ~/.config/rc/runlevels/g
sh$ mkdir ~/.config/rc/runlevels/h
and stack them accordingly:
sh$ ln -s /home/<user>/.config/rc/runlevels/default /home/<user>/.config/rc/runlevels/g
sh$ ln -s /home/<user>/.config/rc/runlevels/g /home/<user>/.config/rc/runlevels/h
A set of minimal applications consisting of the terminal emulator (foot
), application launcher (fuzzel
(1)), notification daemon (mako
) and display configurator (kanshi
(2)) that will be of use in the graphical session may be emerged via:
{ .annotate }
-
Presently
fuzzel
is unstable inGURU
. Enable unstable releases offuzzel
with:gui-apps/fuzzel ~amd64
-
Presently
kanshi
and its dependencylibsfcg
is unstable inGURU
. Enable unstable releases ofkanshi
andlibscfg
with:gui-apps/kanshi ~amd64 dev-libs/libscfg ~amd64
sh# emerge -a gui-apps/foot gui-apps/fuzzel gui-apps/mako gui-apps/kanshi
These applications require WAYLAND_DISPLAY
to be set by the window manager. Therefore allow this variable to propagate in the user session:
rc_env_allow="WAYLAND_DISPLAY"
The terminal emulator (foot
) requires the backend footserver
for its clients. Therefore create a user service that will supervise footserver
:
#!/usr/bin/openrc-run
depend() {
need graphical-session
}
supervisor=supervise-daemon
command="/usr/bin/foot"
command_args="--server"
Also the notification daemon (mako
) and the display configurator (kanshi
) require to be supervised, thus create a user service for them as well:
#!/usr/bin/openrc-run
depend() {
need graphical-session
}
supervisor=supervise-daemon
command="/usr/bin/mako"
#!/usr/bin/openrc-run
depend() {
need graphical-session
}
supervisor=supervise-daemon
command="/usr/bin/kanshi"
Adding these user services to <runlevel-post-graphical-session>
can simply be performed with rc-update -U
:
sh$ rc-update -U add footserver h
sh$ rc-update -U add mako h
sh$ rc-update -U add kanshi h
Window management with River
We require a seat manager as the mediating layer between the window manager and the hardware, to be able to run the window manager in the user session. We will use seatd
as the seat management daemon, which requires us to set the following USE
flags:
sys-auth/seatd server -builtin
Now emerge seatd
:
sh# emerge -a seatd
and add the daemon to the default runlevel:
sh# rc-update add seatd default
We need to be part of the
seat
group to effectively communicate with the seat manager:sh# usermod --append --groups seat <user>
Video drivers are necessary for a graphical session to function. In Gentoo it is as simple as specifying what video card the system uses using the USE
flags:
*/* VIDEO_CARDS: -* <amdgpu radeonsi || intel || nvidia>
By setting the VIDEO_CARDS
USE
flag the dependencies of river
are compiled for the video card of the system, thus emerge river
(1):
{ .annotate }
-
Presently
river
is unstable inGURU
. Enable unstable releases ofriver
with:gui-wm/river ~amd64
sh# emerge -a gui-wm/river
Buckle up, this is quite a hefty emerge.
river
starts by executing an init (POSIX shell) script present in ~/.conig/river/init
, this scipt should contain or link to all the configuration of river
. A minimal configuration of the river/init
script is given below:
#!/bin/sh
# Set background and colours
swaybg -m fill -c 000000 -i $HOME/.local/share/backgrounds/<background> & #(1)!
riverctl border-color-focused 0xFFFFFFCC
riverctl border-color-unfocsed 0xFFFFFF66
riverctl border-color-urgent 0xFFFFFF
# Set border-width
riverctl border-width 2
# Set layout
riverctl default-layout rivertile
rivertile --view-padding 4 --outer-padding 4 &
# Source the keybinds
source ~/.config/river/binds
# Switch to post-graphical runlevel
openrc -U h
## Device input settings ##
# Keyboard layout
riverctl keyboard-layout qwerty
# Set keyboard repeat rate
riverctl set-repeat 50 300
# Set focus-follow-cursor
riverctl focus-follow-cursor normal
-
This line is optional, but if you want to spruce up your graphical session, then emerge:
sh# emerge -a gui-apps/swaybg
with the river/binds
given by:
#!/bin/sh
# Super+Shift+Q to exit river
riverctl map normal Super+Shift Q spawn "openrc -U g && openrc -U default"
## Aplication mapping ##
# Super+Backspace to spawn waylock
riverctl map normal Super BackSpace spawn "waylock -ignore-empty-password" #(1)!
# Super+Return to spawn foot
riverctl map normal Super Return spawn footclient
# Super+Space to spawn fuzzel
riverctl map normal Super Space spawn fuzzel
# Super+D to dismiss mako notification
riverctl map normal Super D spawn "makoctl dismiss"
# Super+T to spawn datetime notification
riverctl map normal Super T spawn "$HOME/.local/bin/datetime" #(2)!
# Super+B to spawn battery state notification
riverctl map normal Super B spawn "$HOME/.local/bin/battery" #(3)!
# MonBrightnessUp or MonBrightnessDown to spawn brightness notification (4)
rivcertl map normal None XF86_MonBrightnessUp spawn "$HOME/.local/bin/brightness"
riverctl map normal None XF86_MonBrightnessDown spawn "$HOME/.local/bin/brightness"
# Print to spawn interactive screenshot
riverctl map normal None Print spawn "grim -g "(slurp -d)" - | wl-copy -t image/jpeg" #(5)!
# Shift+Print to spawn screenshot
riverctl map normal Shift Print spawn "grim - | wl-copy -t image/jpeg" #(6)!
## Focused view mapping ##
# Super+Q to close the focused view
riverctl map normal Super Q close
# Super+F to fullscreen toggle the focused view
riverctl map normal Super F toggle-fullscreen
# Super+BTN_MIDDLE to toggle float of the focused view
riverctl map-pointer normal Super BTN_MIDDLE toggle-float
# Super+BTN_LEFT to move the floated focused view
riverctl map-pointer normal Super BTN_LEFT move-view
# Super+BTN_RIGHT to resize the floated focused view
riverctl map-pointer normal Super BTN_RIGHT resize-view
# Super+{K;J} to focus next/previous view
riverctl map normal Super K focus-view next
riverctl map normal Super J focus-view previous
# Super+Shift+{K;J} to swap the focused view with the next/previous view
riverctl map normal Super+Shift K swap next
riverctl map normal Super+Shift J swap previous
# Super+{.;,} to focus the next/previous output
riverctl map normal Super Period focus-output next
riverctl map normal Super Comma focus-output previous
# Super+Shift+{.;,} to send the focused view to the next/previous output
riverctl map normal Super+Shift Period send-to-output next
riverctl map normal Super+Shift Comma send-to-output previous
## Rivertile mapping ##
# Super+{Up;Right;Down;Left} to change layout orientation
riverctl map normal Super Up send-layout-cmd rivertile "main-location top"
riverctl map normal Super Right send-layout-cmd rivertile "main-location right"
riverctl map normal Super Down send-layout-cmd rivertile "main-location down"
riverctl map normal Super Left send-layout-cmd rivertile "main-location left"
# Super+Shift+{Up;Down} to increment/decrement the main count of rivertile
riverctl map normal Super+Shift Up send-layout-cmd rivertile "main-count +1"
riverctl map normal Super+Shift Down send-layout-cmd rivertile "main-count -1"
# Super+Shift+{Right;Left} to increase/decrease the main ratio of rivertile
riverctl map normal Super+Shift Right send-layout-cmd rivertile "main-ratio +0.05"
riverctl map normal Super+Shift Left send-layout-cmd rivertile "main-ratio -0.05"
## Tag mapping ##
for i in $(seq 1 9)
do
j=$((1 << ($i - 1)))
# Super+[1-9] to focus tag [0-8]
riverctl map normal Super $i set-focused-tags $j
# Super+Shift+[1-9] to tag focused view with tag [0-8]
riverctl map normal Super+Shift $i set-view-tags $j
# Super+Control+[1-9] to toggle focus of tag [0-8]
riverctl map normal Super+Control $i toggle-focused-tags $j
# Super+Shift+Control+[1-9] to toggle tag [0-8] of focused view
riverctl map normal Super+Shift+Control $i toggle-view-tags $j
done
all_tags=$(((1 << 32) - 1))
# Super+O to focus all tags
riverctl map normal Super O set-focused-tags $all_tags
# Super+Shift+O to tag focused view with all tags
riverctl map normal Super+Shift O set-view-tags $all_tags
## Media mapping ## (7)
# AudioRaiseVolume to increase volume audio sink by 5%
riverctl map normal None XF86AudioRaiseVolume spawn "$HOME/.local/bin/audio sink volup"
# AudioLowerVolume to decrease volume audio sink by 5%
riverctl map normal None XF86AudioLowerVolume spawn "$HOME/.local/bin/audio sink voldown"
# AudioMute to toggle mute audio sink
riverctl map normal None XF86AudioMute spawn "$HOME/.local/bin/audio sink toggle"
# AudioMicRaiseVolume to increase volume audio source by 5%
riverctl map normal None XF86AudioMicRaiseVolume spawn "$HOME/.local/bin/audio source volup"
# AudioMicLowerVolume to decrease volume audio source by 5%
riverctl map normal None XF86AudioMicLowerVolume spawn "$HOME/.local/bin/audio source voldown"
# AudioMicMute to toggle mute audio source
riverctl map normal None XF86AudioMute spawn "$HOME/.local/bin/audio source toggle"
-
Optionally, it could be useful to lock your session, therefore emerge
waylock
(1): { .annotate }-
Presently
waylock
is unstable inGURU
. Enable unstable releases ofwaylock
with:gui-apps/waylock ~amd64
sh# emerge -a gui-apps/waylock
-
-
Optionally, it could be useful to see the datetime as a notification when you long for it, which is part of ampel/notifiers.
-
Optionally, it could be useful to see the battery state as a notification, especially when you have a laptop. This functionality is part of ampel/notifiers.
-
Optionally, it could be useful to see the brightness state of your display as a notification, which is part of ampel/notifiers. As an additional remark, the default of configuration of
acpid
in Gentoo sucks, setting brightness capability with the same keys requires some changes in the configuration that can be found here. -
Optional screenshot capability.
-
Optional screenshot capability.
-
Optionally, it could be useful to set and get the state of the audio sink and source, which is part of ampel/notifiers.
To be able to run our window manager (river
) supervised, we require the river
user service:
#!/usr/bin/openrc-run
depend() {
need dbus
provide graphical-session
}
supervisor=supervise-daemon
command="/usr/bin/river"
and add it to <runlevel-graphical-session>
:
sh$ rc-update -U add river g
Interfacing audio and video with PipeWire
We will interface audio and video with pipewire
with wireplumber
as fronted and pulseaudio
support, which requires us to set the following USE
flags:
media-video/pipewire sound-server loudness echo-cancel bluetooth
media-sound/pulseaudio -daemon
Emerge libpulse
for pulseaudio
support, pipewire
and wireplumber
:
sh# emerge -a media-libs/libpulse media-video/pipewire media-video/wireplumber
and copy the default configuration of pipewire
and wireplumber
to their respective ~/.config
folders:
sh$ cp /usr/share/pipewire/pipewire.conf ~/.config/pipewire/
sh$ cp /usr/share/wirelumber/wireplumber.conf ~/.config/wireplumber/
Now create the corresponding user services for the pipewire-pulse
support layer, pipewire
itself, and wireplumber
:
#!/usr/bin/openrc-run
depend() {
need audio-session
}
supervisor=supervise-daemon
command="/usr/bin/pipewire-pulse"
#!/usr/bin/openrc-run
depend() {
need graphical-session
provide audio-session
}
supervisor=supervise-daemon
command="/usr/bin/pipewire"
#!/usr/bin/openrc-run
depend() {
need audio-session
}
supervisor=supervise-daemon
command="/usr/bin/wireplumber"
and add them to <runlevel-post-graphical-session>
:
sh$ rc-update -U add pipewire h
sh$ rc-update -U add pipewire-pulse h
sh$ rc-update -U add wireplumber h
We need to be part of the
pipewire
group forpipewire
and dependencies to work accordingly:sh# usermod --append --groups pipewire <user>
For screencast and screenshot support in the graphical session we require the xdg-desktop-portal
layer for rivers' backend wlroots
, together with some tools (grim
, slurp
, wl-clipboard
) to perform and save screenshots to the clipboard:
sh# emerge -a gui-libs/xdg-desktop-portal-wlr gui-apps/grim gui-app/slurp gui-apps/wl-clipboard
The xdg-desktop-portal
should be run supervised, therefore create the xdg-desktop-portal-wlr
user service:
depend() {
need graphical-session
need audio-session
}
supervisor=supervise-daemon
command="/usr/libexec/xdg-desktop-portal-wlr"
and add it to <runlevel-post-graphical-session>
:
sh$ rc-update -U add xdg-desktop-portal-wlr h
Concluding remarks
Well, I think we are there, a minimal graphical session suitable for everyday use. Regarding the customisation of the installed gui-apps, you may have a look at my gentoo-desktop repository, or if you think you have a better taste for aesthetic, then do it yourself you cynical connoisseur!