diff --git a/debian/control b/debian/control index 9434c42..2da9940 100644 --- a/debian/control +++ b/debian/control @@ -2,13 +2,13 @@ Source: tileos-settings-river Section: x11 Priority: optional Maintainer: Aleksey Samoilov -Build-Depends: debhelper-compat (= 13) +Build-Depends: debhelper-compat (= 13), Standards-Version: 4.6.0 Homepage: https://github.com/Tile-OS/tileos-settings-river.git Rules-Requires-Root: no Package: tileos-settings-river Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} +Depends: ${misc:Depends}, python3-tenacity, python3-dbus-next Conflicts: tileos-settings-sway Description: default settings for River on TileOS diff --git a/etc/environment.d/90tileos-river.conf b/etc/environment.d/90tileos-river.conf new file mode 100644 index 0000000..530ed1d --- /dev/null +++ b/etc/environment.d/90tileos-river.conf @@ -0,0 +1,17 @@ +# Force Wayland for Mozilla Firefox +MOZ_ENABLE_WAYLAND=1 +MOZ_DBUS_REMOTE=1 + +# Force Wayland for Qt apps +QT_QPA_PLATFORM="wayland;xcb" +QT_QPA_PLATFORMTHEME=qt5ct + +# Set default cursor size +XCURSOR_SIZE=24 + +# Force Wayland for EFL (Enlightenment) apps +ECORE_EVAS_ENGINE="wayland-egl" +ELM_ACCEL="gl" + +# Java XWayland blank screens fix +_JAVA_AWT_WM_NONREPARENTING=1 diff --git a/etc/skel/.config/river/environment b/etc/skel/.config/river/environment deleted file mode 100644 index 6b53f94..0000000 --- a/etc/skel/.config/river/environment +++ /dev/null @@ -1,3 +0,0 @@ -# This file is a part of TileOS configuration for River and will be sourced -# from /usr/bin/start-river script for all users of the system. -# User-specific variables should be placed in $XDG_CONFIG_HOME/river/environment diff --git a/etc/skel/.config/river/init b/etc/skel/.config/river/init index b2e444c..cab9def 100755 --- a/etc/skel/.config/river/init +++ b/etc/skel/.config/river/init @@ -1,5 +1,41 @@ #!/usr/bin/env bash +export XDG_CURRENT_DESKTOP=river +export XDG_SESSION_DESKTOP="${XDG_SESSION_DESKTOP:-river}" +export XDG_SESSION_TYPE=wayland +VARIABLES="DESKTOP_SESSION XDG_CURRENT_DESKTOP XDG_SESSION_DESKTOP XDG_SESSION_TYPE" +VARIABLES="${VARIABLES} DISPLAY WAYLAND_DISPLAY" +SESSION_TARGET="river-session.target" +SESSION_SHUTDOWN_TARGET="river-session-shutdown.target" +SESSION_XDG_AUTOSTART_TARGET="river-xdg-autostart.target" +SNI_CHECK="/usr/share/river/scripts/wait-sni-ready" + +if hash dbus-update-activation-environment 2>/dev/null; then + # shellcheck disable=SC2086 + dbus-update-activation-environment --systemd ${VARIABLES:- --all} +fi + +# reset failed state of all user units +systemctl --user reset-failed + +# shellcheck disable=SC2086 +systemctl --user import-environment $VARIABLES +systemctl --user start "$SESSION_TARGET" + +# declare cleanup handler and run it on init termination via kill, Ctrl-C or 'riverctl exit' +session_cleanup () { + # stop the session target and unset the variables + systemctl --user start --job-mode=replace-irreversibly "$SESSION_SHUTDOWN_TARGET" + if [ -n "$VARIABLES" ]; then + # shellcheck disable=SC2086 + systemctl --user unset-environment $VARIABLES + fi +} +trap session_cleanup EXIT INT TERM + +# Wait for StatusNotifierWatcher is available and start XDG Autostart target +"$SNI_CHECK" && systemctl --user start "$SESSION_XDG_AUTOSTART_TARGET" + # Set keyboard layout. See man riverctl # riverctl keyboard-layout -options "grp:caps_toggle" "us,ru" @@ -12,9 +48,6 @@ riverctl spawn "/usr/bin/mate-polkit" # Wallpaper riverctl spawn $HOME/.azotebg -# Waybar -riverctl spawn $HOME/.config/river/waybar.sh - # Include keybindings riverctl spawn $HOME/.config/river/keybindings.sh @@ -34,24 +67,13 @@ nwg-drawer -r & disown # Screenshot notification riverctl spawn /usr/share/river/scripts/screenshot-notify.sh & disown -# Lock screen daemon -riverctl spawn $HOME/.config/river/screenlock.sh - -# Poweralertd daemon -pkill poweralertd -poweralertd -s -i "line power" & disown - # Set background and border color riverctl background-color 0x002b36 riverctl border-color-focused 0x8aadf4 riverctl border-color-unfocused 0xa5adcb riverctl border-width 2 -# Import environment variables to D-Bus and systemd -VARIABLES="DISPLAY WAYLAND_DISPLAY XDG_CURRENT_DESKTOP XDG_SESSION_TYPE" - -exec systemctl --user import-environment $VARIABLES - -if hash dbus-update-activation-environment 2>/dev/null; then - exec dbus-update-activation-environment --systemd ${VARIABLES:- --all} -fi +# Set the default layout generator to be rivertile and start it. +# River will send the process group of the init executable SIGTERM on exit. +riverctl default-layout rivertile +rivertile -view-padding 6 -outer-padding 6 diff --git a/etc/skel/.config/river/keybindings.sh b/etc/skel/.config/river/keybindings.sh index f582eda..97bd946 100755 --- a/etc/skel/.config/river/keybindings.sh +++ b/etc/skel/.config/river/keybindings.sh @@ -170,8 +170,3 @@ riverctl float-filter-add app-id "qt6ct" riverctl float-filter-add app-id "pavucontrol" riverctl float-filter-add title "Picture-in-Picture" riverctl float-filter-add title "Firefox — Sharing Indicator" - -# Set the default layout generator to be rivertile and start it. -# River will send the process group of the init executable SIGTERM on exit. -riverctl default-layout rivertile -rivertile -view-padding 6 -outer-padding 6 & diff --git a/etc/skel/.config/river/screenlock.sh b/etc/skel/.config/river/screenlock.sh deleted file mode 100755 index 6de6516..0000000 --- a/etc/skel/.config/river/screenlock.sh +++ /dev/null @@ -1,13 +0,0 @@ -#!/bin/bash - -lock="~/.config/swaylock/lock.sh" - -pkill swayidle -sleep 1s - -exec swayidle -w \ - timeout 240 'light -G > /tmp/brightness && light -S 10' resume 'light -S $([ -f /tmp/brightness ] && cat /tmp/brightness || echo 100%)' \ - timeout 300 $lock \ - before-sleep 'playerctl pause' \ - before-sleep $lock \ - lock $lock & disown diff --git a/etc/skel/.config/river/waybar.sh b/etc/skel/.config/river/waybar.sh deleted file mode 100755 index b598e78..0000000 --- a/etc/skel/.config/river/waybar.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash - -killall -q waybar -while pgrep -x waybar >/dev/null; do sleep 2s; done -exec waybar & disown -exit diff --git a/etc/skel/.config/swayidle/config b/etc/skel/.config/swayidle/config new file mode 100644 index 0000000..36c92e1 --- /dev/null +++ b/etc/skel/.config/swayidle/config @@ -0,0 +1,6 @@ +timeout 240 'light -G > /tmp/brightness && light -S 10' resume 'light -S $([ -f /tmp/brightness ] && cat /tmp/brightness || echo 100%)' +timeout 300 ~/.config/swaylock/lock.sh +timeout 600 'swaymsg "output * power off"' resume 'swaymsg "output * power on"' +before-sleep 'playerctl pause' +before-sleep ~/.config/swaylock/lock.sh +lock ~/.config/swaylock/lock.sh diff --git a/usr/bin/start-river b/usr/bin/start-river index 6537699..5f6653a 100755 --- a/usr/bin/start-river +++ b/usr/bin/start-river @@ -5,21 +5,6 @@ export XDG_CURRENT_DESKTOP=river export XDG_SESSION_DESKTOP=river export XDG_SESSION_TYPE=wayland -# Force Wayland for Mozilla Firefox -export MOZ_ENABLE_WAYLAND=1 -export MOZ_DBUS_REMOTE=1 - -# Force Wayland for Qt apps -export QT_QPA_PLATFORM="wayland;xcb" -export QT_QPA_PLATFORMTHEME=qt5ct - -# Force Wayland for EFL (Enlightenment) apps -export ECORE_EVAS_ENGINE="wayland-egl" -export ELM_ACCEL="gl" - -# Java XWayland blank screens fix -export _JAVA_AWT_WM_NONREPARENTING=1 - # Check if system is running in virtual machine case "$(systemd-detect-virt)" in qemu) @@ -42,11 +27,23 @@ if [ -d /sys/module/nvidia ]; then export LIBVA_DRIVER_NAME=nvidia fi -## Load user environment customizations -if [ -f "${XDG_CONFIG_HOME:-$HOME/.config}/river/environment" ]; then +## Load system environment customizations +if [ -d /etc/environment.d ]; then set -o allexport - # shellcheck source=/dev/null - . "${XDG_CONFIG_HOME:-$HOME/.config}/river/environment" + for f in /etc/environment.d/*.conf; do + # shellcheck source=/dev/null + . "$f" + done + set +o allexport +fi + +## Load user environment customizations +if [ -d "${XDG_CONFIG_HOME:-$HOME/.config}/environment.d" ]; then + set -o allexport + for i in "${XDG_CONFIG_HOME:-$HOME/.config}/environment.d"/*.conf; do + # shellcheck source=/dev/null + . "$i" + done set +o allexport fi diff --git a/usr/lib/systemd/user/river-session-shutdown.target b/usr/lib/systemd/user/river-session-shutdown.target new file mode 100644 index 0000000..d01452e --- /dev/null +++ b/usr/lib/systemd/user/river-session-shutdown.target @@ -0,0 +1,10 @@ +[Unit] +Description=Shutdown running River session +DefaultDependencies=no +StopWhenUnneeded=true + +Conflicts=graphical-session.target graphical-session-pre.target +After=graphical-session.target graphical-session-pre.target + +Conflicts=river-session.target +After=river-session.target diff --git a/usr/lib/systemd/user/river-session.target b/usr/lib/systemd/user/river-session.target new file mode 100644 index 0000000..61ee733 --- /dev/null +++ b/usr/lib/systemd/user/river-session.target @@ -0,0 +1,6 @@ +[Unit] +Description=River session +Documentation=man:systemd.special(7) +BindsTo=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target diff --git a/usr/lib/systemd/user/river-xdg-autostart.target b/usr/lib/systemd/user/river-xdg-autostart.target new file mode 100644 index 0000000..c2d1841 --- /dev/null +++ b/usr/lib/systemd/user/river-xdg-autostart.target @@ -0,0 +1,10 @@ +# Systemd provides xdg-desktop-autostart.target as a way to process XDG autostart +# desktop files. The target sets RefuseManualStart though, and thus cannot be +# used from scripts. +# +[Unit] +Description=XDG autostart for River session +Documentation=man:systemd.special(7) man:systemd-xdg-autostart-generator(8) +BindsTo=xdg-desktop-autostart.target +PartOf=river-session.target +After=river-session.target diff --git a/usr/lib/systemd/user/swayidle.service b/usr/lib/systemd/user/swayidle.service new file mode 100644 index 0000000..4d6a4c0 --- /dev/null +++ b/usr/lib/systemd/user/swayidle.service @@ -0,0 +1,12 @@ +[Unit] +Description=Idle manager for Wayland +Documentation=man:swayidle(1) +PartOf=graphical-session.target +ConditionPathExists=/usr/bin/swayidle + +[Service] +Type=simple +ExecStart=/usr/bin/swayidle + +[Install] +WantedBy=river-session.target diff --git a/usr/share/river/scripts/wait-sni-ready b/usr/share/river/scripts/wait-sni-ready new file mode 100755 index 0000000..68f7be6 --- /dev/null +++ b/usr/share/river/scripts/wait-sni-ready @@ -0,0 +1,61 @@ +#!/usr/bin/python3 +""" +A simple script for waiting until an org.kde.StatusNotifierItem host implementation +is available and ready. + +Dependencies: dbus-next, tenacity +""" +import asyncio +import logging +import os + +from dbus_next.aio import MessageBus +from tenacity import retry, stop_after_delay, wait_fixed + +LOG = logging.getLogger("wait-sni-host") +TIMEOUT = int(os.environ.get("SNI_WAIT_TIMEOUT", default=25)) + + +@retry(reraise=True, stop=stop_after_delay(TIMEOUT), wait=wait_fixed(0.5)) +async def get_service(bus, name, object_path, interface_name): + """Wait until the service appears on the bus""" + introspection = await bus.introspect(name, object_path) + proxy = bus.get_proxy_object(name, object_path, introspection) + return proxy.get_interface(interface_name) + + +async def wait_sni_host(bus: MessageBus): + """Wait until a StatusNotifierWatcher service is available and has a + StatusNotifierHost instance""" + future = asyncio.get_event_loop().create_future() + + async def on_host_registered(): + value = await sni_watcher.get_is_status_notifier_host_registered() + LOG.debug("StatusNotifierHostRegistered: %s", value) + if value: + future.set_result(value) + + sni_watcher = await get_service(bus, "org.kde.StatusNotifierWatcher", + "/StatusNotifierWatcher", + "org.kde.StatusNotifierWatcher") + sni_watcher.on_status_notifier_host_registered(on_host_registered) + # fetch initial value + await on_host_registered() + return await asyncio.wait_for(future, timeout=TIMEOUT) + + +async def main(): + """asyncio entrypoint""" + bus = await MessageBus().connect() + try: + await wait_sni_host(bus) + LOG.info("Successfully waited for org.kde.StatusNotifierHost") + # pylint: disable=broad-except + except Exception as err: + LOG.error("Failed to wait for org.kde.StatusNotifierHost: %s", + str(err)) + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO) + asyncio.run(main())