Screen Sharing with niri, Including Specific Regions with OBS

niri has some of the best features I've seen for seamless screen sharing such as full monitor, specific window or dynamic casting.
Jump to the demo video if you want to see how all of these features work.
As someone who came from Windows, I really like the Linux model of XDG desktop portals / PipeWire to handle screen sharing. You have a system level abstraction which allows all apps to initiate screen sharing the same way.
It doesn’t matter if you’re using Google Meet, Zoom, Slack, Discord, Teams, OBS or something else. You’ll be able to share in the same way.
# Pre-reqs
To get screen sharing to work with niri, the docs say it best:
In order to use it, you need a working D-Bus session, pipewire,
xdg-desktop-portal-gnome, and running niri as a session (i.e. through niri-session or from a display manager). On widely used distros this should all “just work”.
I’m using Arch and it “just worked” once I installed
xdg-desktop-portal-gnome. The rest are pretty standard packages and services
you’ll likely have already.
One thing worth mentioning is if you have an NVIDIA GPU and are using the
experimental egl-wayland2 library at https://github.com/NVIDIA/egl-wayland2
then you’ll want to configure this. I didn’t have to do it with the default
egl-wayland library.
$ cat ~/.config/xdg-desktop-portal/portals.conf
[preferred]
default=gnome;gtk;
Since niri uses xdg-desktop-portal-gtk, without defining the above the OS
screen share menu never popped up letting me pick a screencast source in any
app. It said permission denied which was a little misleading.
# Ways to Share Your Screen
These 3 options are exposed to you in the same way for all apps at an OS level:
- Full output / monitor / desktop
- If you have multiple monitors you can pick which one to share
- Typically available with most compositors
- Specific window
- Only the window you select will be shared
- Typically available with most compositors
- Dynamic cast
- Similar to specific window but instead of having to fiddle with an OS menu to switch windows you can have niri cast the focused window on demand
- Something I’ve only seen in niri
There’s also one more way but it typically requires the screen sharing app to have custom support for it. Normally this isn’t exposed in the same way as the other 3. It’s presented using whatever UI controls that app provides:
- Specific region of a monitor
- Define a portion of your monitor to capture
- Typically available in OBS and some screen sharing apps
Now that we know how a screen can be shared, let’s go over a few use cases and along the way we’ll see how niri offers really nice features to make it a great experience.
# Screencast Strategies
I’ve recorded hundreds of pre-recorded videos, given live demos to clients and have done live remote presentations. Here’s how different ways of sharing my screen helped me achieve certain outcomes I had at the time.
Entire Monitor / Full Desktop
All around good choice for showing your whole desktop experience since it captures everything on a specific monitor. If you want someone to experience what you’re experiencing this is the way to go.
If you have a high resolution display (4k, etc.), niri makes it easy to scale your output on demand so you can quickly switch to 1.5x, 2x, etc.. This can make things easier to see for folks on lower resolution displays or on recorded videos without having to micro-manage zoom levels in each individual app.
The way niri adjusts the scale isn’t switching the resolution of your monitor. It’s a near instant no-flicker scale. In my dotfiles I wrote a small script to switch scales on the fly.
Specific Window
Sometimes you don’t want to show everything because it’s too distracting or maybe you have notes off to the side that you don’t want shared. For privacy reasons you may choose to only share 1 specific window as well.
You can achieve this by casting a specific window or using niri’s dynamic cast. The outcome is the same in the sense that 1 specific window will be shared at a time.
Dynamic cast is a niri feature that lets you cast a specific window without needing to mess around with selecting a new window in the app that’s screen sharing.
Instead you can pick a window on the fly or assign to a key bind to cast the focused window. Here’s what that bind looks like which I very occasionally use:
Mod+E hotkey-overlay-title="Cast focused window" {
spawn-sh "niri msg action set-dynamic-cast-window --id $(niri msg -j focused-window | jq .id)";
}
To be honest I rarely use this. I either capture my whole screen or a region of my screen but I know a lot of folks capture 1 specific window. I often see this at work where co-workers are sharing just a browser or code editor in Slack or Teams.
Dynamic cast makes it so fast to switch between specific windows.
One cool thing niri has related to this is if you’re capturing a window you
can apply a custom window-rule so you as the presenter know it’s being
casted. Viewers won’t see this because window styles aren’t casted, only the
contents of the window.
For example, here’s a snippet from my dotfiles that adds a pink / red gradient focus ring around the currently casted window:
window-rule {
match is-window-cast-target=true
focus-ring {
active-gradient from="#f38ba8ee" to="#cc241dee" angle=45
}
}
This only matches due to is-window-cast-target=true. That matches through
picking a specific window or dynamic cast. It replaces the default focus ring’s
color.
Specific Region
This is handy if you have a high resolution monitor but only want to capture a sub-section of it. For example maybe you have a 1440p or 4k monitor and you want to capture a 1080p rectangle of your screen. Anything that ends up inside of that region becomes shared.
This is much different than sharing 1 window, think of this more like cropping your display.
When I was on Windows, most of my videos were recorded this way. It let me keep notes off to the side but flip between a terminal and web browser on video. I’d keep my 4k display at 1:1 native scaling and then crank the font size in a terminal or zoom into a browser.
On Windows I used a program called Sizer to quickly resize and position windows to fit into the region. It took a few seconds to get pixel perfect sizing.
On Linux with niri it’s even better. We have a few options, 1 of which I haven’t seen in any other environment before niri.
If you want to replicate the “size and position a window into a region” workflow you can do that with niri. niri’s IPC capabilities let you send commands to niri to do a number of things, such as resize and position a floating window of your choosing.
You can even combine this with niri’s tab groups. You’d put a few windows into a tab group, position it where you want it and then flip between the tabs. If you know you’ll only be screen casting a few things (code editor + browser) you can position it ahead of time.
But wait, there’s more!
You can use struts to create a fenced in area of your monitor. All tiled windows will automatically be inside of there. The best part is you can float windows out of that fence to escape the strut. This gives you the best of both worlds.
You can have whatever you want open outside of that region and you don’t have to fiddle with resizing or positioning windows when you normally open and interact with windows.
When I want this behavior I just uncomment this in my niri config. Keep in mind the values are optimized for a 4k display capturing a 1080p window:
output "DP-1" {
// layout { struts { left 949; right 949; top 6; bottom 1031; }; }
}
The recording zone ends up being near the top of the desktop and centered. I like this because it gives me plenty of room to have notes on either side and it keeps the main content close to my webcam so it looks like my eyes are facing center.
On the OBS side, I created a source that captures the full display. Then I
right clicked and applied a transform that crops it at left: 958 | right: 958 | top: 38 | bottom: 1034.
With the way I have my gaps set up with niri, OBS ends up capturing the contents of the region without drawing the outer focus ring from niri.
Long story short, niri gives you a lot of options here!
# Block Out Windows
You can block out specific windows from being picked up in the recording. They will become solid black windows. On your end (the presenter), the window looks normal.
This could be handy to avoid sharing your password manager or something else. This is especially useful for live streams or demos where you can’t go back and edit a video afterwards to redact it manually.
It’s exposed through a window-rule, here’s an example from my dotfiles:
window-rule {
match app-id=r#"^KeePassXC$"#
block-out-from "screencast"
}
There’s feature requests in place to enhance this feature such as being able to toggle blocking out a window with a key bind or configuring things like a custom focus ring or anything you’d like so a presenter can see what’s blocked (similar to the red focus ring for what window is actively casted).
# Screen Mirroring
You can take an entire monitor’s output and mirror it into a regular window. If you have multiple monitors, you can put this window on a different monitor and then share that.
This isn’t something I’ve done yet but it’s described in niri’s docs. It takes advantage of ml-mirror. niri is used to hook up the key bind and tie it into the focused monitor:
binds {
Mod+Ctrl+E repeat=false { spawn-sh "wl-mirror $(niri msg -j focused-output | jq -r .name)"; }
}
The video below shows how to do everything mentioned in this post except mirroring. niri is a gift that keeps on giving.
# Demo Video
Timestamps
- 2:19 – Casting 2nd monitor
- 2:52 – Casting a specific window
- 4:08 – niri’s dynamic casting
- 5:12 – Recording a region of the monitor with OBS
- 6:59 – Configuring niri struts to fence off a region
- 7:57 – Tab groups help to contain windows
- 8:32 – Blocking out sensitive windows
How are using niri’s features for screen sharing? Let me know below.