emersion / xdg-desktop-portal-wlr

xdg-desktop-portal backend for wlroots
MIT License
580 stars 54 forks source link

Allow sharing of individual windows. #107

Open selflein opened 3 years ago

selflein commented 3 years ago

Adding the option to share individual windows was discussed in different issues/PRs but there is no corresponding open issue right now.

selflein commented 3 years ago

Some discussion can be found here

danshick commented 3 years ago

True, thanks. Also worth linking to the corresponding wlr-protocols issue, which I would consider a prerequisite/blocker for this feature.

timsofteng commented 2 years ago

Hello. What is a current state of this feature?

hugopeixoto commented 2 years ago

Here's the link to the same issue mentioned by danshick in the new forge: https://gitlab.freedesktop.org/wlroots/wlr-protocols/-/issues/93

tm512 commented 2 years ago

I'd really like to see a feature like this implemented. My streaming setup on X11 involves having a game in a window on one side of the screen with stuff like terminals and twitch's dashboard on the other side of the screen. Since I'm stuck with a single-monitor setup, I don't have the option of just running the game fullscreen on one monitor and using fullscreen capture, with everything else on the other monitor.

It sounds like some other Wayland compositors like GNOME already provide the ability for single-window capture? Maybe I'm misunderstanding, but if that's the case then what are they doing that makes that possible that wlroots doesn't do?

columbarius commented 2 years ago

GNOME/mutter uses a completely different base. There is currently no way to export a toplevel buffer in wlroots. The current roadmap (and take this with a grain of salt is):

  1. Finish ext-screencopy
  2. Create a way to export handles for toplevels (windows) from the compositor
  3. Expand ext-screencopy to accept these handles
  4. Expand the chooser protocol to account for them (#169)
  5. Add this together to share window buffers

For the meantime there is #156 to share a region of the screen.

tm512 commented 2 years ago

For the meantime there is https://github.com/emersion/xdg-desktop-portal-wlr/pull/156 to share a region of the screen.

Unfortunately specifying the cropping region ahead of time isn't really practical without a way to precisely move and resize a window into that region (like what xdotool can easily do on X11). While things are limited to capturing entire outputs, would it be possible to run the game to be captured inside of a nested wayland compositor running in a window? I'm not sure if its buffer would then be available for capture via xdpw. The ability to set the resolution of the nested compositor would also be important in that case.

If that's not feasible, I've been thinking of just hacking whatever compositor I settle on to keep a tmpfile with a full list of windows and their size/position, then pass those into slurp to interactively select which window to capture, using that with wf-recorder or ffmpeg w/ kmsgrab.

wallabra commented 1 year ago

If that's not feasible, I've been thinking of just hacking whatever compositor I settle on to keep a tmpfile with a full list of windows and their size/position, then pass those into slurp to interactively select which window to capture, using that with wf-recorder or ffmpeg w/ kmsgrab.

@tm512 Another concern is that a window is not always in the foreground, like when using workspaces or a tabbed container in Sway.

spagootie commented 1 year ago

This feature would be great to have, because it's on virtually every platform other than wlroots compositors.

sfrique commented 1 year ago

It's bad, but it's able to share only "a few things" if you need. For me it solved the biggest problem, not wanting to show my full output.

it seems to work just "fine", but did not test it enough.

You will need to run a nested Wayland session.. i tested with sway what i did to make it work:

  1. Created a clean sway config (no auto start bar etc...) run sway -c ..../sway-nested
  2. Created a pass-through mode on my main sway (to be able to run any command on my nested sway)
  3. Created a chromium/firefox profile (just so i don't have to close my current one at the "main" sway)
  4. Killed all xdg portal (if i did not do this, it would share the whole output, even when at nested session)
  5. Share the output, since this is nested, it only shared the nested session, that on my test was a floating windows of sway with whenever opened there

as I said, it's ugly.. I just did some initial testing, did not check audio etc etc yet. I was just trying to solve a problem I now have, since i am moving to a single big monitor, i wasn't able to share my second output and keep doing stuff while sharing the screen, but this should solve this.. since nothing would be running at nested, only what i actually want.

I don't know if nested session work with all the apps and etc.. but if it's only one instance of it, and it's running in the nested, should be fine.

There is a lot of improvement and tests that can be made, i just wanted to share something i just got working.

Hope that helps people =]

wallabra commented 1 year ago

You see, this nesting should naturally derive from "surfaces" as a first-class abstraction of rendered regions, indifferent to whether it's a desktop, a window, or whatever else. A tree of surfaces should be enumerable and visible from anywhere in it, globally but probably with some sort of access control scheme. The issue is this was not considered initially in the design of wlroots, which will haunt it for a very long time because of a little something called technical debt.

cmprmsd commented 1 year ago

I wonder why nested Sway is a functioning workaround but it's not possible to share individual windows. For now I just share via xwayland, which works okay and is more easy to use than having nested WMs.

ckcr4lyf commented 1 year ago

For now I just share via xwayland

@cmprmsd can you expand on this - do you mean the specific window you want to share is what you launch via xwayland?

cmprmsd commented 1 year ago

Sure thing. When you explicitly launch two applications like chromium and e.g. VMware workstation with xwayland, than both will use "the old" methods for sharing and therefore see each other.

In Chromium you'll notice that for example in Teams you will see all xwayland windows. The monitor share tabs will show black screens. But if you switch to single window, than you can select windows started under xwayland.

Hope this helps for the time being.

WhyNotHugo commented 1 year ago

Following up on the roadmap above:

  • Create a way to export handles for toplevels (windows) from the compositor

This will be covered by ext-foreign-toplevel-info.

cmprmsd commented 1 year ago

My workaround does not work any more since some weeks. I don't know why but some component been updated and now only the "Wayland-dialog" is visible even for Chromium or Firefox which have been started with xWayland instead of Wayland. :/

I looked at the pipewire settings and some other flags but nothing did work. :cry:

clushie commented 1 year ago

Thanks @sfrique for your idea. I'm quite happy with that solution. Changing the nested sway window size will automatically resize the screen-sharing appropriately. If you are sharing a window and move to another space, the content that is getting shared won't update while it's not visible for you. Really feels like sharing a window, it's quite perfect for me. Hope this helps others who want to share a window/part of their screen.

Here is how I've accomplished everything:

A script to allow using the sway shortcuts in either the main session or the sub-session if that one is selected:

~/bin/swaymsg-nested

#!/usr/bin/env bash
set -ueo pipefail

if pid="$(swaymsg -t get_tree | jq -e '.. | select(.type?) | select(.focused==true) | select(.app_id=="wlroots").pid')"; then
 export SWAYSOCK="/run/user/${UID}/sway-ipc.${UID}.${pid}.sock"
fi

swaymsg "${@}"

I just check if the window is wlroots which is the window of the nested session, if that's the case I already have the PID of that session an can set it as a socket and then pass it to swaymsg in my~/.config/sway/config I simply did a find/replace with vim :%s/\(^bindsym \S*\) \(.*\)/\1 exec \~\/bin\/swaymsg-nested \2/g and then had to fix few places where I was doing bindsym --locked and similar.

Only difficulty is that I forward everything to that window, which means I can't control it like other windows 😆 not sure what to do about that yet.

For starting the nested session I just have to make sure to later restore. I'm doing that in a script

~/bin/sway-nested

#!/usr/bin/env bash
set -ueo pipefail

sway -c ~/.config/sway/nested || true

dbus-update-activation-environment --systemd WAYLAND_DISPLAY SWAYSOCK
systemctl --user restart xdg-desktop-portal.service xdg-desktop-portal-wlr.service

and in the .config/sway/nested I simply duplicated most of my main swaywm config and removed any exec or bindsym configs) and made sure to include:

exec dbus-update-activation-environment --systemd WAYLAND_DISPLAY SWAYSOCK
exec systemctl --user restart xdg-desktop-portal.service
exec systemctl --user restart xdg-desktop-portal-wlr.service

Now I can simply start the sway nested session, share that nested window via Firefox/Chromium from the main (since the connection happens via dbus which is shared for both instances). If I wanted to share a whole browser, I'd have to close it and then re-open it inside the nested session.

jalvesaq commented 1 year ago

A workaround for this is to create a virtual output (HEADLESS-1), move the window to it, and share the virtual output. My configuration for this is:

~/.config/xdg-desktop-portal-wlr/config:

[screencast]
chooser_type=dmenu
chooser_cmd=swaymsg -t get_outputs | jq '.[] | .name' | sed 's/"//g' | wofi -d
max_fps=30

~/.config/sway/config:

# Do the following command in a terminal emulator when you need the virtual output:
# swaymsg create_output

output HEADLESS-1 resolution 1280x720 position 0,1080
output HEADLESS-1 bg "#220900" solid_color
workspace 0 output HEADLESS-1
bindsym $mod+0 workspace number 0
bindsym $mod+Shift+0 move container to workspace number 0

The command swaymsg create_output creates the virtual output HEADLESS-1 the first time that it is run. If you repeat the command, it will create the virtual outputs HEADLESS-2, HEADLESS-3, and so on...

By default, when I try to share an output, the cursor turns a cross (slurp -o is running) and I have to click anywhere in the output to choose it. I could move the cursor to the virtual output, but I prefer to see the list of available outputs. That's why I configured xdg-desktop-portal-wlr to use wofi instead of slurp.

clushie commented 1 year ago

@jalvesaq Oh cool, so the idea is to overlay a headless output (what you are doing by picking the right pixel position on creation) with a regular output and then to share that headless output? I just tested it quickly by using wdisplay to move the headless output on the same screen as I'm currently at, unfortunately only shows up if I set it to floating and place it within the positioning of the headless output. How do you solve this? PS: You can do jq -r to remove the " symbols.

jalvesaq commented 1 year ago

Thanks for the tip on how to remove the " symbols!

I can use wl-mirror to visualize the headless output:

wl-mirror HEADLESS-1

But I can also see it in Google Meet (if I'm sharing the output) or preview it in OBS as a "Screen Capture (PipeWire)" source.

trinitronx commented 7 months ago

I can use wl-mirror to visualize the headless output

Very nice! I'll have to try this method out.

But I can also see it in Google Meet (if I'm sharing the output) or preview it in OBS as a "Screen Capture (PipeWire)" source.

Another way that I've found is to use wayvnc + VNC client to view the headless output:

wayvnc --gpu --performance --output=HEADLESS-1
# Then connect to 127.0.0.1:5900 with VNC client (e.g. Remmina)

Keyboard capture becomes somewhat of an obstacle with this method, but usually I can switch back out of the HEADLESS-* output VNC window using the Sway workspace switching keyboard shortcuts. Also, unfortunately the VNC "Host key" doesn't work very well (default in Remmina is Ctrl_R / right Ctrl).

paolomainardi commented 7 months ago

I am curious to know if other portals have found a solution to this problem?

paolomainardi commented 7 months ago

Hyprland supports this, so at least their portal started as a fork of this one: https://github.com/emersion/xdg-desktop-portal-wlr/pull/253. This should be the code used to implement this feature, I am not sure why they didn't try to merge back here.

WhyNotHugo commented 7 months ago

@paolomainardi Hyperland uses the hyprland-toplevel-export-v1 protocol, which wlroots does not support.


Based on the roadmap above, the current state is:

Finish ext-screencopy

Still in progress, lots of unsolved discussion there.

Create a way to export handles for toplevels (windows) from the compositor

Done. Implemented via the ext-foreign-toplevel-list protocol.

Expand ext-screencopy to accept these handles

This could be implemented experimentally in zwlr_screencopy_manager_v1. Although I'd ask the wlroots devs if they'd merge such a change at this stage or would rather wait for ext-screencopy.

Expand the chooser protocol to account for them (https://github.com/emersion/xdg-desktop-portal-wlr/issues/169)

The OUTPUT CHOOSER protocol can be extended to take a value matching a ext_foreign_toplevel_handle_v1::identifier.

Given that, potentially, and output name can match a toplevel name (it would be terrible, but nothing explicitly forbids this), I think a prefix of toplevel: should be used here.

Add this together to share window buffers

Blocked by the items above

paolomainardi commented 7 months ago

Wow, thanks, @WhyNotHugo. That's a great summary. So we have to help to push ext-screenshopy further.

amano-kenji commented 7 months ago

Is hyprland already more mature than sway? I already found a few things that work properly only in hyprland.

RioMeYeah commented 2 months ago

Still nothing with Sway?

ngotchac commented 2 months ago

I have this little script to start a new headless output for screen sharing:

#!/usr/bin/env bash

# Step 1: Create a new output
swaymsg create_output

# Step 2: Find the name of the newly created output
NEW_OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name' | sort | tail -n 1)

# Check if the output was successfully created
if [ -z "$NEW_OUTPUT" ]; then
    echo "Failed to create a new output."
    exit 1
fi

# Step 3: Assign a workspace to the new output
swaymsg workspace sshr output "$NEW_OUTPUT"

# Step 4: Set the resolution for the new output
swaymsg output "$NEW_OUTPUT" resolution 1280x720

# Step 5: Set the background color for the new output
swaymsg output "$NEW_OUTPUT" bg "#220900" solid_color

# Step 6: Switch to workspace sshr and then back to the previous workspace
CURRENT_WORKSPACE=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused) | .name')
swaymsg workspace sshr
swaymsg workspace "$CURRENT_WORKSPACE"

wl-mirror "$NEW_OUTPUT"

notify-send "Created new output $NEW_OUTPUT."

with keybindings in my sway config for workspace sshr.

It creates the new output, assign the sshr workspace to it, switch back and forth to it (since when the output is created, the next available workspace is assigned to it), open wl-mirror to it, and send a notification.

rmasad commented 2 months ago

Nice, the problem is that now Chrome doesn't let me share windows, only tabs. Pressing on windows returns me to the tabs section.

jalvesaq commented 2 months ago

@rmasad, you can share the whole virtual (headless) output and use wl-mirror only to visualize the output outside the browser.

YellowOnion commented 1 month ago

@paolomainardi Hyperland uses the hyprland-toplevel-export-v1 protocol, which wlroots does not support.

Hyprland uses a fork of wlroots, the question of why they haven't migrated these changes to upstream is valid, though I think just a boring answer where they just want to move fast and break things without worrying about compatibility till designs are stable or trying be considerate of ever potential use case, I also have a suspicion that wlroots/wayland devs have other priorities, hyprland has a focus on features that make using OBS good, like global hotkey support, that's been a stagnant in wayland for about 8 years, and hyprland is the only wl-wm with support for it.

Php22 commented 1 month ago

I have this little script to start a new headless output for screen sharing:

#!/usr/bin/env bash

# Step 1: Create a new output
swaymsg create_output

# Step 2: Find the name of the newly created output
NEW_OUTPUT=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name' | sort | tail -n 1)

# Check if the output was successfully created
if [ -z "$NEW_OUTPUT" ]; then
    echo "Failed to create a new output."
    exit 1
fi

# Step 3: Assign a workspace to the new output
swaymsg workspace sshr output "$NEW_OUTPUT"

# Step 4: Set the resolution for the new output
swaymsg output "$NEW_OUTPUT" resolution 1280x720

# Step 5: Set the background color for the new output
swaymsg output "$NEW_OUTPUT" bg "#220900" solid_color

# Step 6: Switch to workspace sshr and then back to the previous workspace
CURRENT_WORKSPACE=$(swaymsg -t get_workspaces | jq -r '.[] | select(.focused) | .name')
swaymsg workspace sshr
swaymsg workspace "$CURRENT_WORKSPACE"

wl-mirror "$NEW_OUTPUT"

notify-send "Created new output $NEW_OUTPUT."

A awesome script! just one question, how do i delete the virtual output (the "headless" one) after i'm done recording/sharing screen?

ngotchac commented 1 month ago

A awesome script! just one question, how do i delete the virtual output (the "headless" one) after i'm done recording/sharing screen?

I use this script for stopping the screenshare:

#!/usr/bin/env bash

# Get all outputs with names starting with HEADLESS-
headless_outputs=$(swaymsg -t get_outputs | jq -r '.[] | select(.name | startswith("HEADLESS-")) | .name')

# Check if there are any HEADLESS outputs
if [ -z "$headless_outputs" ]; then
    echo "No HEADLESS outputs found."
    exit 0
fi

# Unplug each HEADLESS output
for output in $headless_outputs; do
    echo "Unplugging $output..."
    swaymsg output "$output" unplug
done

notify-send "All HEADLESS outputs have been unplugged."