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
andfocus-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.
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).
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
.
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
}
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"
}
}
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):
These custom titles also support full Pango markup which allows you to change styles, colors, and fonts.
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, andniri 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
or1
for the first or the second layout respectively) in addition tonext
andprev
. - @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 toniri 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 afixed
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 aswlr-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!