qutebrowser / qutebrowser

A keyboard-driven, vim-like browser based on Python and Qt.
https://www.qutebrowser.org/
GNU General Public License v3.0
9.45k stars 1.01k forks source link

Add screen picker completion menu for screen sharing #8214

Open toofar opened 3 weeks ago

toofar commented 3 weeks ago

Since Qt version 6.7 WebEngine has had a QWebEnginePage.desktopMediaRequested signal that is called when you go to share a screen to let applications pick a screen or window to share. The default behaviour shares the primary screen or the whole desktop depending on which platform you are on.

The request object is documented here: https://doc.qt.io/qt-6/qwebenginedesktopmediarequest.html That holds two QAbstractListModels, one for screens and one for windows. The current API only makes available titles for screens and windows, no thumbnails or previews (although it looks like chrome's DesktopCapturer that is used underneath has them). On one hand that limits what we can implement in a screen picker, on the other hand it aligns well with the single generic list based UI widget we have, the completion widget.

You'll need to create a completion model from the request (not sure if you can use the same list models on the request in the completion widget, I doubt it) and figure out how to show the completion widget - I'm not sure how to show that not from a command registration, possibly via the CompletionView on MainWindow.

You can test with: https://www.webrtc-experiment.com/screen-sharing/

Starter patch that just connects to the new signal and prints the rows. ```python diff --git a/qutebrowser/browser/webengine/webenginetab.py b/qutebrowser/browser/webengine/webenginetab.py index 48ae7ea5080..bf2958d62b0 100644 --- a/qutebrowser/browser/webengine/webenginetab.py +++ b/qutebrowser/browser/webengine/webenginetab.py @@ -27,6 +27,11 @@ resources, message, jinja, debug, version, urlutils) from qutebrowser.qt import sip, machinery from qutebrowser.misc import objects, miscwidgets +try: + from qutebrowser.qt.webenginecore import QWebEngineDesktopMediaRequest +except ImportError: + # Qt<6.7 + QWebEngineDesktopMediaRequest = None # type: ignore[assignment,misc] # Mapping worlds from usertypes.JsWorld to QWebEngineScript world IDs. @@ -1660,6 +1665,33 @@ def _on_select_client_certificate(self, selection): else: selection.selectNone() + def _on_desktop_media_requested(self, request: QWebEngineDesktopMediaRequest) -> None: + windows = request.windowsModel() + screens = request.screensModel() + if windows is None or screens is None: + log.webview.error( + "Cannot pick screen to share, got a null model: " + "windows={}, screens={}".format(windows, screens) + ) + return + + # We only get names for model items, that's all. Underneath Qt has + # DesktopMediaList::Source structs from + # `chrome/browser/media/webrtc/desktop_media_list.h` and these have + # id, name, thumbnail and preview. Currently DesktopMediaListQt only + # surfaces the name attribute (eg the window title or "Screen 1"), + # presumably to keep the diffstat of the initial PR smaller. + + print("--- screens ---") + for i in range(screens.rowCount()): + print(f"{i}: {screens.data(screens.index(i))}") + print("--- windows ---") + for i in range(windows.rowCount()): + print(f"{i}: {windows.data(windows.index(i))}") + + # This is the default upstream behavior. + #request.selectScreen(screens.index(0)) + def _connect_signals(self): view = self._widget page = view.page() @@ -1677,6 +1709,8 @@ def _connect_signals(self): page.navigation_request.connect(self._on_navigation_request) page.printRequested.connect(self._on_print_requested) page.selectClientCertificate.connect(self._on_select_client_certificate) + if version.qtwebengine_versions().webengine >= utils.VersionNumber(6, 7): + page.desktopMediaRequested.connect(self._on_desktop_media_requested) view.titleChanged.connect(self.title_changed) view.urlChanged.connect(self._on_url_changed) ```