Open-source project
ErikReider/SwayOSD avatar
ErikReider/SwayOSD

SwayOSD: An On-Screen Display for Volume, Mute, Brightness and Lock Keys

A GTK based on screen display for keyboard shortcuts like caps-lock and volume

1,320 stars74 forksRustGPL-3.0

At a glance

What is it?
SwayOSD is a GTK on-screen display for Wayland desktops, built as three Rust binaries: a server that draws the OSD, a client that tells it what to show, and an optional LibInput backend that watches caps lock, num lock and scroll lock directly. It is aimed at Sway and Hyprland users who want a volume indicator without the desktop's own.
Who is it for?
SwayOSD fits a Sway or Hyprland user who wants a consistent volume, mute and brightness overlay across a bare window manager setup, and it does not fit a desktop that already ships a volume OSD, since the two will fight over the same corner of the screen.
Can I use it commercially?
Yes, with conditions. GPL-3.0 is a copyleft licence: if you distribute software that includes it, you must release that software's source code under the same licence. Running it internally without distributing it does not trigger that obligation.
Is it still maintained?
Yes. The repository last received commits 122 days ago.
What is it written in?
Mainly Rust, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 20, 2026, and from our analysis. They are not legal advice.

Editorial analysis

Three Binaries Instead of One Daemon

SwayOSD is a GTK based on-screen display for common actions like volume, playback and caps lock, and the interesting structural decision is that it is not a single program. The Cargo manifest declares three binaries with explicit paths:

- swayosd-server, at src/server/main.rs - swayosd-client, at src/client/main.rs - swayosd-libinput-backend, at src/input-backend/main.rs

The split is a client and server model with a separate input watcher. The server has to run in the background and it is the process that owns the OSD window, which is what the usage notes state: swayosd-server must be running in the background, and swayosd-client is used to send commands and display the OSD.

That design is what makes the rest of the tool work the way it does. The client is a one-shot command that a window manager key binding invokes, it asks the server to show something, and the server handles the GTK window and the animation. So the key bindings in your Sway config stay short, and the OSD window is not recreated for every keypress.

The window is drawn with GTK 4 through gtk-layer-shell, which is how a window is placed as a layer surface on a Wayland compositor rather than as a normal window.

The LibInput Backend Removes the Key Binding Work

The most significant addition in recent versions is the LibInput watcher binary, swayosd-libinput-backend, which can automatically detect key presses so that no key combo binding is needed. The supported keys are listed in the feature list: Caps Lock, Num Lock, Scroll Lock and audio playback.

The background to that is the caps lock entry in the feature list, which notes that it reports a capslock change and that it does not change the caps lock state. That distinction matters. A caps lock indicator that also toggled the key would be a different tool, and one that read the LED would be a different tool again. This one observes the state change and displays it.

The backend is optional and runs either as a systemd service or directly with privilege escalation:

sh
# Using Systemd
sudo systemctl enable --now swayosd-libinput-backend.service

or, for other setups:

sh
pkexec swayosd-libinput-backend

The Cargo manifest shows why it needs elevated access: the backend dependencies include the input crate at 0.9, evdev-rs at 0.6.2, libc and nix with the poll feature, plus async-std and async-channel for the event loop, and blight for the brightness side.

Sway Bindings for Volume, Mute, Brightness and Media Keys

If you do not want the backend, the client can be driven directly from the window manager, and the README gives a full set of Sway bindings. The volume ones show the shape of the interface:

ini
# Sink volume raise optionally with --device
bindsym XF86AudioRaiseVolume exec swayosd-client --output-volume raise
# Sink volume lower optionally with --device
bindsym XF86AudioLowerVolume exec  swayosd-client --output-volume lower --device alsa_output.pci-0000_11_00.4.analog-stereo.monitor
# Sink volume toggle mute
bindsym XF86AudioMute exec swayosd-client --output-volume mute-toggle
# Source volume toggle mute
bindsym XF86AudioMicMute exec swayosd-client --input-volume mute-toggle

The same file shows relative changes with a custom value, where the plus sign is needed for brightness and a plain number is used for volume, and a maximum volume override with --max-volume 120, which pairs with the Customizable maximum Volume feature.

Media keys are handled too, through the --playerctl option, with play-pause and next bound to XF86AudioPlay and XF86AudioNext. Brightness uses the XF86MonBrightnessUp and XF86MonBrightnessDown keys with a --device argument that accepts a device name or a wildcard. The caps lock binding uses the --release modifier so the OSD appears when the key is let go, and there is a variant with a specific LED name taken from /sys/class/leds/.

Picking the Device and the Player

Three optional arguments change what the OSD acts on, and the README documents each of them in its own short section.

--device is for audio and BrightnessCtl devices only. If it is omitted, the default audio device or the first BrightnessCtl device is used. It only changes the target device for the current action that changes the volume or brightness, which is a sensible design: the device is a property of the one command, not a persistent setting. The README tells you how to find the names, with pactl list short sources for inputs and sinks for outputs, and brightnessctl -l for brightness devices, adding -c backlight when you want the backlights specifically.

--player is for playerctl only, and if it is omitted the default playerctld device is used. The README notes that playerctl -l lists the available players, and that there are special operators called all, shift and unshift, the last two of which call playerctld [un]shift to cycle between players.

--monitor decides which screen the OSD appears on. By default, without using --monitor, the OSD is shown on all monitors. On a multi-monitor setup where you want it only on the focused one, the README gives a per-compositor command, using swaymsg for Sway and hyprctl for Hyprland, with jq to pick the focused output.

Theming Requires Plain CSS Because GTK Cannot Read SCSS

The theming section carries a specific and slightly awkward constraint. Because SwayOSD uses GTK, its appearance can be changed, but the style files are initially written in scss, which GTK does not support, so plain css is needed.

The style configuration file lives at ~/.config/swayosd/style.css, and the README is explicit that it is not automatically generated. That means there is no default file to fall back on, and a user who deletes it gets an unstyled window rather than an error.

For a reference, the README links to the style file in the repository, data/style/style.scss, and to an issue discussing the theme. The fact that the source of truth is an scss file while the runtime needs css also tells you the conversion is a manual step, so anyone changing the theme is doing the compile themselves.

The Cargo manifest also lists the cascade crate at 1.0.1 and shrinkwraprs at 0.3.0 among its dependencies. So the theme is assembled from more than one file, and the css in the config directory is the part GTK reads.

Packaging and the Brightness Permission Problem

There are five documented installation routes, and picking the right one saves a lot of time.

Building from source uses Meson, and the README notes that the setup command might require --prefix /usr on some systems:

sh
# Please note that the command below might require `--prefix /usr` on some systems
meson setup build --buildtype release
meson compile -C build
meson install -C build

Fedora users have a COPR package, enabled with dnf copr enable erikreider/swayosd followed by dnf install swayosd, and there is a separate recipe for Fedora Silverblue and other rpm-ostree variants that adds the Copr as an ostree repo and layers the package with rpm-ostree install swayosd.

Arch Linux is covered in two ways, the swayosd package in the extra repository and the swayosd-git AUR package, with the README asking that AUR packaging issues go to the AUR rather than to the repository. Debian and Ubuntu are covered too, since Debian trixie and Ubuntu Plucky ship swayosd via apt.

One permission issue is called out for brightness. Some devices do not permit writing to the sysfs backlight brightness paths. The fix is to install the packaged udev rules and add the user to the video group with sudo usermod -a -G video $USER, and after that everything should work as expected.

Where the Project Stands and What to Check First

SwayOSD is a small, focused project with a clear release history. The versions are v0.3.2 on 2026-06-22, described as further bug fixes, v0.3.1 on 2026-03-12 with bug fixes, and v0.3.0 on 2026-01-15. The repository is not archived and the last push was on 2026-06-22, so the 0.3 line is current.

The project is written in Rust, with the manifest declaring edition 2024 and the package version matching the 0.3.2 release. The dependency set reflects the platform integration precisely: GTK 4 through the gtk4 package at 0.10.0, gtk4-layer-shell at 0.6.3 for the layer surface, zbus 5 for the D-Bus side, libpulse-binding for audio, mpris and playerctld for media control, blight at 0.7.1 for brightness, toml with serde for configuration, and clap with the derive feature for argument parsing.

The README opens by saying this is the author's first time coding in Rust and that fixes and improvements are appreciated, which is a fair signal about the level of polish to expect. For an evaluation, the checks that matter are simple: install the packaging route for your distribution, confirm the client can talk to the server over D-Bus, and try the OSD on a multi-monitor setup, since the monitor selection and the focus detection are the parts most likely to need a tweak for your own hardware.

Editorial conclusion

SwayOSD fits a Sway or Hyprland user who wants a consistent volume, mute and brightness overlay across a bare window manager setup, and it does not fit a desktop that already ships a volume OSD, since the two will fight over the same corner of the screen. Before installing, check the packaging route for your distribution, because the Fedora COPR, the Arch extra repository, the AUR and Debian and Ubuntu are covered while other systems need the Meson build, and read the brightness section, since writing to the backlight sysfs paths needs the packaged udev rules and membership of the video group. The project is GPL-3.0 licensed, the newest release is v0.3.2 from 2026-06-22, and the last push was on 2026-06-22.

Frequently asked questions

Which binaries does SwayOSD install?

Three. swayosd-server from src/server/main.rs draws the OSD and has to run in the background, swayosd-client from src/client/main.rs sends the commands that display it, and the optional swayosd-libinput-backend from src/input-backend/main.rs watches caps lock, num lock, scroll lock and audio playback keys.

How do I install SwayOSD on Fedora?

The package is available on COPR, so `dnf copr enable erikreider/swayosd` followed by `dnf install swayosd`. Fedora Silverblue and other rpm-ostree variants add the Copr as an ostree repo and then run `rpm-ostree install swayosd`.

What does the --device option do?

It selects the target device for audio and BrightnessCtl actions. If omitted, the default audio device or the first BrightnessCtl device is used, and it only changes the target for the current action. The README lists devices with `pactl list short sources` and `brightnessctl -l`.

How do I show the OSD only on the focused monitor?

Pass --monitor with the focused output name. The README gives a Sway command using swaymsg with jq to select the focused output, and an equivalent Hyprland command using hyprctl monitors. Without --monitor the OSD is shown on all monitors.

Why does brightness control need extra permissions?

Some devices do not permit writing to the sysfs backlight brightness paths. The README says to install the packaged udev rules and add the user to the video group with `sudo usermod -a -G video $USER`, after which things should work as expected.

Official sources

  1. ErikReider/SwayOSD on GitHub
  2. Issues
  3. License: GPL-3.0
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/erikreider-swayosd.svg)](https://hysenlabs.com/projects/erikreider-swayosd)

Was this useful?