Screen Sharing
September 20, 2026 · View on GitHub
Otto shares your screen through the standard XDG Desktop Portal plus PipeWire, the same mechanism GNOME and KDE use. Browsers, video-call apps, OBS and Flatpak'd applications all work through it without knowing anything about Otto.
Portal setup
Screen sharing needs three things running: xdg-desktop-portal (the frontend),
xdg-desktop-portal-otto (Otto's backend), and PipeWire.
1. Install the portal frontend
sudo pacman -S xdg-desktop-portal # Arch
sudo dnf install xdg-desktop-portal # Fedora
sudo apt install xdg-desktop-portal # Debian/Ubuntu
Otto's own packages install the backend, its D-Bus service file and
/usr/share/xdg-desktop-portal/portals/otto.portal.
2. Tell the frontend to use Otto
Create ~/.config/xdg-desktop-portal/portals.conf:
[preferred]
default=gtk
org.freedesktop.impl.portal.ScreenCast=otto
org.freedesktop.impl.portal.Settings=otto
This routes screen capture and the appearance settings (light/dark) to Otto, and
leaves everything else (file chooser, printing, notifications) to the GTK
backend. A copy of this file ships as
/usr/share/doc/otto/portals.conf.example.
org.freedesktop.impl.portal.Settings is what makes applications follow your
theme_scheme; see Theming.
3. Restart the portal
systemctl --user restart xdg-desktop-portal
Verify
gdbus call --session \
--dest org.freedesktop.portal.Desktop \
--object-path /org/freedesktop/portal/desktop \
--method org.freedesktop.portal.ScreenCast.CreateSession \
"{'handle_token':<'t1'>,'session_handle_token':<'s1'>}"
A successful call returns a request object path immediately.
Sharing your screen
In a browser, click "Share screen"; in a video call, pick screen sharing. The portal takes over from there:
- A permission and source dialog appears in the dynamic island: who is asking, and a list of what you can share.
- You pick a source and grant, or cancel.
- On grant, Otto starts a PipeWire stream of that source and hands the node to the application.
otto-islands is what renders that dialog with icons. If it is not running,
Otto falls back to another desktop's standard Access dialog (same choices,
without icons); if that is unreachable either, the request is denied rather
than left hanging — a screenshare that cannot ask fails closed.
Choosing a source
The dialog lists, in order:
- every monitor, by connector name, including virtual outputs, so
virtual-1is shareable like any other screen; - every open window, by its title (or its app id if it has none), with its application icon.
The first entry is preselected.
Sharing one window
Pick a window in that list and Otto captures that window alone: what is shared is the window's own content, so windows stacked over it do not leak into the stream, and the size follows the window rather than the screen. A window Otto decorates shows a sharing indicator in its title bar while it is being captured.
Only the window's identity is remembered. If it moves, is resized or changes monitor mid-share, the stream follows it.
Being asked only once
Apps that ask for persistence get a restore token. The next time the same app starts a session it hands the token back and Otto re-uses the source that was already approved instead of prompting again. This matters in practice for Chrome. It creates a session twice, once for the preview inside its own picker and once for the real capture, and would otherwise ask you twice for the same share.
A token written by another desktop's portal is rejected, and a token naming a source that no longer exists (an unplugged monitor, a closed window) falls back to asking.
When no dialog is reachable
If neither dialog backend can be reached, Otto shares one monitor without asking. Which one comes from a file:
echo "HDMI-A-1" > ~/.config/otto/screencast-output
It is read fresh on every share request, so you can change it between sessions
without restarting anything. Use a name from otto --probe, or a virtual output
name like virtual-1. If the name does not match any available output, Otto
logs a warning and falls back to the first one.
Cursor
The portal's three cursor modes are supported: hidden, embedded (drawn into the video) and metadata (sent alongside, for the receiver to draw). The application picks; there is nothing to configure.
Recording with OBS
OBS uses the portal like any other client:
- Add a source → Screen Capture (PipeWire).
- Grant the permission dialog.
For a dedicated recording surface that is not one of your real monitors, set up a virtual output and point OBS at its PipeWire node directly:
[[virtual_outputs]]
name = "virtual-1"
resolution = { width = 1920, height = 1080 }
refresh_hz = 60.0
position = { x = 3840, y = 0 }
interactive = false
Otto logs the node id at startup:
Virtual output 'virtual-1' started (PipeWire node 42)
gst-launch-1.0 pipewiresrc path=42 ! videoconvert ! autovideosink
A virtual output is a real workspace you can drag windows onto: a "recording stage" separate from what you are looking at.
Screenshots
Otto implements zwlr-screencopy-v1, so the usual wlroots screenshot tools
work:
grim ~/Pictures/shot.png # whole screen
grim -o eDP-1 ~/Pictures/shot.png # one monitor
grim -g "$(slurp)" ~/Pictures/shot.png # a region you drag out
Bind one to a key:
[keyboard_shortcuts]
"Print" = { run = { cmd = "sh", args = ["-c", "grim ~/Pictures/$(date +%s).png"] } }
Applications that ask through the desktop portal are answered too, whether GTK
and Qt screenshot tools or sandboxed apps that cannot talk to Wayland directly.
Otto captures the whole screen to ~/Pictures/Screenshots/ and returns the file.
That path runs grim, so install it if you want portal screenshots to work.
There is no built-in screenshot UI for selecting a region or a window interactively, and per-window capture through screencopy is not implemented: screencopy captures whole outputs or rectangles of them. Sharing a single window is available through the screen-sharing portal; see Sharing one window.
AirPlay
You can send an Otto output to an Apple TV using doubletake, which captures through the very portal you just configured:
- Set up a non-interactive virtual output (AirPlay mirroring has no input channel, so it is view-only).
- Run doubletake and pick your receiver.
- Choose that virtual output in Otto's share dialog; it is listed with the physical monitors.
Otto does not implement the AirPlay protocol itself. The sender side requires Apple's FairPlay handshake, which no clean-room implementation exists for.
Receiving from an Apple device (an iPhone screen appearing as a window in Otto) is not implemented either; UxPlay works as an independent application.
Remote control
Screen sharing is view-only. To control Otto remotely, use the RDP bridge; see Remote Desktop.
Screen sharing and power
A lid close does not suspend the machine while a screenshare session is actively streaming, so a remote viewer is not cut off. See Power Management.
Troubleshooting
The screen-share picker in my browser is empty, or sharing fails silently. Check the backend is running and reachable:
busctl --user status org.freedesktop.impl.portal.desktop.otto
journalctl --user -u xdg-desktop-portal-otto -f
The backend logs every method call, the compositor interaction and the PipeWire
node ids. For more detail, restart it with RUST_LOG=debug.
The permission dialog never appears. otto-islands is not running. Start
it ([[exec_once]]) and try again.
The wrong source is shared. Pick it in the dialog; see Choosing a source. If no dialog appeared at all, Otto took the fallback path; see When no dialog is reachable.
The video is mangled: torn, sheared or wrongly coloured. This is a dmabuf modifier negotiation problem between Otto and the consumer. Capture a log with:
GST_DEBUG=3 <your app>
and open an issue with it.
Sharing works in Firefox but not in a Flatpak app. Flatpak apps use the
portal from inside the sandbox; make sure xdg-desktop-portal and the Otto
backend are both on the session bus the sandbox sees.
Not yet implemented
- A graphical source picker with window thumbnails and region selection; the consent dialog lists sources by name
- Region capture (a rectangle of an output) through the portal
- Multiple simultaneous outputs in one session (the first is used)
- Audio capture