Repository: electron/electron (language: cpp)

Issue:
[Feature Request]: Use combined PipeWire Picker for desktopCapturer when windows and screens are requested
### Preflight Checklist

- [X] I have read the [Contributing Guidelines](https://github.com/electron/electron/blob/main/CONTRIBUTING.md) for this project.
- [X] I agree to follow the [Code of Conduct](https://github.com/electron/electron/blob/main/CODE_OF_CONDUCT.md) that this project adheres to.
- [X] I have searched the [issue tracker](https://www.github.com/electron/electron/issues) for a feature request that matches the one I want to file, without success.

### Problem Description

When calling `desktopCapturer.getSources({ types: ['window', 'screen'] })` it opens two PipeWire pickers, one for a window and one for a screen. It can't get the sources if one of them is canceled (e.g. the user only wants to share a window or screen and cancels the other one).

![image](https://github.com/electron/electron/assets/32410361/db11fb32-7c77-4d8f-980d-9251ba587254)

### Proposed Solution

Use a combined picker if a window and screen capture is requested. 
My Pseudo Idea how this could be implemented:
Add a check in [electron_api_desktop_capturer.cc](https://github.com/aiddya/electron/blob/72f357ad773056483518964820700672465a048f/shell/browser/api/electron_api_desktop_capturer.cc#L240) if capture_window and capture_screen are true and a delegated Sourcelist is used.
Then somehow pass `ScreenCastPortal::CaptureSourceType::kAnyScreenContent` as a CaptureType in [screencast_portal.cc](https://webrtc.googlesource.com/src/+/8fcc6df79daf1810cd4ecdb8d2ef1d361abfdc9c/modules/desktop_capture/linux/wayland/screencast_portal.cc#95)

### Alternatives Considered

Ignore if one of the pickers gets canceled.

### Additional Information

Fixing behavior that was out of scope of #38833 


Discussion / hints:
This cannot be addressed without patching Chromium, specifically webrtc. I initially thought that this patch would not have any chance of being upstreamed, but I saw that [Firefox is patching webrtc](https://phabricator.services.mozilla.com/D169627) for similar reasons. Considering that there are two potential consumers for this patch, perhaps it does have a chance.

Let me reach out to the original author of the patch and find out what he thinks about this.
The original PR is now merged, so this should be unblocked to work on now!
I emailed Jan Grulich, the author of the Firefox WebRTC patch. He replied saying that he had already talked to WebRTC maintainers about upstreaming that patch and is planning to submit it soon. I think this patch can be added as it'll be temporary.

Fix patch:
diff --git a/patches/chromium/.patches b/patches/chromium/.patches
index 72c5e85befd87..767054b15cde0 100644
--- a/patches/chromium/.patches
+++ b/patches/chromium/.patches
@@ -131,3 +131,4 @@ fix_select_the_first_menu_item_when_opened_via_keyboard.patch
 fix_return_v8_value_from_localframe_requestexecutescript.patch
 fix_harden_blink_scriptstate_maybefrom.patch
 chore_add_buildflag_guard_around_new_include.patch
+fix_use_delegated_generic_capturer_when_available.patch
diff --git a/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch
new file mode 100644
index 0000000000000..79099b2382878
--- /dev/null
+++ b/patches/chromium/fix_use_delegated_generic_capturer_when_available.patch
@@ -0,0 +1,54 @@
+From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
+From: Athul Iddya <athul@iddya.com>
+Date: Fri, 14 Jul 2023 08:03:37 -0700
+Subject: fix: use delegated generic capturer when available
+
+When the generic capturer is used to fetch capture sources, the returned
+ID will be arbitrarily prefixed with "screen" or "window" regardless of
+the source type. If the window capturer is used to stream video when the
+source was a screen or vice-versa, the stream fails to restart in
+delegated capturers like PipeWire.
+
+To fix this, use the generic capturer to fetch the media stream if it's
+delegated and available. This does not cause any issues if the original
+capturer was window or screen-specific, as the IDs remain valid for
+generic capturer as well.
+
+diff --git a/content/browser/media/capture/desktop_capture_device.cc b/content/browser/media/capture/desktop_capture_device.cc
+index b0c4efdd8a6401e09520bdfa96221e5addcfd829..f5b7447938f70dd4623a60fa6a856775b627f3d6 100644
+--- a/content/browser/media/capture/desktop_capture_device.cc
++++ b/content/browser/media/capture/desktop_capture_device.cc
+@@ -794,8 +794,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
+               DesktopCapturerLacros::CaptureType::kScreen,
+               webrtc::DesktopCaptureOptions());
+ #else
+-      std::unique_ptr<webrtc::DesktopCapturer> screen_capturer(
+-          webrtc::DesktopCapturer::CreateScreenCapturer(options));
++      std::unique_ptr<webrtc::DesktopCapturer> screen_capturer;
++      if (auto generic_capturer =
++              webrtc::DesktopCapturer::CreateGenericCapturer(options);
++          generic_capturer && generic_capturer->GetDelegatedSourceListController()) {
++        screen_capturer = std::move(generic_capturer);
++      } else {
++        screen_capturer = webrtc::DesktopCapturer::CreateScreenCapturer(options);
++      }
+ #endif
+       if (screen_capturer && screen_capturer->SelectSource(source.id)) {
+         capturer = std::make_unique<webrtc::DesktopAndCursorComposer>(
+@@ -814,8 +820,14 @@ std::unique_ptr<media::VideoCaptureDevice> DesktopCaptureDevice::Create(
+           new DesktopCapturerLacros(DesktopCapturerLacros::CaptureType::kWindow,
+                                     webrtc::DesktopCaptureOptions()));
+ #else
+-      std::unique_ptr<webrtc::DesktopCapturer> window_capturer =
+-          webrtc::DesktopCapturer::CreateWindowCapturer(options);
++      std::unique_ptr<webrtc::DesktopCapturer> window_capturer;
++      if (auto generic_capturer =
++              webrtc::DesktopCapturer::CreateGenericCapturer(options);
++          generic_capturer && generic_capturer->GetDelegatedSourceListController()) {
++        window_capturer = std::move(generic_capturer);
++      } else {
++        window_capturer = webrtc::DesktopCapturer::CreateWindowCapturer(options);
++      }
+ #endif
+       if (window_capturer && window_capturer->SelectSource(source.id)) {
+         capturer = std::make_unique<webrtc::DesktopAndCursorComposer>(
diff --git a/shell/browser/api/electron_api_desktop_capturer.cc b/shell/browser/api/electron_api_desktop_capturer.cc
index 1e908db7c87c8..5cfe0fedb9ef3 100644
--- a/shell/browser/api/electron_api_desktop_capturer.cc
+++ b/shell/browser/api/electron_api_desktop_capturer.cc
@@ -234,6 +234,33 @@ void DesktopCapturer::StartHandling(bool capture_window,
   // clear any existing captured sources.
   captured_sources_.clear();
 
+  if (capture_window && capture_screen) {
+    // Some capturers like PipeWire suppport a single capturer for both screens
+    // and windows. Use it if possible, treating both as window capture
+    if (auto capturer = webrtc::DesktopCapturer::CreateGenericCapturer(
+            content::desktop_capture::CreateDesktopCaptureOptions());
+        capturer && capturer->GetDelegatedSourceListController()) {
+      capture_screen_ = false;
+      capture_window_ = capture_window;
+      window_capturer_ = std::make_unique<NativeDesktopMediaList>(
+          DesktopMediaList::Type::kWindow, std::move(capturer));
+      window_capturer_->SetThumbnailSize(thumbnail_size);
+
+      OnceCallback update_callback = base::BindOnce(
+          &DesktopCapturer::UpdateSourcesList, weak_ptr_factory_.GetWeakPtr(),
+          window_capturer_.get());
+      OnceCallback failure_callback = base::BindOnce(
+          &DesktopCapturer::HandleFailure, weak_ptr_factory_.GetWeakPtr());
+
+      window_listener_ = std::make_unique<DesktopListListener>(
+          std::move(update_callback), std::move(failure_callback),
+          thumbnail_size.IsEmpty());
+      window_capturer_->StartUpdating(window_listener_.get());
+
+      return;
+    }
+  }
+
   // Start listening for captured sources.
   capture_window_ = capture_window;
   capture_screen_ = capture_screen;


Test patch: