diff --git a/Cargo.lock b/Cargo.lock index f01f6e8..97e854a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -813,6 +813,7 @@ dependencies = [ "clap", "keyframe", "profiling", + "sd-notify", "smithay", "smithay-drm-extras", "tracing", @@ -1186,6 +1187,12 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" +[[package]] +name = "sd-notify" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "621e3680f3e07db4c9c2c3fb07c6223ab2fab2e54bd3c04c3ae037990f428c32" + [[package]] name = "sharded-slab" version = "0.1.4" diff --git a/Cargo.toml b/Cargo.toml index 4699fbe..c1634f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,7 @@ bitflags = "2.3.3" clap = { version = "4.3.21", features = ["derive"] } keyframe = { version = "1.1.1", default-features = false } profiling = "1.0.9" +sd-notify = "0.4.1" smithay-drm-extras = { version = "0.1.0", path = "../smithay/smithay-drm-extras" } tracing = "0.1.37" tracing-subscriber = { version = "0.3.17", features = ["env-filter"] } diff --git a/resources/niri-session b/resources/niri-session new file mode 100755 index 0000000..77332e3 --- /dev/null +++ b/resources/niri-session @@ -0,0 +1,30 @@ +#!/bin/bash + +# Make sure there's no already running session. +if systemctl --user -q is-active niri.service; then + echo 'A niri session is already running.' + exit 1 +fi + +# Reset failed state of all user units. +systemctl --user reset-failed + +# Set the current desktop for xdg-desktop-portal. +export XDG_CURRENT_DESKTOP=niri + +# Ensure the session type is set to Wayland for xdg-autostart apps. +export XDG_SESSION_TYPE=wayland + +# Import the login manager environment. +systemctl --user import-environment + +# DBus activation environment is independent from systemd. While most of +# dbus-activated services are already using `SystemdService` directive, some +# still don't and thus we should set the dbus environment with a separate +# command. +if hash dbus-update-activation-environment 2>/dev/null; then + dbus-update-activation-environment --all +fi + +# Start niri and wait for it to terminate. +exec systemctl --user --wait start niri.service diff --git a/resources/niri.desktop b/resources/niri.desktop new file mode 100644 index 0000000..5470fe7 --- /dev/null +++ b/resources/niri.desktop @@ -0,0 +1,6 @@ +[Desktop Entry] +Name=Niri +Comment=A scrollable-tiling Wayland compositor +Exec=niri-session +Type=Application +DesktopNames=niri diff --git a/resources/niri.service b/resources/niri.service new file mode 100644 index 0000000..97c6e44 --- /dev/null +++ b/resources/niri.service @@ -0,0 +1,9 @@ +[Unit] +Description=A scrollable-tiling Wayland compositor +BindsTo=graphical-session.target +Wants=graphical-session-pre.target +After=graphical-session-pre.target + +[Service] +Type=notify +ExecStart=/usr/bin/niri diff --git a/src/main.rs b/src/main.rs index 4095334..2b5715f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,6 +19,7 @@ use std::ffi::OsString; use backend::Backend; use clap::Parser; use niri::Niri; +use sd_notify::NotifyState; use smithay::reexports::calloop::EventLoop; use smithay::reexports::wayland_server::{Display, DisplayHandle}; use tracing_subscriber::EnvFilter; @@ -121,4 +122,9 @@ fn main() { } }) .unwrap(); + + // Tell systemd we're stopping. + if let Err(err) = sd_notify::notify(false, &[NotifyState::Stopping]) { + warn!("error notifying systemd: {err:?}"); + } } diff --git a/src/niri.rs b/src/niri.rs index 05a2b3a..b428345 100644 --- a/src/niri.rs +++ b/src/niri.rs @@ -1,8 +1,10 @@ use std::collections::HashMap; use std::os::unix::io::AsRawFd; +use std::process::Command; use std::sync::{Arc, Mutex}; use std::time::Duration; +use sd_notify::NotifyState; use smithay::backend::renderer::element::surface::{ render_elements_from_surface_tree, WaylandSurfaceRenderElement, }; @@ -149,6 +151,27 @@ impl Niri { socket_name.to_string_lossy() ); + if std::env::var_os("NOTIFY_SOCKET").is_some() { + // We're starting as a systemd service. Export our variables and tell systemd we're + // ready. + let rv = Command::new("/bin/sh") + .args([ + "-c", + "systemctl --user import-environment WAYLAND_DISPLAY && \ + hash dbus-update-activation-environment 2>/dev/null && \ + dbus-update-activation-environment WAYLAND_DISPLAY", + ]) + .spawn(); + if let Err(err) = rv { + warn!("error spawning shell to import environment into systemd: {err:?}"); + } + + // Notify systemd we're ready. + if let Err(err) = sd_notify::notify(false, &[NotifyState::Ready]) { + warn!("error notifying systemd: {err:?}"); + }; + } + let display_source = Generic::new( display.backend().poll_fd().as_raw_fd(), Interest::READ,