-
released this
2026-04-25 18:20:03 +07:00 | 36 commits to main since this release📅 Originally published on GitHub: Sat, 25 Apr 2026 13:50:41 GMT
🏷️ Git tag created: Sat, 25 Apr 2026 11:20:03 GMTNiri 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.
As you may have noticed, niri now lives in a GitHub org rather than my (@YaLTeR) personal account.
The primary reason was the ability to give out issue triage permissions: I'd like to give a massive thanks to @Sempyos for triaging all of our issues and pull requests, answering many, many questions, and helping people diagnose their problems with niri.
We've also moved a few niri-adjacent projects to the GitHub org, like the awesome-niri list of related projects maintained by @Vortriz and a new artwork repo by @bluelinden and @HumpityDumpityDumber—two of the creators of our project logo. In the artwork repo, you can find a badge and several wallpapers, including two stunning 3D works created by @Duncan-Rose in Blender:
pool cut 

The main niri repo also flew past 20,000 stars in February! 🌟 Thanks everyone for support.
Note
Packagers:
- our minimum supported Rust version is now 1.85.
niri.serviceno longer hardcodes/usr/bin/in the niri binary path (thanks @Axlefublr).- @markK24 restructured the dinit service files: https://github.com/niri-wm/niri/commit/3bfa4a71ffb4457ce092454dfad6938f70028c28
Now with introductions out of the way, here are the improvements from the last release.
Blur
It's here. The most requested niri feature by far. Our highest upvoted issue on GitHub. After tireless fork maintenance by @visualglitch91 and @Naxdy, blur is in mainline niri for everyone to use!
Windows and layer-shell components can request blur through the
ext-background-effectWayland protocol with no extra niri configuration. Many already do:- Dank Material Shell v1.4.5: enable background blur in settings
- Noctalia shell: enable in settings and see docs
- Vicinae launcher
- foot terminal v1.26: set
blur=truein colors config - kitty terminal v0.46.2: set
background_blur 1 - Ghostty terminal: will have support in v1.4
Toolkits:
- Quickshell: will have support in v0.3
- winit: will have support in v0.31
For apps that don't support
ext-background-effectyet, you can enable blur through the niri config:// Enable blur behind the Alacritty terminal. window-rule { match app-id="^Alacritty$" background-effect { blur true } } // Enable blur behind the fuzzel launcher. layer-rule { match namespace="^launcher$" background-effect { blur true } }Keep in mind that niri-configured blur needs the right
geometry-corner-radius, and it won't work with complex surface shapes. See the Window Effects wiki page for details.
We have both normal blur and xray blur that always shows the wallpaper. Xray blur is the default because it's much more efficient: niri computes the blurred wallpaper once, and then reuses it as a static image, which is extremely cheap. Only if the wallpaper changes, the blur is recomputed (so an animated background will shrink the efficiency gains).
If you prefer non-xray (normal) blur, you can enable it with a window/layer rule. For example, you can set it on top and overlay layers (that usually overlap other content), via the new
layermatcher:// Make top and overlay layers use the regular blur (if enabled), // while bottom and background layers keep using the efficient xray blur. layer-rule { match layer="top" match layer="overlay" background-effect { xray false } }So, if blur is so good, where's blur 2? Err, I mean, why did it take so long to add?
In short, background blur turned out to be a massive undertaking. Not because of the blur algorithm itself (by the way, if you want to learn about different blurs, including the widely used Dual Kawase, I highly recommend this blog post), but because window background effects in general required a lot of thinking and additions to the code, especially to make them as efficient as possible. This is one of the most complex niri features thus far.
Xray and non-xray effects are also pretty much two entirely separate and very different beasts, code-wise. Non-xray reads back the just-rendered pixels in the middle of a frame, blurs them, then continues drawing the frame. This required extensive refactors of Smithay's rendering architecture (big thanks to @Drakulix!). Xray on the other hand requires threading the window positions all throughout the rendering code to draw the right cut-out of the background.
But it gets worse: we have our Overview. It was quite a challenge figuring out how to support xray blur in the overview, while maintaining the property that it is never re-rendered.
https://github.com/user-attachments/assets/b5b18398-04a2-4af5-8440-15ce3b4bd87a
I also had to get both of them working with all other niri features, like blocking out from screencasts. When the window itself is blocked out that's easy, but what if something in the background layer, inside the blur, is blocked out? An unusual case for sure, but hardly a good exclude if your sensitive data gets accidentally leaked.
https://github.com/user-attachments/assets/71980240-eda0-48de-80ad-dae06b6aa4b4
By the way, I made it so xray can be used on its own, without the blur. As well as the noise and saturation effects (normally for reducing blur color banding and bumping the vividness). For example:
window-rule { match app-id="Alacritty" // Xray without the blur! background-effect { xray true } }One more thing you can do starting from this release is to configure niri to apply transparency and background effects to pop-up menus, using the new
popupsblock in window or layer rules.// Blur the background behind pop-up menus in Loupe. window-rule { match app-id="Loupe" popups { // Matches the default libadwaita pop-up corner radius. geometry-corner-radius 15 // Note: it'll look better to set background opacity // through your GTK theme CSS and not here. // This is just an example that makes it look obvious. opacity 0.5 background-effect { blur true } } }Keep in mind that pop-up rules tend to bump even more into problems with application behavior and surface shapes. For example, web apps or Electron don't use Wayland pop-ups at all; they're entirely emulated inside the client—niri cannot do anything with them.
Shape-wise, in GTK 4, pop-ups with
has-arrow=truewon't look right because they aren't rounded rectangles. Thankfully, clients implementingext-background-effectcan shape their blur in any sort of elaborate pattern.Well, enough about blur, we've got more interesting things to cover!
Optional includes
Pretty much right after I added config includes last release (before I merged them even), people started requesting optional includes—that can be absent without failing config loading. Some use-cases are being able to change parts of an immutable niri config on NixOS, or having local/private overrides for parts of the config.
I pushed back for a time because I think some of those problems should be solved elsewhere, rather than requiring every program with includes to support optional. However, the added code complexity was rather low, so I eventually went ahead and accepted @johnrichardrinehart's implementation.
Starting from this release, you can make an include optional by setting
optional=true:// Won't fail if this file doesn't exist. include optional=true "optional-config.kdl" // Regular include, will fail if the file doesn't exist. include "required-config.kdl"When an optional include file is missing, niri will emit a warning in the logs on every config reload. This reminds you that the file is missing while still loading the config successfully.
The optional file is still watched for changes, so if you create it later, the config will automatically reload and apply the new settings. Finally,
optional=trueonly affects whether a missing file causes an error, so if the file exists but contains invalid syntax or other errors, those errors will still cause a parsing failure.While we're talking about includes: they now expand paths starting with
~to the home directory, so~/file.kdlwill expand to/home/user/file.kdl. Thanks to @HigherOrderLogic and @BennyDeeDev for prototype implementations of this feature.Pointer warping while scrolling
Last release, I made dragging windows horizontally by their titlebars scroll the view left and right. This made mouse-only navigation much more convenient, but I still felt that something was missing.
This release makes the pointer warp from one side of the screen to the other during view scrolling gestures, similarly to Blender. It makes scrolling through several windows natural and convenient, even when you start right next to the monitor edge.
https://github.com/user-attachments/assets/c7e833c8-517c-4988-a758-a5c9b9a84173
Screencasting features
Earlier in the release cycle, I spent some time improving various aspects of our screencasting support. In niri, you can screencast through xdg-desktop-portal-gnome via PipeWire (the recommended approach), or through wlr-screencopy (mainly intended for tools such as wf-recorder). Both of these have seen improvements.
Pointer in window screencasts
When sharing the screen, you generally want to include the cursor in the video stream. In PipeWire, you can either do the simple thing and just draw the cursor directly inside the video frames, or you can attach it as a separate frame metadata. In this mode, the video stream itself doesn't contain the cursor, instead, the compositor sends a separate buffer with the cursor icon and coordinates. The consuming application itself (such as OBS, or your browser in a video meeting) then has to draw the cursor on top.
This allows the consuming application to control the cursor visibility. You might have seen this toggle in OBS; it can work thanks to the metadata cursor mode:
Ever since I implemented PipeWire screencasting in niri about a month into development, it's been using the embedded cursor mode for simplicity. I rendered the cursor for monitor streams and hid it for window streams, and this mostly did what you wanted (and the cursor toggle in OBS didn't work).
Doing it properly has always been in the back of my mind though. I was most missing cursor in window streams because I pretty much always use the window target when screensharing in meetings.
Well, in the summer of last year, @abmantis took up the task. The road was quite bumpy though: they hit and managed to debug a memory corruption issue in PipeWire (that other compositors haven't hit due to more eagerly overriding unchanged data every frame). The bug was thankfully promptly fixed by a PipeWire developer.
It took me several more months to get to the PR (busy with uni and other things as usual), then some heavy refactoring to make it work correctly and iron out all the edge cases, and now niri does screencasting with cursor metadata!
The implementation is quite comprehensive. It works in both monitor and window capture modes and draws the cursor along with its drag-and-drop icon (if any). In window capture mode, the cursor is shown only when it's targeting the window or any of its pop-ups. So, for example, if you cast a window fully covered by another window, and move your mouse on top of that, the screencast of the window below will not show the cursor.
https://github.com/user-attachments/assets/bba92cb3-c78e-4af2-b1fb-049a370c6491
Metadata cursor is also intended to support an optimization where if you move your mouse over an otherwise unchanging screen, the compositor can skip sending these stationary video frames and only update the cursor position. Unfortunately, the OBS PipeWire code doesn't quite allow for this code path yet, so I couldn't do this for the time being.
While working on this, I also found several disagreements between the intended meaning in PipeWire (indicated through comments in header files) and what the code was doing in consumers such as libwebrtc (used in all browsers). This is unfortunate since compositors have to not only work around these problems, but also keep the workarounds forever, as they can't tell how old is the library on the other side of the PipeWire stream. It would be good to have high-quality PipeWire producer and consumer examples for these more complex scenarios, that get all the small details right.
Anyhow! Metadata cursor is here and works with everything I tested. As a bonus, I added pointer capture to window screenshots with a new flag on the action:
screenshot-window show-pointer=true.Delayed start for dynamic cast target
Dynamic cast target is a niri feature that lets you instantly switch what you're screencasting with a keybind. I personally use it all the time because it's very convenient to toggle screencasting between different windows without going through some video conferencing screen sharing UI or having to cast the entire monitor.
You start a dynamic cast by selecting a special "niri Dynamic Cast Target" in the window picker. The dynamic cast always starts as a blank video stream to avoid sharing something sensitive by mistake.
Before this release, the dynamic cast literally started as a 1×1 black pixel video stream. This worked just fine in every app... except, apparently, everyone's favorite Microsoft Teams. So, in this release I changed the dynamic cast to delay starting the video stream until the first dynamic target is selected. As far as the screen sharing programs are concerned, you're just taking a bit longer to pick what to screen share. No more slightly odd brief 1×1 video.
Cast IPC
It can be useful to know if there's an ongoing screencast. For example, desktop bars may want to show a screen recording indicator to alert you of any unintended screen capture.
For PipeWire, a bar could enumerate all ongoing video streams and try to figure out which ones are screencasts, but it's error prone, and for wlr-screencopy there's no way at all to tell from outside the compositor.
So in this release, I added screencast IPC to niri. You can see currently active screencasts with
niri msg casts. Desktop components can subscribe to the niri event stream and listen for the new cast events.The
Castobject provides various bits of information: kind (PipeWire or wlr-screencopy), current target (output, window), whether the cast is active. PipeWire screencasts provide their node ID which you can use to find out the consumer, while wlr-screencopy screencasts provide the client process ID for the same purpose.DankMaterialShell already shows a screencasting indicator using the niri IPC:
And if you need more, it's easy to make a plugin that shows all exposed information.
Keep in mind that for wlr-screencopy, there's no robust way to tell apart different screencasts and screenshots, so I had to come up with some heuristics. Notably, xdg-desktop-portal-wlr always uses and keeps alive a single wlr-screencopy manager object, so there's no way to tell when a screencast has stopped short of using a timeout for when the last frame was requested.
All of these wlr-screencopy problems are fixed in the new ext-image-copy-capture protocol, but we don't have it in niri just yet (and some clients will remain legacy anyway).
Also, with cast IPC providing IDs for screencasts, we can add actions to manipulate them. The new
niri msg action stop-cast --session-id <ID>will force-stop a PipeWire screencast (wlr-screencopy ones cannot currently be stopped through IPC).https://github.com/user-attachments/assets/23fec208-a0dd-4dd8-83e9-e8b6581f3a86
Miscellaneous fixes
Some more random things I fixed in this release:
- Copying with damage would always include the cursor even if the wlr-screencopy client said not to; now this is honored.
- Fixed behavior when a wlr-screencopy client requests multiple frame copies with damage at once. I don't know of any client that does this but now it should work.
- Fixed the niri wlr-screencopy data never getting freed in some cases, like when the client was killed.
- Reduced the default PipeWire screencast buffer count from 16 to 8.
- @kriive worked around a use-after-free bug in pipewire-rs by reordering some struct fields in niri.
- Fixed wrong rendering z-order that could appear for one frame when switching the dynamic cast target to a window.
Animation improvements
In winter, I felt like doing a bit of an "animation detox" and spent several weeks with some niri animations disabled. (If you're curious, I turn off window open, close, resize and movement animations, and leave horizontal view movement since it helps with spatial awareness.) While doing this, I noticed some jank in the unfullscreen/unmaximize animation.
You can configure individual animations differently in niri. However, in several cases, two animations are meant to run together and match exactly. In those cases, niri will synchronize the animations—for example, it can run some animation that is otherwise disabled.
In particular, a window resize animation is synchronized with the horizontal view movement animation that it causes. This way, resizing a window next to the right edge of the monitor will "grow" it to the left instead of an awkward combination of growing to the right and moving back in-view.
The problem that I found is that while fullscreen/maximize correctly synchronized the view movement, unfullscreen/unmaximize didn't. So the window would unmaximize instantly (window-resize is off) but slowly scroll back into position.
This is now fixed:
https://github.com/user-attachments/assets/6db06b54-c83e-462d-b3bc-3259326a1d41
Another animation issue that's been bugging me for a while was fairly specific. When you "drag out" a maximized window, it will automatically unmaximize. If it was floating before you maximized it, it will also automatically return to floating. And when you did this specific action—drag a window to unmaximize into floating—it would skip the horizontal view movement animation of other tiled windows on the same workspace.
https://github.com/user-attachments/assets/d8273330-2a51-44ea-b4e8-6f380f83da35
This was a tricky issue to find because it was at an intersection with another feature: left-right workspace scrolling if you drag-and-drop near a monitor edge. If this drag-and-drop scrolled the view, the view needs to resnap to a window edge, but if it never scrolled the view, then the view should remain exactly as it was, no resnapping. It turned out that this drag-and-drop finalization code ran right after the horizontal view animation was started, and since no scrolling had occurred, it immediately skipped the animation.
The fix was to take a possibly running animation into account explicitly (and add a test of course).
Finally, there was always some weirdness when "dragging out" the leftmost column on a workspace, specifically when it wasn't focused. (You can easily hit this when moving windows from the overview.)
"Dragging out" in this case preserves the view position, which is intended: the focused window (not the one we're dragging out) always remains fixed in the view, regardless of what's happening around it. But dropping the window back would awkwardly put it on the right side instead of where it previously was.
https://github.com/user-attachments/assets/ca5fd740-32a5-4d2c-a21e-b39401fcebc4
After carefully reading through the relevant code (which is among the earliest code I wrote in niri since this is a fundamental windowing operation, but also changed shape many times over development), I noticed that some operation ordering wasn't quite logical when inserting the leftmost column into a workspace, and was able to refactor things a bit to make it work right.
And the last small animation fix was to prevent the slowdown/speedup setting affecting the duration the config error notification is visible on screen.
IME in pop-ups
We fixed (or rather worked around) one long-standing annoying problem: GTK 4 pop-ups with input fields didn't work if you were running an IME like Fcitx5. Effectively, you couldn't open any pop-up with a text entry.
The underlying issue is that Smithay's abstractions don't allow for multiple input grabs at once. Pop-ups generally take a pointer and keyboard grab (notice how when a pop-up is open, moving the mouse over other windows doesn't trigger any hover effects in them), but an IME also works through a keyboard grab in order to handle key events. These two conflicted with each other in niri, so it dropped the pop-up grab, which closed the pop-up immediately upon opening it.
A proper fix would be rearchitecting this part of Smithay, but until then, I loosened some checks, allowing this grab sequence to work. Finally, IME users are able to rename files in Nautilus.
Escape to cancel drag-and-drop
Pressing Escape during drag-and-drop will now cancel the operation. I wanted to add this for a while since it's a common gesture, so I did as soon as Smithay recently merged the necessary code.
Input device improvements
We've had an assortment of improvements to input devices:
- Fixed compounding slowdown over time when using a high Hz mouse with cursor
hide-after-inactive-msor an idle monitoring daemon. - If you have libwayland-server v1.23 or later, niri will increase its Wayland buffer size, so moving a high Hz mouse over non-responsive windows will no longer quickly crash them.
- @qqwa added the
map-to-focused-outputtablet option that makes the tablet target the currently focused output rather than some single configured output. - @skrmc fixed an issue where putting the cursor at the topmost pixels on a workspace wouldn't always target a maximized window under the cursor.
- Fixed Alt-Tab reacting to mouse input before it's visible.
- @ArijanJ made trackball (
on-button-down) scrolling work in the overview. - @mgabor3141 made the Num Lock state preserve across loading a custom .xkb file keymap.
- @Atan-D-RP4 fixed niri being unable to use any input devices when starting from a different TTY via tmux.
- Enabled the loading of libinput plugins.
GPU profiling
One of my main blockers for blur in niri has always been the lack of GPU profiling integration in Smithay. Blur is a heavy operation, and I wanted to see its performance behavior to make good decisions about the code architecture.
In Smithay and niri we use Tracy, a highly capable frame profiler. It supports showing GPU zones, however collecting timestamps from the GPU requires a fair bit of integration work: you need to submit timestamp queries along with your GPU work, then keep a queue of in-flight queries, periodically collect the values of completed queries and upload them to Tracy. At the end of 2025, I sat down and did the necessary work in Smithay which enables both profiling GPU operations done by Smithay itself, and for compositors to annotate their own GPU operations.
Here's an example Tracy recording with GPU zones shown in red at the top, and CPU zones in teal below.
On this recording niri draws a single frame, first to the DRM buffer (goes to the monitor), then, separately, to a buffer for an ongoing PipeWire screencast. You can see the screencast rendering on the GPU in parallel with some CPU work, then as soon as it's done, the CPU is notified, and sends the finished frame over PipeWire to the screencast consumer.
On multi-GPU systems (common on laptops if you have an integrated + discrete GPU), Tracy will show multiple GPU tracks:
On this frame profile, I have a laptop with the main screen (connected to the iGPU) and an external screen (connected to the dGPU). You can see the main GPU rendering both screens (niri renders everything on the main GPU), then the external screen contents are copied over to the dGPU where they are rendered in a single texture draw.
This profiling integration allowed me to verify that blur isn't slower than expected (actually it turned out to run faster than I thought it would). Also, it's now much easier to diagnose dropped frames caused by GPU rendering stalls.
Rendering optimizations
In Smithay and, by extension, niri, rendering works by first constructing a render list, a
Vecof render elements that describe exactly how the final scene is laid out on screen. This render list is then processed by the damage tracker to cut out all invisible and unchanged regions, and then, only if anything needs to be redrawn, the damage tracker hands the elements over to the GPU—just the ones that changed. Compositors try hard to minimize unnecessary redrawing and waking up the GPU to conserve battery.When designing the rendering architecture in niri, I implemented everything through iterators. Functions like
Workspace::render()would return a type like-> impl Iterator<Item = SomeRenderElement>, aggregating and processing render elements from their constituent parts (like individual windows on a workspace). At the top level,Niri::render()would collect from such an iterator into aVecof render elements.Generally, this code structure avoids intermediate allocations (returning an iterator like this compiles down to a state machine that creates all items on-demand as they are pulled by the caller). It also avoids doing unnecessary work since the caller can cut the iterator short at any time if it doesn't need some of the items.
However, as you may know if you have dealt with complex iterators in Rust, there's a whole range of annoyances that come with this kind of structure. For a start, it's hard to write any logic, like conditionals, around returning iterators. Since this
-> impl Iteratormust be a single type, you cannot just write:if condition { return one_iterator; } else { return another_iterator; }It won't compile as these are two different types. You have to come up with workarounds.
Then, in many cases in niri, the returned iterator would borrow from
&self, leading to complex lifetimes. I actually designed for this from the start, withrender()functions intentionally borrowing a shared&self, preceded by a separateupdate(&mut self)step. However, rendering also needs an exclusive&mut Renderer, and this thing did cause annoying borrowing issues every now and then.In several cases, the borrowing is not practical to work around, so I had to fall back to returning a
Vecfrom intermediate functions, which is a short-lived temporary allocation that's immediately freed. Especially unfortunate is that the iterator approach doesn't really work across crate boundaries, so Smithay's surface rendering function returns aVec—and niri calls it per Wayland window and pop-up, causing many temporary allocations during rendering.For a few months, thoughts brewed in my head on how to rearchitect this. Finally, in December, I felt like I had a solid, working idea, and attempted the refactor.
The idea was to replace pull-based functions with push-based ones. Instead of returning
-> impl Iterator<Element>, all rendering functions would accept apush: &mut dyn FnMut(Element)closure, and call it to push their render elements to the list. At the top level, push would simplyfinal_vec.push(element), and intermediate rendering functions would forward this push function down. (This design is not unlike how render tree construction works in GTK 4.)The refactor honestly succeeded beyond my expectations. It solved pretty much all problems I've had. Conditionals become trivial and just work. No complex iterator chains. Functions can still do their logic by wrapping the parent's
pushin their own closure. There's no borrowing. All temporaryVecs gone. As for cutting iterators short, we didn't actually need it in niri.It also sped up the render list construction by 2-3× on my main machines (I didn't expect that):
And, wildly enough, by 8× on my ancient Eee PC! Render list construction does not include the rendering time, which still dominates the frame duration. But it happens much more frequently, even when no actual rendering is needed afterwards, so the improvement is very welcome.
I measured performance and memory use with Tracy. Here's an example profile showing old and new render list construction side-by-side:
The orange line at the bottom tracks the allocated memory. You can see that the previous rendering allocates and drops many times, while the only allocations in the new rendering are pretty much growing the output vector (the steps are the vector capacity increasing as more elements are added—it should be possible to reuse the same vector to get rid of even those, but I haven't got around to it yet).
If you're curious for a more detailed motivation and want to see the diff, which somehow turned out to be negative, see the pull request.
Old laptop support
There's been a long-standing niri issue where screenshots (both built-in and through wlr-screencopy tools) didn't work on old Intel laptops with a weird error. Last week, @xdagiz finally dug in and figured it out: a wrong OpenGL enum value in Smithay.
Also, I did some small optimizations to the niri shaders and managed to fit our resize and clip into the (extremely limited) GPU of an ancient ASUS Eee PC that I have lying around, meaning that window resize animations and compositor-rounded corners now work there (can't say they are particularly smooth though).
Both things combine to show you the following image:
Other improvements in this release
- @cmeissl fixed a VRAM leak that occurred on some systems after closing certain apps.
- @sodiboo and @HigherOrderLogic implemented the
ext-foreign-toplevel-listprotocol which will help Quickshell and other shells associate Wayland window objects with niri IPC window IDs. - @Ind-E made it so the error message for a duplicate bind in the config also shows the first definition of the same bind.
- Mod+LMB window dragging is now indicated with a grabbing cursor (thanks @kchibisov).
- @zimward added the
--pathargument toniri msg action load-config-filewhich lets you switch to a different niri config at runtime. - @Fingel added DMA-BUF support to nested niri, which makes hardware acceleration work there again, now that Mesa's wl_drm is deprecated and phased out.
- Removed padding that niri added to layer-shell pop-ups near monitor edges, as it was more confusing than helpful.
- The default config now binds Mod+M to
maximize-window-to-edgesand Mod+Shift+R toswitch-preset-column-width-back. - Added the
force-disable-connectors-on-resumedebug flag to force a screen blank on TTY switch into niri or waking up from suspend, which can help on some rare hardware configurations. - Putting a window into windowed fullscreen now correctly squares the corners.
- Fixed constant screen repainting while the overview is open.
- Slightly corrected the
relative-to=workspace-viewgradient border rendering for interactively dragged windows. - @jakobhellermann prettified diaeresis shortcut rendering in the Important Hotkeys dialog.
- Fixed the description of
expel-window-from-columninniri msg actionto say that it expels the bottom window (this has been the behavior for a few niri releases already). - Fixed several panics possible if a client tries to use a recently removed output.
- Fixed broken rendering when
clip-to-geometryis applied to a client that attachesy_invertbuffers. - @tobhe fixed building on OpenBSD.
- @titaniumtraveler made nested niri set its window
app-id. - @DuskyElf changed niri to re-evaluate the
ignore-drm-devicedebug settings when a new GPU is plugged in, allowing to use/dev/dri/by-path/symlinks there. - Updated Smithay:
- Improved automatic GPU selection on some devices such as ARM Macs. Asahi and Pinephone should now run niri out of the box with no manual
render-drm-deviceconfiguration necessary. - Improved the behavior of some layer-shell clients like wl_shimeji by not considering subsurfaces for layer surface positioning.
- Improved support for docks that cause monitor EDID to be loaded late.
- Made screenshots and screencasts work on older Intel systems.
- Fixed stale outputs being left behind when some USB-C docks are disconnected while the computer is suspended.
- Fixed a
zxdg_exporter_v2panic with some clients. - Fixed a memory leak when clients using the clipboard protocols don't destroy them explicitly.
- Fixed a panic when the client tries to set unrecognized text-input content hint and purpose (this started to happen in the GTK 4.23 development release).
- Various fixes to drag-and-drop, IME text input and multi-GPU, as well as various performance improvements.
- Improved automatic GPU selection on some devices such as ARM Macs. Asahi and Pinephone should now run niri out of the box with no manual
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!
Downloads
-
released this
2025-11-29 17:00:30 +07:00 | 245 commits to main since this release📅 Originally published on GitHub: Sat, 29 Nov 2025 10:06:23 GMT
🏷️ Git tag created: Sat, 29 Nov 2025 10:00:30 GMTTwo weeks ago, Cloudflare had a major outage that took down a good half of the internet. Their postmortem included a snippet of Rust code with an
unwrap(), causing much internet discourse. Now it is my turn to release Rust software with a healthy dose ofunwrap()s,assert!s, and checked arithmetic enabled in release builds.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: niri now supports libdisplay-info 0.3. Most niri packages updated this dependency back in September using a patch—this patch is no longer necessary.
Alt-Tab
After multiple major iterations and a lot of work by @rustn00b, we have an Alt-Tab recent windows switcher!
https://github.com/user-attachments/assets/4996d0be-638f-403b-b111-89a343d50c00
It's got live window previews, window titles that fade if they're too long, and supports windows blocked out from screencasts—they will draw as black rectangles with their titles hidden. Just like in GNOME, there's a small delay to drawing the interface, so that quickly hitting Alt-Tab will switch to the previous window with no visual disturbance.
There are some interesting design differences compared to Alt-Tab in other desktops. On niri, I expect it's common to have multiple terminals open, so Alt-Tab must go by windows (not by apps), and the previews must be big enough for you to be able to pick the right window. So, instead of a panel showing all windows at once, we have a scrolling layout with a few large previews.
Then, since niri is primarily tiling, you might frequently focus intermediate windows while navigating the workspace. Think pressing Super→ a few times to reach some window towards the end, or moving the mouse across the screen with
focus-follows-mouse. These intermediate windows shouldn't "pollute" the Alt-Tab list, so we have a small debounce delay before a focused window is marked as recent.Workspaces in niri can get long (especially if you forget how many terminals you open), so our Alt-Tab can scope windows by current workspace or by current output by pressing W or O.
The default binds are AltTab / ModTab to switch across all windows, and Alt` / Mod` to switch between windows of the current application. Note that if you bound something else to those keys, your existing binds will take precedence.
The recent windows configuration wiki page explains how to change these key bindings, as well as everything else: open delay, debounce, window preview size, and so on.
Finally, the niri IPC now exposes the window
focus_timestampand an event stream event, so you can use the recent windows list in your own desktop components and scripts.Un/fullscreen animations
The transitions for windows going to and from fullscreen are now fully animated. No more windows jumping and growing to fullscreen size in a single frame.
The black backdrop fades in and out and grows if needed, and
clip-to-geometryrounded corners smoothly transition between square and circular during this animation.https://github.com/user-attachments/assets/05b340b0-4e5e-4b66-93e3-bd42734f33c6
https://github.com/user-attachments/assets/d736341a-7e77-4c06-8fad-96857e2da93a
Even if you run with animations disabled, you will see an improvement: windows will no longer jump up and down when un/fullscreening.
https://github.com/user-attachments/assets/f3316e87-6086-4608-ae2e-a6e202cf03f3
True maximize
One of the bigger changes in this release that required several weeks of work: niri now supports the true Wayland maximize. This is the normal "maximize button next to the X button" or "double-click on the titlebar" maximize.
https://github.com/user-attachments/assets/f2b9bb97-a611-491b-be2d-6c8f95cf2737
We historically didn't implement true Wayland maximize because it's very similar to, yet slightly different from, our full-width columns (the
maximize-columnbind). These preserve gaps, borders and struts, and can contain multiple windows. The true Wayland maximize, on the other hand, makes a single window occupy the entire working area, with no gaps or struts.Full-width column Maximized window After plenty of requests, and thinking about it, I reconsidered. Double-click-to-maximize and the maximize button are things that users (especially new users) expect to work; additionally, plenty of people had asked for variations of "maximize without borders and gaps".
Ironically, after implementing true maximize, it has by far become my favorite window sizing mode. I keep windows true-maximized all the time: the browser, code editor, terminal ssh'd into a remote server.
Since the
maximize-columnandopen-maximizednames are taken by full-width columns, the true-maximize action is calledmaximize-window-to-edges. This name reflects what it does—maximize to the entire working area, right up to the screen edges or exclusive layer-shell surfaces.Similarly to fullscreen, true maximize always puts the window into the scrolling layout (since otherwise it would easily obscure all windows below). Naturally, you can still scroll left and right from a maximized window.
You can read more about fullscreen and maximize on the new wiki page.
Drag windows horizontally to scroll
On a trackpad, you can swipe left and right with three fingers to scroll the view. With a mouse, however, you have to either do a SuperMMB drag, which requires reaching for the keyboard, or switch windows via the Overview hot corner, which causes a lot of movement on screen.
To make scrolling the view mouse-only less annoying, I added a new behavior directly inspired by PaperWM: dragging tiled windows by their titlebar horizontally will scroll the view instead of moving the window. To move the window, you can still drag vertically (or with Super, or in the Overview).
https://github.com/user-attachments/assets/b3dca1c3-fd6f-4139-a431-91de8fc048a5
This also works on touchscreens, finally adding an easy way to scroll the view by touch. I opted to make the touchscreen gesture work even when holding Super, since with touch there's no concern of quickly moving windows across monitors, like you sometimes need to do with a mouse.
https://github.com/user-attachments/assets/59d83b61-dfd9-4b28-983c-4f12a6ea6e19
As a bonus, tapping with a second finger while moving a window now switches it between floating and tiling, just like pressing the right mouse button does.
Per-output and per-workspace layout config
You can now put
layout {}sections insideoutput {}andworkspace "name" {}to override layout settings for specific outputs and named workspaces.output "SomeCompany VerticalMonitor 1234" { transform "90" // Layout config overrides just for this output. layout { default-column-width { proportion 1.0; } // ...any other setting. } } output "SomeCompany UltrawideMonitor 1234" { // Narrower proportions and more presets for an ultrawide. layout { default-column-width { proportion 0.25; } preset-column-widths { proportion 0.2 proportion 0.25 proportion 0.5 proportion 0.75 proportion 0.8 } } } workspace "aesthetic" { // Layout config overrides just for this named workspace. layout { gaps 32 struts { left 64 right 64 bottom 64 top 64 } border { on width 4 } // ...any other setting. } }While simple on the surface, this feature required a complete overhaul of niri's config loading and parsing to support merging settings from multiple definitions of the
layout {}section. As it happens, this was an important prerequisite for...Config includes
You can now include other files in your niri config.
// Some settings... include "colors.kdl" // Some more settings...Included files have the same structure as the main config file. Settings from included files are merged with the settings from the main config. Includes are positional: they will override options set prior to them in the file.
Includes are useful for splitting your config into manageable chunks, and they make it easy for third-party tools to robustly inject niri configuration. For example, my top-level niri config currently looks like this:
// Configuration split into files by sections. include "debug.kdl" include "input.kdl" include "outputs.kdl" include "workspaces.kdl" include "layout.kdl" include "animations.kdl" include "misc.kdl" include "binds.kdl" // Autogenerated settings from DankMaterialShell (DMS). // https://github.com/AvengeMedia/DankMaterialShell include "dms/colors.kdl" include "dms/wpblur.kdl" // These includes are last, they will override settings from DMS. include "decorations.kdl" include "rules.kdl"See the config includes wiki page for more details, for instance how different config sections are merged together.
DisplayLink support
Thanks to @cmeissl and @scottmckendry, niri now supports monitors connected through DisplayLink docks. This is especially important for Asahi Macs, which currently don't support any other way of connecting external displays.
Reduced screen blanking
In this release, I finally adapted the logic from cosmic-comp to avoid screen blanking when possible. If your login manager / TTY resolution and refresh rate match the ones in niri, your screen should switch to niri without flickering to black. This works both when starting niri and when switching the TTY back to a running niri session.
https://github.com/user-attachments/assets/7af85cd6-d218-409e-8d0c-414dade0249e
https://github.com/user-attachments/assets/78dc353d-4017-43ed-84fc-b59d1c66ace3
Even if the screen does blank on your setup (common for high refresh rate monitors where the TTY tends to use a 60 Hz mode, since that's usually the monitor-preferred one), niri should start up faster, as it will do just one modeset instead of two.
Custom output modes
@ToxicMushroom, building upon the initial work of @cmeissl, implemented custom modes and modelines in niri. When configuring an output mode, you can set
custom=trueto force the use of a given mode, even if it's not advertised by the monitor. Alternatively, you can provide a full custom modeline.Custom modes can be useful when the monitor's EDID doesn't advertise the right mode for some reason. For example, some monitors can use a lower refresh rate than any of the advertised modes, which can be useful for reduced power consumption.
Keep in mind that custom modes are not guaranteed to work. Niri is asking the monitor to run in a mode that is not supported by the manufacturer. Trying a non-working mode will generally turn the monitor blank.
Caution
Custom modes may damage your monitor, especially if it's a CRT. Follow the maximum supported limits in your monitor's instructions.
output "eDP-1" { // Custom modes. Caution: may damage your display. mode custom=true "1920x1080@100" // Or: // modeline 173.00 1920 2048 2248 2576 1080 1083 1088 1120 "-hsync" "+vsync" }Screen reader enhancements
Thanks to an issue report, I found out that niri wasn't entirely correctly signalling keyboard modifiers to screen readers. This has been fixed, so combos like Orca + Ctrl + Space and Orca + Shift + A now work on niri.
The new Alt-Tab switcher should also be a nice addition here; from what I understand, it's a common navigation method for screen reader users. I made it speak the selected window title, which, if I'm not mistaken, is the expected behavior. Watch with sound:
https://github.com/user-attachments/assets/3a399726-0d22-4497-9cc6-d4fcc444f882
Other improvements in this release
- @Fireye04 and @Aadniz made the hot corners configurable, including per-output.
- @ilyx-me made it possible to run windowed
niri --sessionin WSL. - @shaunren added the
ignore-drm-devicedebug option that prevents niri from touching the given DRM device. This is useful for GPU passthrough when you need exclusive access to a certain device. - @Szybet made the
calibration-matrixinput setting work for touchscreens. - @nenikitov added proportional change support (
+10%,-10%,10%) to themove-floating-windowaction. - @iynaix added a
--pathargument to thescreenshot,screenshot-windowandscreenshot-screenactions that sets the path where niri will write the screenshot. - @ThatOneCalculator added a new
ScreenshotCapturedIPC event that fires whenever niri captures a screenshot, with the output file path. - The default config now includes play/stop/prev/next media key binds (thanks @anagram3k) and limits the volume adjustment to 100% (thanks @whiskeyPeak).
- Niri will now retry adding a DRM device if it previously failed. This fixes some cases where plugging a monitor into a laptop's port connected to the discrete GPU didn't work.
- Added support for the panel orientation DRM property, used to set the default screen orientation on some devices.
- Niri no longer sets the max bpc DRM property. It didn't really do any help, at the same time it started hitting an annoying driver bug on some OLED panels. The
keep-max-bpc-unchangeddebug flag is now deprecated and does nothing. - The
output { background-color "..."; }setting is now deprecated, use the newoutput { layout { background-color "..."; } }setting instead. - Narrowed the interactive move insert hint in the overview between workspaces. When it was full-width, it gave a false impression that the window would be maximized on the new workspace.
- @ArijanJ made it so when the cursor is hidden via
hide-when-typingorhide-after-inactive-ms, it will still show up in the screenshot UI, letting you optionally include it in the screenshot. - Added laptop lid state monitoring through logind. This should fix any issues where niri didn't notice the laptop lid opening after a sleep cycle (due to libinput dropping the event), which then caused the laptop display to erroneously turn off upon connecting an external monitor.
- Niri will now match the
default-column-widthto a preset width when opening a window. This fixes the first ModR press (switch preset column width) "doing nothing" for certain windows, like foot in constrain-to-terminal-grid sizing mode. - @miku4k changed niri to create the screenshot file directory with parent directories if they don't exist. This makes saving screenshots to disk work out of the box in more cases.
- @elivance fixed parsing of the case-sensitive
XF86ScreenSaverkey. Niri parses keys case-insensitively. Unfortunately, there's a collidingXF86_Screensaverkey (note the smalls) which is not the key that you generally want. Now, for this specific key, niri also checks for a case-sensitive match to disambiguate. - @valpackett fixed niri building with the systemd feature on musl libc systems.
- @feschber fixed discrete scroll event speed in the virtual-pointer protocol.
- Fixed inability to override border/focus-ring/tab-indicator gradient with a plain color in window rules.
- Fixed window rule border/focus-ring width not getting rounded to physical pixels, leading to small animation jank.
- Fixed drag-and-drop cursor surface being slightly offset.
- Fixed a small jump when releasing an interactively moved window in the overview.
- Fixed the hot corner triggering during the view scrolling gesture.
- Fixed incorrect alpha handling in the layout
background-color. - Removed Herobrine.
- Updated Smithay:
- Improved correctness of window commit tracking.
- Fixed Qt layer-shell popup grabs. Popup menus from LXQt panel and desktop now correctly grab the keyboard focus, and can be closed by clicking outside.
- Fixed visual ordering of popups when several open at once, for example popup + tooltip in GTK 4, or some popups in xwayland-satellite.
- Fixed importing DMA-BUFs from v4l2 devices.
- Fixed a possible integer overflow in the damage shaper, which could cause niri crashes with some misbehaving clients.
- Added a tablet pressure workaround that improves behavior for some applications like wine and MyPaint.
- Added support for layer-shell v5 with its
set_exclusive_edge()request, and changed layer surface ordering to consider exclusive zone surfaces first.
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!
Downloads
-
released this
2025-08-30 20:35:06 +07:00 | 447 commits to main since this release📅 Originally published on GitHub: Sat, 30 Aug 2025 13:38:23 GMT
🏷️ Git tag created: Sat, 30 Aug 2025 13:35:06 GMTNiri 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.
A month ago, on July 31st, we hit 10,000 stars on GitHub! Thanks everyone for your support. 😄 Also, on August 10, niri turned two years old since the initial commit.
With introductions out of the way, here are the improvements from the last release.
Note
Packagers:
- Niri still requires libdisplay-info 0.2 (not 0.3). When libdisplay-info-rs updates its requirement, you will likely be able to bump it in niri with no other changes.
- Some tests now require surfaceless EGL to be available at test time. If this is problematic, you can ignore them with
cargo test -- --skip=::egl. - The wiki contents have moved from
wiki/todocs/wiki/. - We have a new Integrating niri wiki page with information on integrating niri into distributions, which you may find helpful.
A logo
After many, many ideas and discussions (first on Matrix, then on GitHub), and several cool design proposals, niri now has logo!
It's a candle! The logo comes in four versions: full-sized, simple full-sized, icon, and simple icon. The simple versions are single-color and suitable for smaller sizes.
full-sized icon normal simple The logo is intentionally recolorable, and you might've already seen several versions across our wiki, Matrix, and Discord. In fact, there's a webpage that lets you quickly adjust the color and download an SVG.
Big thanks to @ixxie, @bluelinden, and @HumpityDumpityDumber for creating and iterating on this design.
New wiki
Thanks to @chinatsu, we have a new interface to browse our wiki! Check it out: https://yalter.github.io/niri/
The main improvement over GitHub Wiki is the interactive full-text search.
The site uses Material for MkDocs, and we retained full compatibility with GitHub Wiki, meaning all existing links keep working. All wiki pages will continue to be deployed to both the new site and the GitHub Wiki.
The new site allows us to make various customizations. For example, @chinatsu made all of our "Since: version" annotations render as badges and link to the corresponding release notes.
Also, thanks everyone for several suggestions and test wikis in the GitHub discussion!
xwayland-satellite integration
There are still plenty of apps, and notably games, that use X11 and thus require Xwayland to work. Setting this up has been a common stumbling block for new niri users, because unlike most other compositors, we don't integrate Xwayland.
This niri release solves that problem by integrating xwayland-satellite. Make sure xwayland-satellite >= 0.7 is installed, remove any manual
$DISPLAYconfiguration you may have had, and Xwayland will just work. If your xwayland-satellite binary is in a non-standard location, you can configure it with a new option.For all intents and purposes, this integration works the same way as Xwayland integration in other compositors. Niri will create X11 sockets on disk, export
$DISPLAY, and spawn xwayland-satellite on-demand when an X11 client connects. If xwayland-satellite dies, niri will automatically restart it.This integration also removes the startup race condition, meaning you can put X11 apps into
spawn-at-startupand systemd autostart.We're using xwayland-satellite rather than Xwayland directly because those same reasons for avoiding Xwayland still apply. xwayland-satellite takes on the bulk of the work of dealing with the X11 peculiarities, giving niri normal Wayland windows to manage.
As a reminder, xwayland-satellite can perfectly run Steam, games, Proton, JetBrains IDEs, Ghidra, Electron apps, and most other X11 clients. But, applications that try to position windows and bars at specific screen coordinates will likely break and need a nested compositor to run.
Huge thanks to @Supreeeme for all the continued development on xwayland-satellite, and making it work as well as it does!
Screen reader support
A series of posts by fireborn earlier this year on the screen reader situation in Linux got me curious: how does one support screen readers in a Wayland compositor? The documentation is unfortunately scarce and difficult to find. Thankfully, @DataTriny from the AccessKit project came across my issue, pointed me at the right protocols, and answered a lot of my questions.
So, as of this release, niri has basic support for screen readers! We implement the
org.freedesktop.a11y.KeyboardMonitorD-Bus interface for Orca to listen and grab keyboard keys, and we expose the main niri UI elements via AccessKit. Specifically, niri will announce:- workspace switching, for example it'll say "Workspace 2";
- the exit confirmation dialog;
- entering the screenshot UI and the overview;
- whenever a config parse error occurs;
- the important hotkeys list.
Here's a demo video, watch with sound on.
https://github.com/user-attachments/assets/afceba6f-79f1-47ec-b859-a0fcb7f8eae3
I also added a default config binding SuperAltS to toggle Orca, which is the standard key binding for this. Orca still requires an X11 socket, so the new xwayland-satellite integration really helps here too.
The current screen reader support and further considerations are documented on the new Accessibility wiki page.
I also want to thank @tyrylu for his talk at this year's GUADEC that sheds some light on the current developments in Linux accessibility, and for answering a slew of my questions when I caught him afterward.
Modal exit confirmation
The screen reader integration necessitated making our exit confirmation dialog a proper modal dialog that takes full keyboard focus. A natural extension of this was to add full-screen dimming, emphasizing that you're about to log out of the entire session, and a nice open/close animation (which you can of course disable if you want).
Hopefully, this will mark the end of me accidentally logging out instead of closing the nested development niri window. (It didn't. Mere dimming is no match for the speed of a well-practiced SuperShiftE ⇒ Enter motion.)
https://github.com/user-attachments/assets/10eaa5d9-3476-4a0f-90e9-46cf2ae39bb9
Screenshot UI improvements
In the last release, the screenshot UI learned to respond to some keyboard window movement bindings by moving the screenshot selection. Now, @iostapyshyn made the screenshot UI also handle the
move-column-left-or-to-monitor-left/rightandmove-window-up-or-to-workspace-up/downactions, while I implementedmove-column/window-to-monitorthat moves the selection across monitors.This works similarly to a floating window: the selection origin is preserved relatively, and the size is adjusted by the monitor scale difference. Under the right conditions, it'll match a floating window exactly.
Also, holding Space while dragging out a selection with the mouse will now let you move the selection!
https://github.com/user-attachments/assets/f5c896ea-390d-4ca1-907b-60667c7324a7
I made it work with a second touch on a touchscreen too. This was inspired by how in osu! you can drag the cursor with one finger and touch with a second/third to "click".
https://github.com/user-attachments/assets/9c6b3574-4c35-48d1-bef3-d311a6dc2c33
Keyboard layout from systemd-localed
To aid distribution integration, niri learned to read the keyboard layout from systemd-localed at
org.freedesktop.locale1over D-Bus. This is now the default behavior when there's no explicit XKB layout configuration, so it should "just work" for distribution installers that set the layout via localed.Screencasting fixes
Some NVIDIA users saw flickering when screencasting in Discord or OBS. Last release added a
wait-for-frame-completion-in-pipewiredebug flag to work around the problem.In this release, I fixed the problem properly (by delaying sending screencast frames over PipeWire until they finished rendering) and removed the debug flag. As a reminder, debug flags are not covered by our config breaking change policy.
I also corrected the app IDs that niri communicates to xdg-desktop-portal-gnome, making it correctly show the icons for most applications in the window picker.
ext-workspace protocol
I implemented the ext-workspace Wayland protocol in niri. It gives desktop components information about workspaces, so bars can use it to make a workspace indicator compatible with different compositors.
Along the way, I found a number of bugs in the ext-workspace code in various bars. All those bugs are now fixed, so you can use the
ext/workspacesmodule in Waybar 0.14, and in upcoming versions of sfwbar and xfce4-panel. Work is ongoing to implement ext-workspace in lxqt-panel and Quickshell.Window positions and sizes in the IPC
After consuming several people (thanks @calops and @aeghn for the intermediate discussions and PRs), the challenge of adding window sizes and positions to the niri IPC was finally bested by @yrkv. Windows returned by
niri msg windowsand the event stream now provide the following layout information:- for windows in the scrolling layout: index of the window's column on the workspace, and index of the window inside that column;
- for floating windows: position on screen;
- for all windows: size of the tile (including borders) and of the underlying Wayland window, and the Wayland window's offset inside the tile.
You can find the detailed documentation in niri-ipc.
This data already lets you order your window lists to match the workspace, make visual displays for floating windows, or keep track of how many columns are to the left/right of a given window. Here's a quick demo I put together in quickshell which tracks floating windows on the current workspace.
https://github.com/user-attachments/assets/57903ed8-272f-44b4-a945-0ac8b9074db8
Some things are still missing, like the current "scroll position" or floating window stacking order. They will be added later, when we figure out the best way to expose them (there's a tricky trade-off between ease of use and spamming the IPC too much).
As another small IPC improvement, @HigherOrderLogic added the
ConfigLoadedevent and adisable-failedoption for the built-in niri config notification. Together, these let desktop shells implement this notification in a custom manner.Config additions and fixes
One common source of confusion about the niri config is the
spawncommand used for running programs. Unlike similar commands in other compositors, niri'sspawnruns the given binary directly, without going through the shell, meaning that you have to manually split arguments and cannot use shell expansions or pipelines.In this release, I added
spawn-shandspawn-sh-at-startupthat accept a single argument—the command to run—and run it through the shell. Withspawn-sh, all complex commands work as expected:binds { // Works with spawn-sh: all arguments in the same string. Mod+D { spawn-sh "alacritty -e /usr/bin/fish"; } // Works with spawn-sh: shell variable ($MAIN_OUTPUT), ~ expansion. Mod+T { spawn-sh "grim -o $MAIN_OUTPUT ~/screenshot.png"; } // Works with spawn-sh: process substitution. Mod+Q { spawn-sh "notify-send clipboard \"$(wl-paste)\""; } // Works with spawn-sh: multiple commands. Super+Alt+S { spawn-sh "pkill orca || exec orca"; } }Under the hood,
spawn-sh "some command"is equivalent tospawn "sh" "-c" "some command"—it's just a less confusing shorthand.Now, on to the rest of the config additions and improvements in this release:
- @two-horned implemented the
switch-preset-column-width-backandswitch-preset-window-height-backactions that reverse the existing preset-switching ones. - @HigherOrderLogic added the
cubic-bezieranimation easing type that lets you use a custom cubic Bézier curve with the same parameters as in CSS. - @chinatsu added the
hide-not-boundhotkey-overlay setting that prevents the Important Hotkeys dialog from showing(not bound)hotkeys. - The pointing device
accel-speedand animationslowdownwere changed to accept integer values without having to spell out the floating point.0. This also added parsing-time limits to the values: [-1; 1] toaccel-speedand [0; 231) toslowdown. While this is technically a config breaking change, the values outside that range weren't valid either way. - @lierdakil added the
scroll-button-lockoption for pointing devices. - @bkuri, after some struggling against Claude Code, implemented separate vertical and horizontal
scroll-factoroptions for pointing devices. - Fixed hot reloading for trackball, tablet, and touch libinput settings.
- @abdavis added clamping to configured color values after color space conversion, fixing a rendering difference between solid colors and gradients.
- @Gwenodai added the
skip-cursor-only-updates-during-vrrdebug flag that prevents cursor movement from redrawing the screen when variable refresh rate is on. This is useful for games where the cursor isn't drawn internally to prevent erratic VRR shifts in response to cursor movement. - @sashomasho added the
deactivate-unfocused-windowsdebug flag that works around incorrect keyboard focus tracking from various Chromium- and Electron-based applications. If your chat window thinks it's focused, and doesn't show message notifications, despite being on a different workspace, then this debug flag might help. - @bbedward added the
keep-max-bpc-unchangeddebug flag as a workaround for a bug with AMDGPU and some OLED panels that causes niri to fail to turn them on. - Clarified and improved the example values for some options in the default config. @schuelermine set the
close-windowbind asrepeat=false. @dev-nicolaos added backlight adjustment binds usingbrightnessctl. - Num Lock state is now preserved across XKB config changes.
- @sodiboo made config hot reloading work for
/etc/niri/config.kdl.
Other improvements in this release
- @HigherOrderLogic made our FreeDesktop idle inhibitor also register its D-Bus service at
/ScreenSaver, making it work with more apps, like VLC. - @my4ng made niri turn off HDR. We don't support HDR yet, so enabled HDR carried over from other compositors would just result in broken colors.
- @zgibberish fixed
--focus=falsenot working when usingmove-column-to-workspace-up/downon a floating window. - Added
--focus=falseargument tomove-window-to-workspace-up/down. - @artrixdotdev added Nushell completion support:
niri completions nushell. - Fixed tiled window popup menus sometimes appearing underneath exclusive-zone layer-shell components (the unconstraining logic wasn't taking these exclusive zones into account).
- Changed the ext-session-lock surface size to round rather than floor, which removes a 1 px "border" on some fractional scales.
- Fixed ext-session-lock clients that got denied the lock being able to create and show a lock surface. In particular, this fixes extra gtklock instances being able to destroy the active lock client, leaving you with an empty red locked session screen.
- @notpeelz made niri set the logind LockedHint when the screen is locked through ext-session-lock. This for example enables idle daemons to only put the computer to sleep when it is locked.
- @sodiboo added SIGINT, SIGTERM and SIGHUP handling to niri: these will now exit niri properly, cleaning up the Wayland sockets and other resources.
- @Vladimir-csp added a check that makes
uwsm start niri.desktopwork for those who use UWSM. - @vanderlokken added
niri msg action load-config-fileto force-reload the config file without waiting for hot reloading. - Fixed a small animation bug when quickly resizing tiled windows back and forth. This problem was fairly difficult to trigger by accident (unless you're specifically trying to reproduce it), but it caused further problems "downstream" for combined window actions.
- Removed redundant "device is inactive" warning spam when switching to a different TTY.
- Fixed changing floating window height resetting the preset width index rather than the preset height index.
- Thanks to @zimward and @matejc for adding Alpine/musl and FreeBSD CI jobs respectively, ensuring that niri keeps building on those systems.
- Updated Smithay:
- Added support for version 2 of the cursor-shape protocol.
- Fixed the keymap sent to clients, making wvkbd work.
- Fixed integer overflows from layer surfaces setting extremely high margin values.
- Fixed an issue where a clipboard client that terminates unexpectedly did not cause an immediate empty clipboard event.
- Fixed mouse input to the bottommost and rightmost edges of a surface sometimes getting missed.
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!
Downloads
-
released this
2025-05-25 13:03:00 +07:00 | 652 commits to main since this release📅 Originally published on GitHub: Sun, 25 May 2025 06:06:09 GMT
🏷️ Git tag created: Sun, 25 May 2025 06:03:00 GMTThis is a hotfix release for niri v25.05.
- Fixed handling of layer surfaces unmapped through a null buffer commit: they will now receive an initial configure as necessary (thanks @alex-huff). This makes the kitty quick access terminal work.
- Fixed unmapped layer surfaces preventing popups from appearing (thanks @alex-huff).
- Fixed brief hover events when the cursor is hidden (thanks @Duncaen). Also fixed brief cursor hovers when a hidden cursor warps (i.e. with
warp-mouse-to-focusor withfocus-monitor-rightetc.). - Renamed
un/set/toggle-urgenttoun/set/toggle-window-urgent, their intended name. This was my oversight. This change is not config-breaking because these actions aren't bindable, but it does mean you need to change the name when callingniri msg action. I went ahead with this break because I don't believe these actions are used anywhere yet (and others confirmed that). - Fixed screen not always redrawing on
niri msg set-window-urgentand other urgency actions. - Updated Smithay:
- Fixed
numlockactivating only after pressing another modifier key (thanks @erdii). - Fixed incorrect reporting of tablet pen tilt.
- Fixed certain clients like fcitx causing frequent keyboard keymap events to clients.
- Fixed
Downloads
-
released this
2025-05-17 20:08:37 +07:00 | 667 commits to main since this release📅 Originally published on GitHub: Sat, 17 May 2025 13:19:09 GMT
🏷️ Git tag created: Sat, 17 May 2025 13:08:37 GMTNiri 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: the niri default config now spawns waybar at startup so as not to start with a blank desktop. Please consider adding Waybar as a recommended dependency (or changing it to some other bar).
The Overview
The big new thing in niri v25.05 is the Overview. It zooms out your workspaces and windows to let you see what's going on at a glance, navigate, and drag windows around, all without having to touch the keyboard.
https://github.com/user-attachments/assets/379a5d1f-acdb-4c11-b36c-e85fd91f0995
You can open it with the
toggle-overviewbind, via the new top-left hot corner, or using a touchpad four-finger swipe. While in the overview, all keyboard shortcuts keep working, while pointing devices get easier:- Mouse: left click and drag windows to move them, right click and drag to scroll workspaces left/right, scroll to switch workspaces (no holding Mod required).
- Touchpad: two-finger scrolling that matches the normal three-finger gestures.
- Touchscreen: one-finger scrolling, or one-finger long press to move a window.
https://github.com/user-attachments/assets/7d9deada-dfb5-4cad-93f2-23b0d72a7877
Drag-and-drop will scroll the workspaces up/down in the overview, and will activate a workspace when holding it for a moment. Combined with the hot corner, this lets you do a mouse-only DnD across workspaces.
https://github.com/user-attachments/assets/5f09c5b7-ff40-462b-8b9c-f1b8073a2cbb
By the way, this new drag-and-drop hold will also bring floating windows to the top outside the overview.
You can drag-and-drop a window to a new workspace above, below, or between existing workspaces.
https://github.com/user-attachments/assets/b76d5349-aa20-4889-ab90-0a51554c789d
To make the spatial model work for the overview, niri now draws a separate background under each workspace. For layer-shell tools, the background and bottom layers zoom out together with the workspaces, while the top and overlay layers remain on top of the overview. Make sure to put your bar on the top layer.
The background behind the workspaces in the overview is called the backdrop. There are new
backdrop-colorsettings both globally in theoverview {}section and per-output.If you want something more interesting in the backdrop, there's a new
place-within-backdroplayer rule. You can use it on a wallpaper tool showing a blurred version of your background, for example.Since backgrounds are now tied to workspaces, they will also move together with workspaces. If you don't like this, you can combine
place-within-backdropwith a transparentbackground-colorto get the stationary wallpaper back. Check the overview wiki page for an example configuration. There, you will also find how to change the overview zoom level or disable the hot corner.There have also been smaller spatial model fixes to accommodate the overview. For example, the top layer-shell layer and the interactively dragged window now render on top of the background- and bottom-layer popups.
Finally, @CharlieQLe added an IPC request and event for monitoring the overview's open state.
Screencasting features
For this release, I worked on several new features for screencasting and screensharing. To keep track of them, I wrote a new wiki page that describes all screencasting-related functionality in niri. Check it out!
I should also mention the new
wait-for-frame-completion-in-pipewiredebug flag by @coleleavitt. If you started having glitches when screencasting on NVIDIA (e.g., in Discord), this flag should help, until we support explicit sync for PipeWire screencasts.Dynamic screencast target
A dynamic cast target is a special target that can change what it streams. You can select it as a special "niri Dynamic Cast Target" in the window selection dialog:
The dynamic target starts as an empty, transparent video stream. Then, you can use the following binds to change what it shows:
set-dynamic-cast-windowto cast the focused window.set-dynamic-cast-monitorto cast the focused monitor.clear-dynamic-cast-targetto go back to an empty stream.
You can also use these actions from the command line, for example, to interactively pick which window to cast. Check the screencasting wiki page for more details and examples.
https://github.com/user-attachments/assets/c617a9d6-7d5e-4f1f-b8cc-9301182d9634
Windowed fullscreen
A common feature in WMs is fake, or detached, or windowed fullscreen. The compositor tells the window that it went fullscreen, but in reality keeps it as a normal window.
This is useful when screencasting browser-based presentations like Google Slides, where you usually want to hide the browser UI, which requires fullscreening the browser. Real fullscreen is not always convenient, for example, if you have an ultrawide monitor, or just want to leave the browser as a smaller window, without taking up an entire monitor.
Now, niri can help, with the new
toggle-windowed-fullscreenbind. It tells the app that it went fullscreen, while in reality leaving it as a normal window that you can resize and put wherever you want.binds { Mod+Ctrl+Shift+F { toggle-windowed-fullscreen; } }Here's an example showing a windowed-fullscreen Google Slides presentation, along with the presenter view and a meeting app, all on the same monitor:
Screenshot UI
For this release, I made the screenshot UI support tablet and touchscreen input for drawing the selection, closing this long-standing gap. I also added a small capture button to the panel at the bottom, making it possible to select and save a screenshot without a keyboard.
https://github.com/user-attachments/assets/0748412e-da21-4909-9c86-2244d8bb068c
Keyboard use got better too: the screenshot UI will now recognize some windowing binds like
move-column-left/right,move-window-up/downandset-window-width/height, and move the selection region as if it was a floating window. Thanks to @nnyyxxxx for prototyping the implementation! Naturally, this opens the door for making a bunch more windowing actions work on the screenshot UI selection.Finally, @TobyBridle added a
show-pointerflag toscreenshotandscreenshot-screenactions to control whether the mouse cursor is included in the image.Window urgency
Urgency is an unspecified but commonly implemented Wayland behavior where a window can request the user's attention. The compositor will then draw it with a red border, and signal this urgency to other desktop components.
Now, @Duncaen implemented window urgency in niri. It comes with a host of
urgent-colorsettings on borders, focus-rings and tab indicators, anis-urgentwindow rule matcher, and urgency indicators for windows and workspaces in the IPC.Urgency is cleared once you focus a window. You can also manually toggle urgency for a specific window with the new
toggle-urgent,set-urgent, andunset-urgentactions.@Duncaen is also adding niri urgency support to Waybar in this PR.
IPC improvements
We have several new things in the IPC system.
@bbb651 implemented
niri msg pick-windowthat lets you select a window by clicking. The command then outputs information about this window, which can be used for scripting. For example, you can make a "screenshot clicked window" script:$ niri msg action screenshot-window --id="$(niri msg --json pick-window | jq .id)"@nnyyxxxx added
niri msg pick-colorthat prints the color of the selected pixel. They also hooked it up to the PickColor method of the Screenshot portal, making the color picker work in apps.https://github.com/user-attachments/assets/93cc4381-851b-4f3f-aa4e-2042acd00b72
The niri IPC socket had so far been conservatively limited to a single request per connection. @titaniumtraveler helped to lift this limitation: now niri will keep reading and replying to requests as they arrive. Keep in mind that requests are still processed one by one and not atomically; read the niri-ipc documentation for more details.
Finally, there's a small UX improvement. It's a common situation after updating niri that the running niri compositor is still the old version (since you haven't restarted it yet), but the niri binary (and hence the
niri msgCLI) is already the new version. Then, using a newly addedniri msgfeature will ask the running niri compositor something that it doesn't understand yet, yielding an error.When this happens,
niri msgwill additionally request the version of the running niri compositor, and notify you if it doesn't match the CLI version, prompting you to restart. Unfortunately, this check didn't fire ifniri msgfailed parsing the response, leading to some confusion.In the new release, I made the check work properly for this case too.
Windowing actions
We've got a number of new actions for working with windows:
- @annikahannig added
focus-monitor,move-window-to-monitor, andmove-column-to-monitorby monitor name (in addition to the existing directional actions). - @Duncaen added
focus-columnandmove-column-to-indexthat work by index of the column within the workspace. - @nnyyxxxx worked on a new
move-window-to-workspace --focus=falseflag that will send the active window to a different workspace without following it. - @yzy-1 added
--focus=falseto themove-column-to-workspace/up/downactions. - Fixed panic when passing out-of-bounds index to
move-workspace-to-index --reference. - Fixed
move-workspace-to-indexbeing 0-based instead of 1-based (we use 1-based workspace indices elsewhere).
I also added a new
center-visible-columnsaction that centers all fully visible columns on screen, as a companion toexpand-column-to-available-widthfrom the last release.https://github.com/user-attachments/assets/55267aeb-5dc8-4b0e-a49a-6961e9d9c3aa
Finally, while not a new action,
consume-or-expel-window-leftwill now restore the view position similarly to closing a window. This is especially useful with tabbed columns: opening a new window and immediately consuming it left will add it as a tab without messing up the view.https://github.com/user-attachments/assets/eb49c7a4-0bc9-4125-9b48-ff0e071c5171
Input device settings
We added several settings for input devices. Read more about them on the input configuration wiki page.
- Added
dragtotouchpadthat allows disabling tap-and-drag (thanks @alexdavid). - Added
offtotouchto disable touchscreen devices (thanks @nnyyxxxx). - Added
left-handedtotrackpoint(thanks @dbeley). - Added
mod-keyandmod-key-nestedthat let you change theModkey (thanks @notpeelz). - Added
numlocksetting tokeyboardto automatically enable Num Lock at startup (thanks @erdii). Note that there's a known issue where it only works after pressing a modifier key (Super, Alt, etc.). - @TyberiusPrime extended
warp-mouse-to-focuswith modes:mode=center-xyandmode=center-xy-alwaysmake the mouse always go to the center of the window both vertically and horizontally.
Output focusing at startup
@lualeet added a new
focus-at-startupoutput flag that will make niri focus that output when starting up if it is connected. Additionally, niri will now start with the mouse cursor centered in the focused monitor.Screen locker red flash fix
Niri will now wait for a lock surface to appear before rendering a locked session, thereby fixing the infamous red flash when locking the screen. Note that semitransparent lock surfaces will still show a red background, so you'll want to disable the fade-in in hyprlock.
https://github.com/user-attachments/assets/3bca8cae-56fd-40c9-a5cb-17e5bd976050
Tiled state window rule
The Tiled state is one of the states a Wayland compositor can set on a toplevel window. Generally, windows react to it by removing their shadows and squaring rounded corners. Terminals stop snapping their window size to the terminal grid.
By default, niri sets the Tiled state together with
prefer-no-csd. However, it can be useful to control the Tiled state separately, for example if you want to keep client-side decorations (for mouse-only dragging and closing) together with square corners (for visuals).In this release, I exposed it as the
tiled-statewindow rule. Apart from blanket enabling, you can also set it based on whether the window is tiled or floating. For example:// Make tiled windows rectangular while using CSD. window-rule { match is-floating=false tiled-state true }https://github.com/user-attachments/assets/270d823f-0fe4-4065-8b3a-5acebc693c8d
More efficient offscreening
Offscreening is rendering a window into an intermediate texture rather than straight to the monitor. It's often used to correctly apply alpha blending when compositing multiple layers.
So far, offscreening in niri was rather inefficient because it was only used during short animations. The intermediate texture would get recreated from scratch every frame. Due to this, I couldn't add any longer-running transparency effects because I didn't want to use the inefficient offscreening.
For this release, I reworked how offscreening works in niri: it reuses the intermediate texture whenever possible, and it correctly tracks damage both "inside" and "outside" the offscreen. So when an offscreened window redraws, only the damaged part will redraw inside the intermediate texture, and this damage will be propagated out, to be used when compositing the texture itself.
Practically, this means slightly improved animation efficiency for window opening and resizing. It also enables using offscreening for longer-running effects. In particular, I could finally make windows semitransparent as you're dragging them around, making it easier to see where they will land inside the tiling layout.
https://github.com/user-attachments/assets/632093d3-6332-4801-bec6-d384578ee0eb
Animations for tabbed columns also use transparency. Before, they didn't use offscreens, resulting in blending artifacts in some conditions. Now they use the new offscreens, so transparency always looks correct.
Regular window opacity does not use offscreening, so border or focus ring may still show through.
Another improvement from the offscreening rework is that offscreens now track which surfaces are visible "inside" the temporary texture. If a surface is completely obscured or otherwise invisible "inside" the temporary texture, it will no longer get frame callbacks. This is a minor efficiency improvement, but it happens to work around an issue with Firefox's experimental subsurface compositor.
baba-is-floatlayer ruleThe last niri release had a secret April Fools' feature: the
baba-is-floatwindow rule that makes windows FLOAT up and down. Now this widely acclaimed feature is available for layer surfaces too!// Make fuzzel FLOAT. layer-rule { match namespace="^launcher$" baba-is-float true }https://github.com/user-attachments/assets/3f4cb1a4-40b2-4766-98b7-eec014c19509
Other improvements in this release
- Fixed cursor hiding with
hide-when-typingorhide-after-inactive-msbreaking tooltips, and input in some first-person games (thanks @bbogdan-ov). - Added a
background-colorsetting tolayout {}that sets the background color for all workspaces/outputs. - Added anchors to links throughout the wiki. Thanks @chinatsu for implementing anchor support in the wiki deployment GitHub action that we use, and for fixing the links.
- Added
niri completionssubcommand to generate shell completions (thanks @titaniumtraveler). - Added
top,bottom,left,rightvalues fordefault-floating-position relative-to=which align the window to the center of a side of a monitor (thanks @Mandarancio). - Added support for negative shadow spread (thanks @LunarEclipse363).
- Added the
honor-xdg-activation-with-invalid-serialdebug flag that fixes focusing the window when clicking on a tray icon for Discord, Telegram, and some other apps. (It is a bug in the apps or their toolkits that they need this debug flag.) - Added
niri msg action toggle-keyboard-shortcuts-inhibit—the bind was added last release, but we forgot the CLI/IPC action (thanks @sodiboo). - Renamed ISO_Level3/5_Shift to Mod5/3 in the Important Hotkeys dialog.
- In the default config, added custom Important Hotkeys titles for the spawn binds.
- Fixed window opening animation being off-center during a concurrent resize animation.
- Fixed clicking on a partially offscreen window with animations disabled hitting the window at its final position, rather than its visible (initial) position. This makes the behavior consistent with enabled animations.
- Fixed putting
center-focused-column "always"in the config not updating the view position right away. - Fixed swipe and DnD scroll gestures not taking into account the time between the last pointer movement and button release, leading to unintended jumps.
- Made it possible for the view position to animate during a DnD scroll. This makes windows scroll into view when "dragging out" the first or the last window on a workspace, like they used to two releases ago.
- Removed cancellation for the workspace-switch gesture. Now when you add or lift a finger during the gesture, it will complete as if you lifted all fingers, rather than resetting back to the starting position.
- Fixed swipe gesture forgetting the previous workspace when starting and stopping on the same workspace.
- Fixed a jump when "catching" a workspace-switch animation with a workspace-switch gesture.
- Made xdg-activation also consider pointer focus for token validity.
- Fixed popups showing up inside the animating resizing window (in addition to rendering normally on top).
- Fixed panic at startup when the configured xkb keymap fails to compile.
- Fixed panic when trying to interactively resize from a tab indicator.
- Fixed panic and broken frames with some overdamped spring animation settings.
- Fixed tab indicator width, gap, gaps_between being able to round down to 0 physical pixels when they are set to above 0 in the config.
- Fixed the interactively moved window getting input on all outputs rather than just the correct one (you could observe this with a combination of touchscreen and mouse input).
- Fixed wrong min/max size computation for windows while going out of fullscreen.
- Fixed interactively moved window not always updating properly.
- Fixed removing a window that is going into fullscreen not resetting the stored unfullscreen view position.
- Fixed interactively resizing a window that is going out of fullscreen not resetting the stored unfullscreen view position.
- Fixed DnD scroll not stopping when interactively moving a fullscreen window that unfullscreens to floating.
- Changed logging to go to stderr rather than stdout (thanks @titaniumtraveler).
- Updated Smithay:
- Fixed panic on monitor hotplugging.
- Fixed panic on some ARM devices.
- Fixed panic upon receiving negative damage width/height from a Wayland client.
- Fixed panic upon receiving subsurface place_above/below with no parent.
- Fixed presentation feedback getting discarded for subsurfaces.
- Fixed some popups going off-screen instead of shrinking.
- Fixed cursor shape sometimes getting stuck in Chromium.
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!
And here's a bonus for everyone who got all the way through this huge release, an ASUS Eee PC from 2008 running the overview!
https://github.com/user-attachments/assets/a0332c82-b412-461b-a1e8-094ec041f127
Downloads
-
released this
2025-02-22 14:20:26 +07:00 | 926 commits to main since this release📅 Originally published on GitHub: Sat, 22 Feb 2025 07:24:21 GMT
🏷️ Git tag created: Sat, 22 Feb 2025 07:20:26 GMTNiri 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=1on a heavily multithreaded CPU. Please remove that variable if you had it set, so that we don't miss any new bugs.https://github.com/user-attachments/assets/a9e37678-5783-4de0-b2c7-a26094cd635f
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 withconsume-window-into-column/expel-window-from-column, and so on. (Thanks @elkowar for this wonderful UX idea!)https://github.com/user-attachments/assets/5b357895-bcf3-42eb-a2e1-2b220d63d0c9
There are a few new actions that help navigate the tabs. All of them also work on regular columns.
focus-window-top/bottomfocuses the topmost or the bottommost window in a column.focus-window-down-or-topandfocus-window-up-or-bottomcycle 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-tabsetting 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-csdYou 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.
https://github.com/user-attachments/assets/9e1f166e-9194-4830-a482-34a00a9a2d61
Screencast target window rule
There's a new
is-window-cast-target=truewindow 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-titleproperty.- 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
spawnprograms, 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-widthbind 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.
https://github.com/user-attachments/assets/cdb99398-77a3-42aa-8406-6a56e32559ca
Keyboard shortcuts inhibit protocol
@sodiboo implemented the
keyboard-shortcuts-inhibitWayland 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=falseproperty. 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-screenandscreenshot-windowbinds can do this with a newwrite-to-disk=falseflag: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=falseOther improvements in this release
- @sodiboo implemented the
wlr-virtual-pointerWayland 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-factorwindow 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-opacityaction 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-layoutaccept a layout index (for example,0or1for the first or the second layout respectively) in addition tonextandprev. - @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-lockinput flag for touchpads that enables libinput drag lock. - @JohnDowson added a
calibration-matrixinput 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
fixedpreset 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 afixedwindow 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-sessionscript 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-notifyv2 protocol which lets tools monitor the user's input activity, ignoring any idle inhibitors. - Implemented the
ext-data-controlprotocol (same aswlr-data-controlbut 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!
Downloads
-
released this
2025-01-11 23:53:25 +07:00 | 1105 commits to main since this release📅 Originally published on GitHub: Sat, 11 Jan 2025 17:27:52 GMT
🏷️ Git tag created: Sat, 11 Jan 2025 16:53:25 GMTNiri 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... hang on, how come we jumped from v0.1 all the way to v25?
Starting now, niri
escapes ZeroVeris switching to year.month versioning. In 25.01, the "25" is year 2025, and "01" is month 01 (January). So version 25.01 tells you that this release was tagged in January of 2025.Hotfix releases will use the third component. For example, the first hotfix for the 25.01 release would be called 25.01.1.
There are a few reasons for this change.
- For niri, semver isn't very useful. Big and small features are added every release, and so far we've managed to avoid any breaking changes to the config file. Calendar versioning at least tells you how old of a version you're running.
- v0.1.x left no place for a hotfix version. I couldn't even put v0.1.10.1 into
Cargo.tomlbecause it has four components instead of three. The new versioning has just two components, leaving one extra for the hotfix version. - I feel like niri is now sufficiently featureful to graduate from v0.1. :) I expanded the Status section of the README to cover some of the frequently asked "is this thing supported" questions.
Similar versioning is also used in other projects like Helix, NixOS and Ubuntu.
With this change, the niri releases remain unscheduled: once every few months, and not bound to any particular cycle. Whenever I feel that it's a good time for a new version.
Note
Packagers: niri now requires Rust 1.80. Also, there are new environment variables to override the niri version string and commit:
NIRI_BUILD_VERSION_STRINGandNIRI_BUILD_COMMIT. The new Packaging niri wiki page shows how to use them, along with everything else important for packaging.New niri tests need
XDG_RUNTIME_DIRto be set. You can useexport XDG_RUNTIME_DIR="$(mktemp -d)".If some tests fail with
Err::AlreadyInUseon a heavily multi-threaded CPU, setRAYON_NUM_THREADS=1. This is tracked in #953.Floating windows
Floating windows are here! It took a big refactor and a good month of hard work, but the most liked niri feature request is done.
Like other WMs, niri will auto-float dialogs and fixed-size windows. With no extra configuration, this release does away with most of the annoying dialog scrolling.
https://github.com/user-attachments/assets/d560582c-07fd-4fcb-bf86-9ed305df515f
Opening LibreOffice no longer causes the view to shift an ultrawide worth to the right.
Being a scrolling WM, there were several options and design decisions to consider for how floating windows should work. I opted for a setup familiar from other tiling WMs: floating windows are on a separate "layer" that always shows on top of the tiled windows, and the floating layout does not scroll. Each workspace/monitor has its own floating layout, just like each workspace/monitor has its own tiling layout.
Finally, you can properly showcase your scrollable-tiling WM setup.
There's a surprising number of features and small details that go into a good floating experience. Things like correct parent-child stacking, focus-follows-mouse activating but not raising the window, or restoring the floating size and position after moving the window to the tiling layout and back.
https://github.com/user-attachments/assets/376ef176-c546-400e-a977-b21a679dda81
Since floating windows live on a workspace, and workspaces can move between monitors, it's important that floating windows never end up "out of bounds" and unreachable outside the monitor.
Internally, niri remembers floating window positions relative to the monitor size, and will always push windows slightly away from the monitor edges. This way, windows are always visible, and moving the workspace to a smaller monitor will roughly preserve the window layout. Furthermore, moving the workspace to a smaller monitor and back will restore the original window positions exactly.
In the following demo, I'm resizing a nested niri with three floating windows, simulating monitor resolution changes.
https://github.com/user-attachments/assets/3c4d43bf-4e95-4682-acc2-4598ec06cd20
There's a set of actions for focusing the floating or the tiling layout, and for moving windows around. The updated default config includes
switch-focus-between-floating-and-tilingbound to ModShiftV andtoggle-window-floatingbound to ModV. All relevant existing binds keep working when the focus is on the floating layout, e.g.focus-column-rightwill activate the next floating window to the right.Additionally, on a mouse, you can easily move a window between floating and tiling by right-clicking while dragging it. You can tell which of the two it "targets" by the presence of the tiling insertion hint.
https://github.com/user-attachments/assets/c09496b1-db40-48a5-9ba4-90ef55768dac
There's a new
is-floatingwindow rule matcher, and newopen-floatinganddefault-floating-positionrules.You can use
open-floatingto float some window that isn't covered by the auto-floating heuristics, like the Firefox Picture-in-Picture player. Anddefault-floating-positionsupports putting floating windows relative to the four corners of a monitor:// Open the Firefox Picture-in-Picture window at the bottom-left corner of the screen // with a small gap. window-rule { match app-id="firefox$" title="^Picture-in-Picture$" open-floating true default-floating-position x=32 y=32 relative-to="bottom-left" }Meanwhile, real tiling WM users like @algernon can set a blanket
open-floating falserule to disable all auto-floating heuristics. Rest assured that our new set of 3135 snapshot tests across all possible window opening settings will keep this working.All in all, this release contains a fairly complete per-workspace floating layout. Going forward, we can expand the functionality, for example by adding a sticky/show on all workspaces window flag. Or perhaps by putting modal dialogs as floating right into the scrolling layout.
Also, when resizing tiled windows, their height is now clamped to the monitor height. It used to be unlimited so that you could take window screenshots larger than the monitor size, but now you can do that with a floating window.
Layer-shell improvements
This release has several fixes to layer-shell handling.
- @cmeissl fixed the problem where the pop-up menu on Waybar and other GTK 3 bars would sometimes get stuck and fail to open. The way it was fixed disables keyboard navigation in those menus, but this is consistent with other compositors like Sway.
- @cmeissl also fixed some clients crashing when opening nested pop-up menus (like lxqt-panel app menus).
- @calops fixed niri not always activating the window below when clicking through layer-shell surfaces. Previously, that code didn't account for the surface's input region.
- Pop-up menus from all layer-shell surfaces now render on top of regular windows. So putting Waybar at the top layer is no longer necessary for usable context menus.
- Niri will now give bottom and background layer-shell surfaces on-demand keyboard focus and allow them to take pop-up grabs.
- Certain actions like
focus-column-rightwill now move the focus from an on-demand layer-shell surface back to the main layout, allowing you to "escape" layer-shell with just a keyboard.
Combined, these improvements make the desktop icons components from LXQt and Xfce "just work" on niri. Thanks @stefonarch from LXQt for helping me test this and working on the LXQt niri session.
Xfce desktop icons with a context menu, terminal, and file manager on niri.Layer rules
At last, you can block out layer-shell notifications from screencasts just like windows:
// Block out mako notifications from screencasts. layer-rule { match namespace="^notifications$" block-out-from "screencast" }Layer rules work very similarly to window rules but with a different set of matchers and properties. See the wiki page for more details.
You can currently match by layer-shell namespace, and set
block-out-fromandopacity. To find out the namespace, useniri msg layerswhich lists all currently open layer-shell surfaces.Drag-and-drop focus switch
Drag-and-dropping something will now focus the output where you dropped it. This makes dragging a Firefox tab to a different output open it on that output.
https://github.com/user-attachments/assets/de062b76-d3dc-4025-bbb0-289b04209c87
Successful drag-and-dropping (when the target window accepts the drop) will also now activate the target window. This should reduce the confusion when you try to press some app shortcut right after a DnD and it ends up in the wrong (previous) window.
IPC and
niri msgimprovementsI've expanded the IPC interface and
niri msga bit in this release:- Added the process ID and whether the window is floating to
niri msg windows. - Added
niri msg layerswhich shows information about layer-shell surfaces. - Added the
set-window-width,switch-preset-window-width, andcenter-windowactions that mirror the corresponding-columnactions, but can accept their target window by--id. - Actions that can accept negative sizes like
niri msg set-column-width -10%no longer require a double-dash before the negative size. (I set the argument parser option that allows these arguments to start with a hyphen.)
Mouse click bindings
Thanks to @bbb651, you can now bind actions to mouse clicks:
binds { Mod+Ctrl+MouseLeft { close-window; } Mod+MouseMiddle { maximize-column; } }These mouse-click binds will operate on the window that was focused at the time of the click, not the window you're clicking.
Window swapping
@rustn00b added the
swap-window-left/rightactions that swap windows between two adjacent columns. They can be useful for a master/stack-like layout.https://github.com/user-attachments/assets/7c7db95f-805a-4b72-8c05-a9eec402259c
New window focusing
Thanks to @cmeissl, niri will now pass an xdg-activation token to the processes it spawns (with
spawnbinds orniri msg action spawn). As such, new windows that correctly activate themselves from the token will now get focused even when opening on a different workspace, or from a fullscreen window.When developing Wayland applications, you can verify that you use the xdg-activation token correctly with the new
strict-new-window-focus-policydebug flag. It disables all heuristic automatic focusing, so only those new windows that use xdg-activation will get focused.Lazy PipeWire initialization and resilience
Instead of at compositor startup, niri will now initialize PipeWire on-demand (at the first screencast attempt). This makes it possible to run PipeWire after niri startup (e.g. with
spawn-at-startup "pipewire").Also, niri now handles PipeWire restarts, and will reinitialize PipeWire next time it's needed. So restarting PipeWire should no longer make it impossible to screencast.
Other improvements in this release
- Added the
empty-workspace-above-firstlayout option (thanks @FluxTape). If set, niri will always keep an empty workspace at the very start, in addition to the empty workspace at the very end. - Added the
focus-window-previousaction (thanks @zeroeightysix). - Added the
focus-monitor-next/previous,move-window/column/workspace-to-monitor-next/previousactions (thanks @julianschuler). These use internal monitor ordering (that should be consistent across reboots) and loop around. - Added the
un/set-workspace-nameactions that can be useful in scripts to change workspace names at runtime (thanks @rustn00b). - Added the
open-focused true/falsewindow rule to force or prevent a window from getting focused on opening. - Added the
default-window-heightwindow rule that works similarly to the existingdefault-column-width, but for height. expel-window-from-columnnow always expels the bottom-most window in a column (rather than the focused one), and doesn't switch focus to the expelled column. This way, it becomes the exact opposite ofconsume-window-from-column. If you prefer the old behavior, useconsume-or-expel-window-right.- Changed
is-active-in-columnwindow rule matcher to matchtrueduring the initial window opening (thanks @TheZoq2). This makes sense as a new window always opens in a separate column, therefore it is active in its column right away. - Niri will now power on monitors when the session is unlocked (thanks @salman-farooq-sh). This helps with fingerprint and other unlocking methods that don't generate an input event.
- Fixed xdg-desktop-portal notifications by setting niri to use the gtk impl for the Notification portal.
- Fixed
border { off; }unable to override an earlierborder { on; }window rule. - Fixed a crash when connecting two monitors with identical make/model/serial (or one monitor through both HDMI and DP). Niri will detect this and automatically unname the second monitor. So the
disable-monitor-namesdebug flag is no longer necessary for such setups. - Fixed windows on other workspaces losing their Activated state (becoming visually inactive). This was a v0.1.10 regression. Windows keep their Activated state when switching workspaces to reduce unnecessary visual changes.
- Fixed a crash when calling
move-window-to-workspaceto an unfocused monitor. - Fixed several actions applying to the windows below while interactively moving a window.
- Fixed a potential crash when disconnecting a monitor while screencasting it.
- Fixed potentially misaligned borders on the interactively moved window when hot-reloading the output scale config in-flight.
- Fixed a wrong visual position when disconnecting a monitor while the primary monitor is in the middle of a workspace switch.
- Fixed initial window size requested by niri not honoring min/max size from window rules.
- Fixed potential crash when dropping an interactively moved window on a different monitor with a workspace switch in progress.
- Added Xrgb and Xbgr as preferred formats for the primary plane (previously only Argb and Abgr were preferred). I haven't seen it affect anything thus far, but maybe it fixes a bandwidth limitation on some setups.
- Refactored animation timing. This shouldn't have changed much visually (maybe some tiny issues were fixed), but it made the system much more robust, and let me write tests for many more scenarios. I wrote a wiki page about the new system if you're curious about the technical details.
- Added the
force-pipewire-invalid-modifierdebug flag that forces PipeWire screencasting to use the invalid modifier. - Added the
restrict-primary-scanout-to-matching-formatdebug flag. - Debug flags for
enable-overlay-planesanddisable-cursor-planenow apply immediately, without having to reconnect the monitor. - Updated Smithay:
- Fixed some clients crashing when opening nested pop-up menus (lxqt-panel app menus).
- Implemented presentation-time v2 protocol (well-defined refresh rate reporting for VRR).
- Fixed xdg-foreign assigning window parents in reverse, so now portal dialogs are correctly parented.
- Updated rustix:
- Fixed a crash at startup on M2 Apple and other AArch64 devices.
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!
Downloads
-
released this
2024-11-13 14:42:56 +07:00 | 1323 commits to main since this release📅 Originally published on GitHub: Wed, 13 Nov 2024 07:58:29 GMT
🏷️ Git tag created: Wed, 13 Nov 2024 07:42:56 GMTThis is a hotfix release for niri v0.1.10.
- Fixed scrolling not working when the
mouse {}ortouchpad {}section is omitted from the config file. - Made the mouse cursor show up on scroll which makes scrolling work when the cursor was hidden (thanks @r-vdp).
- Fixed a crash when holding Space in the screenshot UI.
- Bound touch-dragging with held Mod to interactive window move.
Downloads
- Fixed scrolling not working when the
-
released this
2024-11-09 22:15:23 +07:00 | 1328 commits to main since this release📅 Originally published on GitHub: Sat, 09 Nov 2024 16:06:10 GMT
🏷️ Git tag created: Sat, 09 Nov 2024 15:15:23 GMTNiri 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.
Interactive window moving
While not full-blown floating window support quite yet, this is an important step towards that. You can now move windows by dragging them by title bars, or anywhere while holding Mod.
https://github.com/user-attachments/assets/b0c36fc3-d4ca-459b-819b-fc7ee223a3d7
To prevent accidental layout changes, the windows rubber-band a little before you drag them out.
https://github.com/user-attachments/assets/7c50c95b-56fe-4d21-b659-ad8cd7f4fad7
Furthermore, I made both interactive moving and resizing work on a touchscreen.
https://github.com/user-attachments/assets/275f27fd-8c26-40a9-82c9-0cd975a96688
Thanks to @Pajn for implementing a fairly complete proof-of-concept of this feature!
Locked pointer location hint
@sodiboo implemented the pointer location hint request. Apps like Blender use it to tell the compositor the final location after a locked pointer movement so that the compositor can update its own pointer location to match it.
https://github.com/user-attachments/assets/76e92efb-7c93-4b1f-876e-fbc61912f7ea
Laptop lid and tablet mode switch bindings
Thanks to @cmeissl, you can now bind commands to laptop lid opening/closing and tablet mode switching. You can use this to automatically enable an on-screen keyboard when a convertible laptop enters tablet mode. See the switch events wiki page for more information and examples.
Additionally, I implemented disabling of the internal laptop monitor when closing the lid. So your workspaces will automatically move to the external screen. If for some reason this breaks for you, set the new
keep-laptop-panel-on-when-lid-is-closeddebug config flag.Pointer hiding
@yzy-1 implemented new cursor hiding options: hide when typing (on any key press), and hide after a set inactivity period. See the wiki page for more details.
cursor { hide-when-typing // Or, after a timeout: // hide-after-inactive-ms 1000 }To complement this, there are a few improvements to the hidden pointer behavior. The pointer will now show up on mouse button press, and on the contrary, it will stay hidden on programmatic and keyboard-triggered movement, like focusing a different monitor, or when using
warp-mouse-to-focus.Input configuration improvements
Thanks to @tazjin, @chillinbythetree and @elipp for:
- Adding a
trackballinput config section. - Adding a
scroll-buttonsetting to mice, touchpads, trackpoints, and trackballs. - Adding a
scroll-factorsetting to mice and touchpads that you can use to speed up or slow down scrolling.
See the input config wiki page for more information.
Other improvements in this release
- Tablet input no longer follows the monitor rotation: you need to rotate your graphics tablet together with your monitor. This makes convertible laptops work properly; this is also how input works on other desktop environments. Thanks @cmeissl.
- The GTK Access portal is now explicitly set in
niri-portals.conf, which makes it work. It is required for applications requesting PipeWire webcam and microphone access, such as the Firefox package on Fedora 41. Thanks @cmeissl. - The
niri-ipccrate is now published to crates.io. - Active workspace is now preserved across monitor disconnects and reconnects.
- Added a window
--idargument toniri msg action consume-or-expel-window-left/rightand to the IPC. - Added an explicit
power-on-monitorsaction that can be useful with certain hardware. Niri still automatically powers on monitors on any input event. - Added support for running niri as a dinit service: files in
resources/dinit/and corresponding code inniri-session(thanks @markK24). - Added a
disable-monitor-namesdebug config flag as a workaround for niri crashing when plugging in two monitors reporting the exact same make/model/serial. This issue is tracked in #734. - The focused window will now become visually inactive when a layer-shell app in front has keyboard focus.
- Fixed
focus-window-up-or-column-rightfocusing left instead of right. - Fixed an animation jump when expelling a narrower window from a column with uneven window widths.
- Fixed the logind power key inhibit file descriptor leaking into processes spawned by niri.
- Fixed window close view position restoration triggering for windows that didn't get focused upon opening.
- Fixed a crash when an output disappears immediately after connecting.
- Fixed used xdg-activation token memory leak.
- Fixed lock screen clients hanging until a monitor is enabled when no monitors are enabled.
- Updated Smithay:
- Fixed memory leak when locking the screen.
- Fixed occasional visual freezing of GTK and other apps.
- Fixed a regression that made it so increasing the output scale in niri v0.1.9 didn't propagate to some clients, keeping them blurry.
Downloads
- Adding a
-
released this
2024-09-14 17:59:43 +07:00 | 1436 commits to main since this release📅 Originally published on GitHub: Sat, 14 Sep 2024 11:44:06 GMT
🏷️ Git tag created: Sat, 14 Sep 2024 10:59:43 GMTNiri 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: niri now requires libdisplay-info.
New IPC functionality
In this release, I designed and implemented an event stream in niri's IPC which lets you continuously listen to compositor events like workspace or window changes. The event stream enables taskbar applications to make correct and efficient widgets for niri.
I implemented the niri modules for workspaces, focused window, and keyboard layout in Waybar, available in its fresh 0.11.0 release. Pull requests are open for yambar and ironbar thanks to their contributors.
https://github.com/user-attachments/assets/6fe9fc99-9246-45e3-a3b2-48ec79282cdd
IPC windows and workspaces now have unique IDs, and all individual window and workspace actions can address a specific window or workspace by its ID. On the command line, a new
niri msg windowscommand lists all windows with their IDs, and window commands accept an--id <ID>argument to target a specific window, for example:$ niri msg action fullscreen-window --id 2Also, there's a new
niri msg action focus-window --id <ID>action and a newniri msg keyboard-layoutscommand.I wrote some documentation on the programmatic access to the niri IPC socket. I also set up an online rustdoc for the niri-ipc crate where I documented every IPC type and request. Please refer there when working with the niri IPC.
Unfortunately, while adding ID arguments to IPC actions, I discovered a backward incompatibility trap in serde-json. The default enum representation—externally tagged—prevents you from changing a unit variant to a struct variant, because the representation gains an extra dictionary.
"FullscreenWindow"becomes{"FullscreenWindow":{}}, and the former does not parse with the new definition.I decided to make a JSON breaking change, converting all unit
Actionenum variants to struct variants (with or without fields). I doubt anyone used them directly through JSON since these actions could only address the focused window or column. All enum variants that already had fields are unchanged, and theniri msgCLI is also unaffected.With this breaking change out of the way, any further JSON additions should remain backward compatible, so that existing scripts and programs communicating with niri will keep working with new niri versions.
Height distribution changes
One common complaint about niri's layout was the ability to make a multi-window column not "add up" to the total height of the monitor. The behavior was also fairly unobvious: with two windows in a column, you resize one, and the other resizes along as expected. Then, you resize the other, but the first window doesn't react. It felt like a bug.
Last time there was a design problem (unwanted scrolling with focus-follows-mouse), we quickly found a solution by brainstorming in a Discussion. So, I made a big write-up about window heights in https://github.com/YaLTeR/niri/discussions/593. While there hasn't been much discussion, the act of laying out in writing all considerations and constraints had spawned a potential solution in my mind, which turned out to work quite well.
In this release, I reworked the window height distribution to do the expected thing in more cases. A column of two or more windows will always try to match the monitor height, as long as the minimum window sizes allow that. Resizing one window will resize all other windows in a column proportionally. The window that you resized last retains its height just like before, which lets you size one window in a column exactly to fit something, unaffected by adding more windows into the column, or moving it across monitors.
Keep in mind that a single-window column can still be resized arbitrarily, including shorter or taller than the monitor. Until floating windows are implemented, this is necessary for some uses that require exact-sized windows.
https://github.com/user-attachments/assets/d4f35887-a275-4531-a223-d518b6ebee8b
Additionally, I found and fixed a small issue where windows in a column would occasionally "snap" to a smaller size when resizing.
Preset window heights
@TheAngusMcFire implemented a
preset-window-heightslayout option and a correspondingswitch-preset-window-heightbind, which work like the existing column width presets.By default, it's bound to ModShiftR, which is consistent with Shift making resize binds affect the height rather than the width. The default bind to resetting the window height therefore moved to ModCtrlR. (None of this affects you if you already have a niri config; you'll need to add any new binds manually.)
Output names
You might be familiar with this sight:
$ niri msg focused-output Output "Unknown Unknown Unknown" (DP-1) ...Thanks to @cmeissl finishing the libdisplay-info bindings, this sight is no more.
$ niri msg focused-output Output "Acer Technologies XV320QU LV 420615FCD4200" (DP-1) ...Following this, all throughout niri I implemented the ability to address outputs by name. This includes config
output,map-to-output,open-on-output;niri msg output; wlr-output-management tools (wdisplays, kanshi); and xdg-desktop-portal-gnome screencasting where the screen selector will now show the monitor model and screencast session restore will remember the output name rather than the connector.The recommended way to configure everything output-related is now by name (as shown in
niri msg outputs). This way, configuration does not depend on the connector name that can be non-deterministic with multiple GPUs or when using thunderbolt docks.// Previously: output "DP-1" { output "Dell Inc. Dell S2716DG #ASOwvAqQj0Dd" { mode "2560x1440@143.998" // ... }I was also finally able to change the monitor sorting order to use the output name rather than the connector name, once again making it more deterministic. Note that this may swap your monitor positions if you were using multiple monitors and haven't manually configured them.
Transactional updates
One of Wayland's premises is that "every frame is perfect"
except the first one. The compositor is in full control of the display, and window state changes are atomic and correlate to specific compositor requests.This allows the compositor to synchronize updates for multiple windows: render the old state until all windows update, then switch to the new state all at once, with no broken frame in between.
However, possible doesn't mean easy, and different kinds of transactional updates need different approaches in the code. For this release, I implemented two relatively common cases.
Resizing
Thanks to the scrollable tiling nature, niri doesn't need to synchronize resizes among all windows on a workspace. However, windows in one column must still resize in unison: they must have the same width, and their heights must add up exactly to the monitor height.
https://github.com/user-attachments/assets/8e1be961-5b72-4fb6-a769-a36cdc830313
Closing
Closing a window resizes all other windows in the column to take up the freed space. Normally, resize and close animations hide this, but if you disable animations, the flicker becomes very noticeable. The closing transaction fixes this: niri waits until other windows have resized before hiding the closed window.
https://github.com/user-attachments/assets/97c87ce9-3cc8-415d-92c1-4cfeb2260173
On-demand VRR
Thanks to @my4ng, we now have on-demand variable refresh rate as a window rule.
Some monitors flicker at the lowest VRR refresh rate, some drivers have VRR bugs, and some clients don't handle VRR too well. Now, niri can enable VRR only when a specific window is on screen (for example, a video player, or a game), thereby avoiding most of those issues.
Configure your output with
on-demand=true:output "Acer Technologies XV320QU LV 420615FCD4200" { // ... // This will keep VRR off unless enabled by a window rule. variable-refresh-rate on-demand=true }Then, add
variable-refresh-rate truewindow rules as necessary:// Enable VRR when mpv is on screen. window-rule { match app-id="^mpv$" variable-refresh-rate true }NVIDIA flickering fix
There was a problem with NVIDIA flickering on niri, which the user could fix by enabling the
wait-for-frame-completion-before-queueingdebug flag. Turns out, this was only necessary because ages ago I forgot to add a check in the code. 🤦Starting from this release, you should no longer need to set that debug flag, and NVIDIA GPUs should no longer flicker on niri out of the box (fingers crossed).
Small UX improvements
The horizontal touchpad swipe gesture will no longer go past the first or last column on the workspace.
https://github.com/user-attachments/assets/a92349e6-bc45-40b1-89d6-57f2e7fd068c
And focus-follows-mouse will no longer "catch" windows on workspaces as you're switching away from them, which is especially important when using the new workspaces bar modules.
https://github.com/user-attachments/assets/ca6e4b17-13aa-476d-ada8-313c4a5f5c7c
Other improvements in this release
- Niri will now attempt to read the config file from
/etc/niri/config.kdlwhen~/.config/niri/config.kdlis missing. - Added an
always-center-single-columnlayout option that makes a single column on a workspace always centered (thanks @elkowar). - Switching a workspace by index and back-and-forth is now animated the same way as switching a workspace up or down.
- On-demand layer-shell surfaces will now automatically get focus when they appear, which works better with some application launchers like
lxqt-runner. - Added a
NIRI_DISABLE_SYSTEM_MANAGER_NOTIFYenvironment variable; when set to1it will suppress niri's own notification to systemd orNOTIFY_FD. This is useful for some custom systemd setups like uwsm. - Niri will try harder to light up monitors, which may help it get a better resolution on some multi-monitor setups on some hardware.
- Fixed xdg-desktop-portal-gnome unable to open a file chooser from Xwayland windows.
- Fixed crashes when a resume from suspend or a monitor power-on sends bogus vblank events on some hardware.
- Fixed
niri msg action do-screen-transitionrendering wrong across monitor scale and transform changes. - Fixed
move-column-to-workspaceto a different output only moving one window and not the whole column. - Fixed
set-window-height N%actually trying to use 100×N% height. - Fixed niri not informing layer-shell surfaces of changes to preferred scale and transform.
- Portal screencasts now use damage to reduce unnecessary rendering.
- Improved portal screencast frame timing to fix an occasional stuck wrong frame and niri sometimes sending frames too often.
- Fixed unsync subsurfaces of layer-shell, cursors, and DnD icons not causing output redraws when they should. (Does anything even use those currently?)
- Exclusive layer-shell surfaces are now preferred for keyboard focus to on-demand layer-shell surfaces on the same layer.
- Updated Smithay:
- Fixed layer-shell pop-up menu cursor input being slightly offset.
- Fixed inverted scroll direction when running the compositor as a nested window.
Downloads
- Niri will now attempt to read the config file from
mirror of
https://github.com/niri-wm/niri.git
synced 2026-06-19 02:01:36 +07:00