github YaLTeR/niri v25.02

23 hours ago

Niri is a scrollable-tiling Wayland compositor. Windows are arranged in columns on an infinite strip going to the right. Opening a new window never causes existing windows to resize.

Here are the improvements from the last release.

Note

Packagers: I fixed the problem where some tests required RAYON_NUM_THREADS=1 on a heavily multithreaded CPU. Please remove that variable if you had it set, so that we don't miss any new bugs.

niri-25-02.mp4

Tabbed columns

Columns can now present windows as tabs, rather than vertically stacked tiles. This is useful when you have limited vertical space, or when you frequently switch between two large windows and want to avoid scrolling.

Add this new bind to your config to switch a column to tabbed mode:

binds {
   Mod+W { toggle-column-tabbed-display; }
}

This is the only new bind you will need. All other keyboard and mouse navigation works exactly the same as with regular columns: switch tabs with focus-window-down/up, add or remove windows with consume-window-into-column/expel-window-from-column, and so on. (Thanks @elkowar for this wonderful UX idea!)

niri-tabs.mp4

There are a few new actions that help navigate the tabs. All of them also work on regular columns.

  • focus-window-top/bottom focuses the topmost or the bottommost window in a column.
  • focus-window-down-or-top and focus-window-up-or-bottom cycle the navigation so the focus jumps from the last to the first window and vice versa.
  • focus-window-in-column <index> focuses a specific window by index.

The tab indicator can be customized in several ways and moved to top/bottom/right of the column. See the wiki page for more details.

Tab indicator at the top of the column.

You can also make windows open as tabbed columns by default globally or with a window rule. This goes well with the hide-when-single-tab setting for the tab indicator.

Shadows

Niri can now draw shadows behind windows. Apart from being a nice aesthetic effect, shadows help to delineate floating and otherwise overlapping windows. They are especially useful when you disable or clip away client-side decorations (which commonly include shadows of their own).

Shadows help to tell windows apart.

Niri shadows are not enabled by default to not clash with the shadows coming from client-side decorations. Turning them on is simple enough:

// Enable shadows.
layout {
    shadow {
        on
    }
}

// Also ask windows to omit client-side decorations, so that
// they don't draw their own window shadows.
prefer-no-csd

You can customize properties like softness (blur radius), spread, offset, and color, both globally and for individual windows. Like borders and focus rings, shadows will follow the window corner radius that you set via geometry-corner-radius.

Hard shadows example.

Shadows also work on layer-shell surfaces. Due to the higher variety among layer-shell components, we don't enable shadows for them automatically; you need to explicitly enable them with a layer rule. For example:

// Add a shadow for fuzzel.
layer-rule {
    match namespace="^launcher$"
    
    shadow {
        on
    }

    // Fuzzel defaults to 10 px rounded corners.
    geometry-corner-radius 10
}

Shadow behind fuzzel.

Drag-and-drop view scrolling

In this release I finally addressed one of the longer-standing UX issues: you can now scroll the view left and right during a drag-and-drop operation by moving the mouse close to the monitor's edge. This is similar to how you can scroll various lists and scrolling views in applications during drag-and-drop.

There's a small debounce delay before the scrolling starts so that it doesn't trigger when quickly moving the mouse across monitors. You can customize this, as well as other parameters like maximum scrolling speed, in the new config section.

In addition to drag-and-drop, this gesture will trigger when dragging a window in the tiling layout. Dragging floating windows however won't scroll the view.

niri-dnd-edge-scroll.mp4

Screencast target window rule

There's a new is-window-cast-target=true window rule that matches windows "targetted" by an individual-window screencast. You can use it, for example, to highlight the window that you're screensharing by changing its border/focus ring colors.

// Indicate screencasted windows with red colors.
window-rule {
    match is-window-cast-target=true

    focus-ring {
        active-color "#f38ba8"
        inactive-color "#7d0d2d"
    }

    border {
        inactive-color "#7d0d2d"
    }

    shadow {
        color "#7d0d2d70"
    }

    tab-indicator {
        active-color "#f38ba8"
        inactive-color "#7d0d2d"
    }
}

Screencasted window highlighted in a red color.

Thanks @elkowar for the suggestion!

Custom titles for Important Hotkeys

We have an Important Hotkeys dialog in niri that pops up at startup with a list of the main binds to get you going. The binds in this list and their titles are hardcoded (so that you're not spammed with all the keys bound by default).

In this release, you can customize this list using the new hotkey-overlay-title property.

  • To add a bind to the dialog, or change an existing bind, set it to the title that you want to show:
    binds {
        Mod+Shift+S hotkey-overlay-title="Toggle Dark/Light Style" { spawn "some-script.sh"; }
    }
    
  • To hide an existing bind from the dialog, set it to null:
    binds {
        Mod+Q hotkey-overlay-title=null { close-window; }
    }
    

This is especially useful for binds that spawn programs, as niri can't automatically deduce good titles for them. For example, here's my Important Hotkeys list where I gave nice titles to most spawn binds (everything below PrtSc):

Important Hotkeys with many customized titles.

These custom titles also support full Pango markup which allows you to change styles, colors, and fonts.

Important Hotkeys with custom Pango markup.

I also made two cosmetic changes to the key combo rendering:

  • Ctrl and Shift are now sorted before Alt, matching most other programs. However, when Alt is the Mod key (running niri as a window), then it will be sorted first to emphasize that.
  • Space is now rendered with capital S. These names come from xkb in all kinds of spelling variations, and we have to "prettify" them manually in niri. Space seems fairly common, so I added it to the code.

Expand to available width

Sometimes windows don't quite neatly divide into preset widths, making it hard to fill the space on the monitor exactly. The new expand-column-to-available-width bind addresses this: it expands the focused window to take up all remaining free space on the screen.

Since windows on niri can scroll in and out of view, this bind considers the current window positions. All fully visible windows remain on screen.

niri-expand-to-available-width.mp4

Keyboard shortcuts inhibit protocol

@sodiboo implemented the keyboard-shortcuts-inhibit Wayland protocol. It is used by apps like virtual machines or remote desktop clients to let them pass compositor bindings to the target system.

You can force-deactivate the inhibiting and get your niri shortcuts back using the following new action. Make sure to add it to your config:

binds {
    Mod+Escape { toggle-keyboard-shortcuts-inhibit; }
}

You can also make certain binds ignore inhibiting with the new allow-inhibiting=false property. They will always be handled by niri and never passed to the window.

binds {
    // This bind will always work, even when using a virtual machine.
    Super+Alt+L allow-inhibiting=false { spawn "swaylock"; }
}

Screenshot without writing to disk

Thanks to @sornas, you can now capture screenshots only to the clipboard, without writing the image to disk. Simply press CtrlC in the screenshot UI instead of Space or Enter.

screenshot-screen and screenshot-window binds can do this with a new write-to-disk=false flag:

binds {
    Ctrl+Print { screenshot-screen write-to-disk=false; }
    Alt+Print { screenshot-window write-to-disk=false; }
}

Or, on the command line:

$ niri msg action screenshot-window --write-to-disk=false

Other improvements in this release

  • @sodiboo implemented the wlr-virtual-pointer Wayland protocol required for tools like wayvnc and lan-mouse. These tools should now work with niri, however, keep in mind that you won't able to use niri keyboard binds through them due to a limitation of how the virtual keyboard protocol is currently implemented in Smithay.
  • @bbb651 added the scroll-factor window rule property. It works the same way as the input setting but can be set for a specific window.
  • @Faervan added the toggle-window-rule-opacity action which lets you temporarily make a window fully opaque, if it was semitransparent from a window rule.
  • @notpeelz added a setting to entirely disable the primary clipboard (middle click to paste selected text): clipboard { disable-primary; } at the top level of the niri config.
  • @Kirottu added two actions to move workspaces: niri msg action move-workspace-to-index <INDEX> moves a workspace to a specific position on its monitor, and niri msg action move-workspace-to-monitor <OUTPUT> moves a workspace to a different monitor.
  • @ezemtsov made niri msg action switch-layout accept a layout index (for example, 0 or 1 for the first or the second layout respectively) in addition to next and prev.
  • @m4rch3n1ng added a way to load the keyboard keymap from an .xkb file. See the wiki page for details.
  • @pranaless made the horizontal view movement gesture play better with center-focused-column "on-overflow": it will now snap a window to the center of the screen when it can't fully fit together with the adjacent window.
  • @zzzsyyy added a drag-lock input flag for touchpads that enables libinput drag lock.
  • @JohnDowson added a calibration-matrix input setting for tablets. See the wiki page for details.
  • @valpackett implemented the necessary D-Bus API to apply configuration changes from the Displays panel in gnome-control-center. These changes are transient, similarly to niri msg output: they are not written into your niri config and will be forgotten after a restart.
  • Enabled the fancy miette errors, which makes config parsing errors printed by niri clearer and easier to understand.
  • Changed idle activity notifications to happen at most once per event loop iteration, which fixes lags on some system configurations, especially when using high polling rate mice.
  • Fixed pointer clicks going "through" window borders to the layer-shell surface below.
  • Fixed a bug where the window corner radius did not always apply immediately after changing the config.
  • Fixed one of the longest-standing issues where a fixed preset column width did not take the focused window's border into account. (This went unfixed for so long because it required a refactor to column width handling.) From now on, all ways to set a fixed window size correctly operate on the window excluding the borders.
  • Fixed window focus inside a column jumping down when a window above disappeared. This seems to have been the longest-standing bug in niri to date: it was there from the very first commit of the layout code, three days after I created the niri repository.
  • Fixed the Enter key press to confirm exiting niri also making its way to the window underneath and potentially triggering some action there.
  • Fixed a panic when using animations with overdamped springs.
  • Fixed the niri-session script incompatibility with POSIX sh (thanks @z-erica).
  • Fixed Mod + middle mouse button (to start a view scroll gesture) activating the window under the cursor on press.
  • Named workspaces no longer forget their original monitor when a new window opens on them. This change makes named workspaces "stick" to their original monitor more. For example, disconnecting a monitor with named workspaces, then doing some work, then connecting that monitor again, will move its named workspaces back. After this change, the only way to update a named workspace's original monitor is to explicitly move a named workspace to a different monitor with a bind.
  • Actions that move a workspace across monitors (like move-workspace-to-monitor-right) now update the workspace's original monitor even if the movement itself did nothing (for example, you tried to move to monitor right, but you were already on the rightmost monitor).
  • When live-reloading the config, niri now parses the config on a different thread, preventing a microstutter.
  • Niri will now send windows a single frame callback when asking them to resize or change state, even when they are currently invisible. This became relevant with tabbed columns where invisible windows (from other tabs) influence the column size.
  • Fixed windows receiving duplicate configure events when requesting un/fullscreen in some cases. (They weren't wrong events, just unnecessary.)
  • Fixed backend detection logic ignoring WAYLAND_SOCKET (thanks @bbb651).
  • Corrected behavior when opening or closing windows while no outputs are connected. Before, this likely caused problems and maybe even panics, but I haven't verified it.
  • @notpeelz fixed logging initialization happening too late and potentially missing one warning.
  • Changed client-server tests to work in-memory, without creating and using on-disk Wayland sockets. This fixes the problem where on highly multi-threaded CPUs it was possible to run out of Wayland socket numbers and fail the test.
  • Updated Smithay:
    • Fixed an IME double-input bug in Chromium and Chromium-based apps.
    • Fixed a panic after ~50 days of uptime due to an integer overflow.
    • Implemented the idle-notify v2 protocol which lets tools monitor the user's input activity, ignoring any idle inhibitors.
    • Implemented the ext-data-control protocol (same as wlr-data-control but graduated).

Funding

I work on niri in the spare time that I have from my university studies. If you like what I do, you can support my work on GitHub Sponsors. Big thanks to all current and past sponsors!

Don't miss a new niri release

NewReleases is sending notifications on new releases.