contents

Screen sharing#

carrot is its own xdg-desktop-portal ScreenCast backend (interface version 4), and the video reaches PipeWire through carrot's built-in pure-Rust client. If an app speaks the portal, it can cast; OBS and browser video calls both go through it.

What you can cast#

Any monitor, a single toplevel (X11 or Wayland), or an entire workspace as one stream. The workspace option is a carrot extra, reachable through the picker.

Sources keep streaming when hidden. A window on another workspace, or a workspace that is not on any monitor, composes off-screen for the cast instead of freezing. A workspace cast keeps showing the settled layout while a switch animation plays on glass.

Keeping windows private#

A window rule with no-capture blacks that window out of every capture path: portal casts, screenshots, and recordings all see an opaque black box in its place while your own screen shows it normally. The blackout covers the close-animation replay too, so pixels never leave the compositor.

When an app asks to cast, one of three things happens, in order:

  1. A restore token from an earlier session skips consent entirely. The portal's permission store keeps tokens on disk, and carrot re-finds the granted source across its own restarts by the app and window it approved, so the approval outlives both sides.
  2. Your picker runs, if you configured one (see below).
  3. With no picker, the next left click chooses whatever the pointer is on. Escape or any other button cancels.

Bring your own picker#

The screencast { picker } key in config names a command that carrot runs for consent, so your launcher of choice can be the dialog. The contract:

  • Candidates arrive as ndjson on stdin, one JSON object per line. Outputs look like {"kind":"output","id":"o:DP-3","name":"DP-3","width":2560,"height":1440,"x":0,"y":0}; windows and workspaces carry w: and ws: ids.
  • The picker prints one chosen id on stdout: o:DP-3, w:42, or ws:2.
  • Empty output or EOF cancels the request.
  • A picker that never answers is killed after 120 seconds, whole process group included, and the request cancels cleanly.

A fuzzel picker is a few lines of shell: read the lines, format labels, print the chosen id.

Testing it#

carrot portal-probe runs the portal backend standalone, and carrot pw-probe checks the PipeWire connection. Every install channel registers the portal files (carrot install writes them), so xdg-desktop-portal routes ScreenCast to carrot automatically.