diff --git a/firefox-pipewire.patch b/firefox-pipewire.patch new file mode 100644 index 0000000..6ed5801 --- /dev/null +++ b/firefox-pipewire.patch @@ -0,0 +1,1943 @@ +PipeWire WebRTC patch from https://github.com/xhorak/firefox-devedition-flatpak +https://bugzilla.mozilla.org/show_bug.cgi?id=1496359 + +diff --git a/config/system-headers.mozbuild b/config/system-headers.mozbuild +index 7f17843a37bd..1f4d711e5d39 100644 +--- a/config/system-headers.mozbuild ++++ b/config/system-headers.mozbuild +@@ -314,6 +314,7 @@ system_headers = [ + 'Gestalt.h', + 'getopt.h', + 'gio/gio.h', ++ 'gio/gunixfdlist.h', + 'glibconfig.h', + 'glib.h', + 'glib-object.h', +@@ -607,6 +608,7 @@ system_headers = [ + 'Pgenerr.h', + 'PGenErr.h', + 'Ph.h', ++ 'pipewire/pipewire.h', + 'pixman.h', + 'pk11func.h', + 'pk11pqg.h', +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn b/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn +index ed12a1521eb0..a9e700ccd3e0 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/BUILD.gn +@@ -213,7 +213,6 @@ rtc_static_library("desktop_capture") { + if (build_with_mozilla) { + sources += [ + "app_capturer_x11.cc", +- "app_capturer_x11.h", + "x11/desktop_device_info_x11.cc", + "x11/desktop_device_info_x11.h", + "x11/shared_x_util.cc", +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc +new file mode 100644 +index 000000000000..4ac35aa8496b +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc +@@ -0,0 +1,55 @@ ++/* ++ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "webrtc/modules/desktop_capture/desktop_capture_options.h" ++#include "webrtc/modules/desktop_capture/desktop_capturer.h" ++ ++#if defined(USE_PIPEWIRE) ++#include "webrtc/modules/desktop_capture/app_capturer_null.cc" ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++#include "webrtc/modules/desktop_capture/app_capturer_x11.cc" ++#endif // defined(USE_X11) ++ ++namespace webrtc { ++ ++// static ++AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) { ++#if defined(USE_PIPEWIRE) ++ if (DesktopCapturer::IsRunningUnderWayland()) { ++ return new AppCapturerNull(); ++ } ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++ return AppCapturerX11::Create(options); ++#endif // defined(USE_X11) ++ ++ return nullptr; ++} ++ ++// static ++std::unique_ptr DesktopCapturer::CreateRawAppCapturer( ++ const DesktopCaptureOptions& options) { ++#if defined(USE_PIPEWIRE) ++ if (DesktopCapturer::IsRunningUnderWayland()) { ++ return std::unique_ptr(new AppCapturerNull()); ++ } ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++ return AppCapturerX11::CreateRawAppCapturer(options); ++#endif // defined(USE_X11) ++ ++ return nullptr; ++} ++ ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc +index 5a0b1a28f058..84a7e5951032 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_null.cc +@@ -7,8 +7,8 @@ + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ +-#include "webrtc/modules/desktop_capture/window_capturer.h" + #include "webrtc/modules/desktop_capture/app_capturer.h" ++#include "webrtc/modules/desktop_capture/desktop_capturer.h" + + #include + +@@ -29,9 +29,11 @@ public: + virtual bool BringAppToFront() override; + + // DesktopCapturer interface. +- virtual void Start(Callback* callback) override; +- virtual void Stop() override; +- virtual void Capture(const DesktopRegion& region) override; ++ void Start(Callback* callback) override; ++ void Stop() override; ++ void CaptureFrame() override; ++ bool GetSourceList(SourceList* sources) override; ++ bool SelectSource(SourceId id) override; + + private: + Callback* callback_; +@@ -61,7 +63,16 @@ bool AppCapturerNull::BringAppToFront() { + return false; + } + +-// DesktopCapturer interface. ++bool AppCapturerNull::GetSourceList(SourceList* sources) { ++ // Not implemented yet. ++ return false; ++} ++ ++bool AppCapturerNull::SelectSource(SourceId id) { ++ // Not implemented yet. ++ return false; ++} ++ + void AppCapturerNull::Start(Callback* callback) { + assert(!callback_); + assert(callback); +@@ -73,16 +84,19 @@ void AppCapturerNull::Stop() { + callback_ = NULL; + } + +-void AppCapturerNull::Capture(const DesktopRegion& region) { +- // Not implemented yet: See Bug 1036653 +- callback_->OnCaptureCompleted(NULL); ++void AppCapturerNull::CaptureFrame() { ++ // Not implemented yet. ++ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + } + + } // namespace + ++#if not defined(USE_PIPEWIRE) + // static +-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) { +- return new AppCapturerNull(); ++std::unique_ptr DesktopCapturer::CreateRawAppCapturer( ++ const DesktopCaptureOptions& options) { ++ return std::unique_ptr(new AppCapturerNull()); + } ++#endif // not defined(USE_PIPEWIRE) + + } // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc +index 8215823672b6..5c20ed0a517b 100755 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc +@@ -53,10 +53,13 @@ protected: + std::unique_ptr frame_; + }; + +-class AppCapturerLinux : public AppCapturer { ++class AppCapturerX11 : public AppCapturer { + public: +- AppCapturerLinux(const DesktopCaptureOptions& options); +- virtual ~AppCapturerLinux(); ++ AppCapturerX11(const DesktopCaptureOptions& options); ++ virtual ~AppCapturerX11(); ++ ++ static AppCapturer* Create(const DesktopCaptureOptions& options); ++ static std::unique_ptr CreateRawAppCapturer(const DesktopCaptureOptions& options); + + // AppCapturer interface. + virtual bool GetAppList(AppList* apps) override; +@@ -91,10 +94,9 @@ private: + Region rgn_background_; + + rtc::scoped_refptr x_display_; +- RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerLinux); ++ RTC_DISALLOW_COPY_AND_ASSIGN(AppCapturerX11); + }; +- +-AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options) ++AppCapturerX11::AppCapturerX11(const DesktopCaptureOptions& options) + : callback_(NULL), + selected_process_(0), + x_display_(options.x_display()) { +@@ -103,7 +105,7 @@ AppCapturerLinux::AppCapturerLinux(const DesktopCaptureOptions& options) + rgn_background_ = XCreateRegion(); + } + +-AppCapturerLinux::~AppCapturerLinux() { ++AppCapturerX11::~AppCapturerX11() { + if (rgn_mask_) { + XDestroyRegion(rgn_mask_); + } +@@ -116,32 +118,32 @@ AppCapturerLinux::~AppCapturerLinux() { + } + + // AppCapturer interface. +-bool AppCapturerLinux::GetAppList(AppList* apps) { ++bool AppCapturerX11::GetAppList(AppList* apps) { + // Implemented in DesktopDeviceInfo + return true; + } +-bool AppCapturerLinux::SelectApp(ProcessId processId) { ++bool AppCapturerX11::SelectApp(ProcessId processId) { + selected_process_ = processId; + return true; + } +-bool AppCapturerLinux::BringAppToFront() { ++bool AppCapturerX11::BringAppToFront() { + // Not implemented yet: See Bug 1036653 + return true; + } + + // DesktopCapturer interface. +-void AppCapturerLinux::Start(Callback* callback) { ++void AppCapturerX11::Start(Callback* callback) { + assert(!callback_); + assert(callback); + + callback_ = callback; + } + +-void AppCapturerLinux::Stop() { ++void AppCapturerX11::Stop() { + callback_ = NULL; + } + +-void AppCapturerLinux::CaptureFrame() { ++void AppCapturerX11::CaptureFrame() { + XErrorTrap error_trap(GetDisplay()); + + //Capture screen >> set root window as capture window +@@ -169,7 +171,7 @@ void AppCapturerLinux::CaptureFrame() { + } + } + +-void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) { ++void AppCapturerX11::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFrame, Region rgn, uint32_t color) { + XErrorTrap error_trap(GetDisplay()); + + if (!pDesktopFrame) { +@@ -192,7 +194,7 @@ void AppCapturerLinux::FillDesktopFrameRegionWithColor(DesktopFrame* pDesktopFra + } + } + +-bool AppCapturerLinux::UpdateRegions() { ++bool AppCapturerX11::UpdateRegions() { + XErrorTrap error_trap(GetDisplay()); + + XSubtractRegion(rgn_visual_, rgn_visual_, rgn_visual_); +@@ -272,18 +274,18 @@ bool AppCapturerLinux::UpdateRegions() { + } // namespace + + // static +-AppCapturer* AppCapturer::Create(const DesktopCaptureOptions& options) { +- return new AppCapturerLinux(options); ++AppCapturer* AppCapturerX11::Create(const DesktopCaptureOptions& options) { ++ return new AppCapturerX11(options); + } + + // static +-std::unique_ptr DesktopCapturer::CreateRawAppCapturer( ++std::unique_ptr AppCapturerX11::CreateRawAppCapturer( + const DesktopCaptureOptions& options) { + + if (!options.x_display()) + return nullptr; + +- std::unique_ptr capturer(new AppCapturerLinux(options)); ++ std::unique_ptr capturer(new AppCapturerX11(options)); + + return std::unique_ptr(std::move(capturer)); + } +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build +index d7909d983053..b50b005c300e 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capture_gn/moz.build +@@ -166,10 +166,6 @@ if CONFIG["OS_TARGET"] == "Linux": + ] + + UNIFIED_SOURCES += [ +- "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_x11.cc", +- "/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc", +- "/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc", +- "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/desktop_device_info_x11.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/shared_x_display.cc", + "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/shared_x_util.cc", +@@ -177,6 +173,35 @@ if CONFIG["OS_TARGET"] == "Linux": + "/media/webrtc/trunk/webrtc/modules/desktop_capture/x11/x_server_pixel_buffer.cc" + ] + ++# Common stuff between Pipewire and X11 ++if CONFIG["OS_TARGET"] == "Linux": ++ ++ UNIFIED_SOURCES += [ ++ "/media/webrtc/trunk/webrtc/modules/desktop_capture/app_capturer_linux.cc", ++ "/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc", ++ "/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc", ++ "/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc" ++ ] ++ ++# Pipewire ++if CONFIG["OS_TARGET"] == "Linux": ++ ++ DEFINES["USE_PIPEWIRE"] = "1" ++ ++ OS_LIBS += [ ++ "rt", ++ "pipewire-0.2", ++ "glib-2.0", ++ "gio-2.0", ++ "gobject-2.0" ++ ] ++ ++ CXXFLAGS += CONFIG['TK_CFLAGS'] ++ ++ UNIFIED_SOURCES += [ ++ "/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc" ++ ] ++ + if CONFIG["OS_TARGET"] == "NetBSD": + + DEFINES["USE_X11"] = "1" +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc +index 7510bde47fbb..9dab1eb503f6 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.cc +@@ -66,4 +66,17 @@ std::unique_ptr DesktopCapturer::CreateAppCapturer( + return capturer; + } + ++#if defined(USE_PIPEWIRE) || defined(USE_X11) ++bool DesktopCapturer::IsRunningUnderWayland() { ++ const char* xdg_session_type = getenv("XDG_SESSION_TYPE"); ++ if (!xdg_session_type || strncmp(xdg_session_type, "wayland", 7) != 0) ++ return false; ++ ++ if (!(getenv("WAYLAND_DISPLAY"))) ++ return false; ++ ++ return true; ++} ++#endif ++ + } // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h +index eb84d3c3bf50..17cacc5dcac6 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/desktop_capturer.h +@@ -129,6 +129,10 @@ class DesktopCapturer { + static std::unique_ptr CreateAppCapturer( + const DesktopCaptureOptions& options); + ++#if defined(USE_PIPEWIRE) || defined(USE_X11) ++ static bool IsRunningUnderWayland(); ++#endif ++ + protected: + // CroppingWindowCapturer needs to create raw capturers without wrappers, so + // the following two functions are protected. +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc +new file mode 100644 +index 000000000000..f7aaf3dcd6d9 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_linux.cc +@@ -0,0 +1,44 @@ ++/* ++ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "webrtc/modules/desktop_capture/mouse_cursor_monitor.h" ++ ++#if defined(USE_PIPEWIRE) ++#include "webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc" ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++#include "webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc" ++#endif // defined(USE_X11) ++ ++namespace webrtc { ++ ++// static ++MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( ++ const DesktopCaptureOptions& options, WindowId window) { ++#if defined(USE_X11) ++ return MouseCursorMonitorX11::CreateForWindow(options, window); ++#else ++ return nullptr; ++#endif // defined(USE_X11) ++} ++ ++// static ++MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( ++ const DesktopCaptureOptions& options, ++ ScreenId screen) { ++#if defined(USE_X11) ++ return MouseCursorMonitorX11::CreateForScreen(options, screen); ++#else ++ return nullptr; ++#endif // defined(USE_X11) ++} ++ ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc +index 3a632cc0d9cd..b70b4e2568dc 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_null.cc +@@ -14,6 +14,7 @@ + + namespace webrtc { + ++#if not defined(USE_PIPEWIRE) + MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( + const DesktopCaptureOptions& options, + WindowId window) { +@@ -25,5 +26,6 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( + ScreenId screen) { + return NULL; + } ++#endif // not defined(USE_PIPEWIRE) + + } // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc +index 2dcad5b3887f..a8397f1ec15c 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/mouse_cursor_monitor_x11.cc +@@ -69,6 +69,9 @@ class MouseCursorMonitorX11 : public MouseCursorMonitor, + void Stop() override; + void Capture() override; + ++ static MouseCursorMonitor* CreateForWindow(const DesktopCaptureOptions& options, WindowId window); ++ static MouseCursorMonitor* CreateForScreen(const DesktopCaptureOptions& options, ScreenId screen); ++ + private: + // SharedXDisplay::XEventHandler interface. + bool HandleXEvent(const XEvent& event) override; +@@ -244,7 +247,7 @@ void MouseCursorMonitorX11::CaptureCursor() { + } + + // static +-MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( ++MouseCursorMonitor* MouseCursorMonitorX11::CreateForWindow( + const DesktopCaptureOptions& options, WindowId window) { + if (!options.x_display()) + return NULL; +@@ -254,7 +257,7 @@ MouseCursorMonitor* MouseCursorMonitor::CreateForWindow( + return new MouseCursorMonitorX11(options, outer_window, window); + } + +-MouseCursorMonitor* MouseCursorMonitor::CreateForScreen( ++MouseCursorMonitor* MouseCursorMonitorX11::CreateForScreen( + const DesktopCaptureOptions& options, + ScreenId screen) { + if (!options.x_display()) +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc +new file mode 100644 +index 000000000000..8c155102ed74 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_linux.cc +@@ -0,0 +1,42 @@ ++/* ++ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "webrtc/modules/desktop_capture/desktop_capture_options.h" ++#include "webrtc/modules/desktop_capture/desktop_capturer.h" ++ ++#include ++ ++#if defined(USE_PIPEWIRE) ++#include "webrtc/modules/desktop_capture/screen_capturer_pipewire.cc" ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++#include "webrtc/modules/desktop_capture/screen_capturer_x11.cc" ++#endif // defined(USE_X11) ++ ++namespace webrtc { ++ ++// static ++std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( ++ const DesktopCaptureOptions& options) { ++#if defined(USE_PIPEWIRE) ++ if (DesktopCapturer::IsRunningUnderWayland()) { ++ return ScreenCapturerPipeWire::CreateRawScreenCapturer(options); ++ } ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++ return ScreenCapturerX11::CreateRawScreenCapturer(options); ++#endif // defined(USE_X11) ++ ++ return nullptr; ++} ++ ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc +new file mode 100644 +index 000000000000..ce47ae29eac9 +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_pipewire.cc +@@ -0,0 +1,1003 @@ ++/* ++ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include ++ ++#include ++#include ++#include ++#define typeof __typeof__ ++#include ++ ++#include ++#include ++#include ++#include ++#include ++ ++#include "webrtc/base/checks.h" ++#include "webrtc/base/logging.h" ++#include "webrtc/modules/desktop_capture/desktop_capture_options.h" ++#include "webrtc/modules/desktop_capture/desktop_capturer.h" ++ ++namespace webrtc { ++namespace { ++ ++const char kDesktopBusName[] = "org.freedesktop.portal.Desktop"; ++const char kDesktopObjectPath[] = "/org/freedesktop/portal/desktop"; ++const char kDesktopRequestObjectPath[] = ++ "/org/freedesktop/portal/desktop/request"; ++const char kSessionInterfaceName[] = "org.freedesktop.portal.Session"; ++const char kRequestInterfaceName[] = "org.freedesktop.portal.Request"; ++const char kScreenCastInterfaceName[] = "org.freedesktop.portal.ScreenCast"; ++ ++const int kBytesPerPixel = 4; ++ ++class PipeWireType { ++ public: ++ spa_type_media_type media_type; ++ spa_type_media_subtype media_subtype; ++ spa_type_format_video format_video; ++ spa_type_video_format video_format; ++}; ++ ++class ScreenCapturerPipeWire : public DesktopCapturer { ++ public: ++ ScreenCapturerPipeWire(); ++ ~ScreenCapturerPipeWire() override; ++ ++ static std::unique_ptr CreateRawScreenCapturer( ++ const DesktopCaptureOptions& options); ++ ++ // DesktopCapturer interface. ++ void Start(Callback* delegate) override; ++ void Stop() override; ++ void CaptureFrame() override; ++ bool GetSourceList(SourceList* sources) override; ++ bool SelectSource(SourceId id) override; ++ ++ private: ++ // PipeWire types --> ++ pw_core* pw_core_ = nullptr; ++ pw_type* pw_core_type_ = nullptr; ++ pw_stream* pw_stream_ = nullptr; ++ pw_remote* pw_remote_ = nullptr; ++ pw_loop* pw_loop_ = nullptr; ++ pw_thread_loop* pw_main_loop_ = nullptr; ++ PipeWireType* pw_type_ = nullptr; ++ ++ spa_hook spa_stream_listener_ = {}; ++ spa_hook spa_remote_listener_ = {}; ++ ++ pw_stream_events pw_stream_events_ = {}; ++ pw_remote_events pw_remote_events_ = {}; ++ ++ spa_video_info_raw* spa_video_format_ = nullptr; ++ ++ gint32 pw_fd_ = -1; ++ ++ // <-- end of PipeWire types ++ ++ GDBusConnection* connection_ = nullptr; ++ GDBusProxy* proxy_ = nullptr; ++ gchar* portal_handle_ = nullptr; ++ gchar* session_handle_ = nullptr; ++ gchar* sources_handle_ = nullptr; ++ gchar* start_handle_ = nullptr; ++ guint session_request_signal_id_ = 0; ++ guint sources_request_signal_id_ = 0; ++ guint start_request_signal_id_ = 0; ++ ++ DesktopSize desktop_size_ = {}; ++ DesktopCaptureOptions options_ = {}; ++ ++ uint8_t* current_frame_ = nullptr; ++ Callback* callback_ = nullptr; ++ ++ void InitPipeWire(); ++ void InitPipeWireTypes(); ++ ++ void CreateReceivingStream(); ++ void HandleBuffer(pw_buffer* buffer); ++ ++ void ConvertRGBxToBGRxIfNeeded(uint8_t* frame, uint32_t size); ++ ++ static void OnStateChanged(void* data, ++ pw_remote_state old_state, ++ pw_remote_state state, ++ const char* error); ++ static void OnStreamStateChanged(void* data, ++ pw_stream_state old_state, ++ pw_stream_state state, ++ const char* error_message); ++ ++ static void OnStreamFormatChanged(void* data, const struct spa_pod* format); ++ static void OnStreamProcess(void* data); ++ static void OnNewBuffer(void* data, uint32_t id); ++ ++ guint SetupRequestResponseSignal(const gchar* object_path, ++ GDBusSignalCallback callback); ++ ++ static void OnProxyRequested(GObject* object, ++ GAsyncResult* result, ++ gpointer user_data); ++ ++ static gchar* PrepareSignalHandle(GDBusConnection* connection, ++ const gchar* token); ++ ++ void SessionRequest(); ++ static void OnSessionRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnSessionRequestResponseSignal(GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ ++ void SourcesRequest(); ++ static void OnSourcesRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnSourcesRequestResponseSignal(GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ ++ void StartRequest(); ++ static void OnStartRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data); ++ static void OnStartRequestResponseSignal(GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data); ++ ++ void OpenPipeWireRemote(); ++ static void OnOpenPipeWireRemoteRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data); ++ ++ RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerPipeWire); ++}; ++ ++// static ++void ScreenCapturerPipeWire::OnStateChanged(void* data, ++ pw_remote_state old_state, ++ pw_remote_state state, ++ const char* error_message) { ++ ScreenCapturerPipeWire* that = static_cast(data); ++ RTC_DCHECK(that); ++ ++ switch (state) { ++ case PW_REMOTE_STATE_ERROR: ++ LOG(LS_ERROR) << "PipeWire remote state error: " << error_message; ++ break; ++ case PW_REMOTE_STATE_CONNECTED: ++ LOG(LS_INFO) << "PipeWire remote state: connected."; ++ that->CreateReceivingStream(); ++ break; ++ case PW_REMOTE_STATE_CONNECTING: ++ LOG(LS_INFO) << "PipeWire remote state: connecting."; ++ break; ++ case PW_REMOTE_STATE_UNCONNECTED: ++ LOG(LS_INFO) << "PipeWire remote state: unconnected."; ++ break; ++ } ++} ++ ++// static ++void ScreenCapturerPipeWire::OnStreamStateChanged(void* data, ++ pw_stream_state old_state, ++ pw_stream_state state, ++ const char* error_message) { ++ ScreenCapturerPipeWire* that = static_cast(data); ++ RTC_DCHECK(that); ++ ++ switch (state) { ++ case PW_STREAM_STATE_ERROR: ++ LOG(LS_ERROR) << "PipeWire stream state error: " << error_message; ++ break; ++ case PW_STREAM_STATE_CONFIGURE: ++ pw_stream_set_active(that->pw_stream_, true); ++ break; ++ case PW_STREAM_STATE_UNCONNECTED: ++ case PW_STREAM_STATE_CONNECTING: ++ case PW_STREAM_STATE_READY: ++ case PW_STREAM_STATE_PAUSED: ++ case PW_STREAM_STATE_STREAMING: ++ break; ++ } ++} ++ ++// static ++void ScreenCapturerPipeWire::OnStreamFormatChanged( ++ void* data, ++ const struct spa_pod* format) { ++ ScreenCapturerPipeWire* that = static_cast(data); ++ RTC_DCHECK(that); ++ ++ LOG(LS_INFO) << "PipeWire stream format changed."; ++ ++ if (!format) { ++ pw_stream_finish_format(that->pw_stream_, /*res=*/0, /*params=*/nullptr, ++ /*n_params=*/0); ++ return; ++ } ++ ++ that->spa_video_format_ = new spa_video_info_raw(); ++ spa_format_video_raw_parse(format, that->spa_video_format_, ++ &that->pw_type_->format_video); ++ ++ auto width = that->spa_video_format_->size.width; ++ auto height = that->spa_video_format_->size.height; ++ auto stride = SPA_ROUND_UP_N(width * kBytesPerPixel, 4); ++ auto size = height * stride; ++ ++ uint8_t buffer[1024] = {}; ++ auto builder = spa_pod_builder{buffer, sizeof(buffer)}; ++ ++ // Setup buffers and meta header for new format. ++ const struct spa_pod* params[2]; ++ params[0] = reinterpret_cast(spa_pod_builder_object(&builder, ++ // id to enumerate buffer requirements ++ that->pw_core_type_->param.idBuffers, that->pw_core_type_->param_buffers.Buffers, ++ // Size: specified as integer (i) and set to specified size ++ ":", that->pw_core_type_->param_buffers.size, "i", size, ++ // Stride: specified as integer (i) and set to specified stride ++ ":", that->pw_core_type_->param_buffers.stride, "i", stride, ++ // Buffers: specifies how many buffers we want to deal with, set as integer (i) ++ // where preferred number is 8, then allowed number is defined as range (r) from ++ // min and max values and it is undecided (u) to allow negotiation ++ ":", that->pw_core_type_->param_buffers.buffers, "iru", ++ 8, SPA_POD_PROP_MIN_MAX(1, 32), ++ // Align: memory alignment of the buffer, set as integer (i) to specified value ++ ":", that->pw_core_type_->param_buffers.align, "i", 16)); ++ params[1] = reinterpret_cast(spa_pod_builder_object(&builder, ++ // id to enumerate supported metadata ++ that->pw_core_type_->param.idMeta, that->pw_core_type_->param_meta.Meta, ++ // Type: specified as id or enum (I) ++ ":", that->pw_core_type_->param_meta.type, "I", that->pw_core_type_->meta.Header, ++ // Size: size of the metadata, specified as integer (i) ++ ":", that->pw_core_type_->param_meta.size, "i", sizeof(struct spa_meta_header))); ++ ++ pw_stream_finish_format(that->pw_stream_, /*res=*/0, params, /*n_params=*/2); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnStreamProcess(void* data) { ++ ScreenCapturerPipeWire* that = static_cast(data); ++ RTC_DCHECK(that); ++ ++ pw_buffer* buf = nullptr; ++ ++ if (!(buf = pw_stream_dequeue_buffer(that->pw_stream_))) { ++ return; ++ } ++ ++ that->HandleBuffer(buf); ++ ++ pw_stream_queue_buffer(that->pw_stream_, buf); ++} ++ ++ScreenCapturerPipeWire::ScreenCapturerPipeWire() { ++ g_dbus_proxy_new_for_bus( ++ G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, /*info=*/nullptr, ++ kDesktopBusName, kDesktopObjectPath, kScreenCastInterfaceName, ++ /*cancellable=*/nullptr, ++ reinterpret_cast(OnProxyRequested), this); ++} ++ ++ScreenCapturerPipeWire::~ScreenCapturerPipeWire() { ++ if (pw_type_) { ++ delete pw_type_; ++ } ++ ++ if (spa_video_format_) { ++ delete spa_video_format_; ++ } ++ ++ if (pw_stream_) { ++ pw_stream_destroy(pw_stream_); ++ } ++ ++ if (pw_remote_) { ++ pw_remote_destroy(pw_remote_); ++ } ++ ++ if (pw_core_) { ++ pw_core_destroy(pw_core_); ++ } ++ ++ if (pw_main_loop_) { ++ pw_thread_loop_destroy(pw_main_loop_); ++ } ++ ++ if (pw_loop_) { ++ pw_loop_destroy(pw_loop_); ++ } ++ ++ if (current_frame_) { ++ free(current_frame_); ++ } ++ ++ if (start_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, start_request_signal_id_); ++ } ++ if (sources_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, ++ sources_request_signal_id_); ++ } ++ if (session_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection_, ++ session_request_signal_id_); ++ } ++ ++ if (session_handle_) { ++ GDBusMessage* message = g_dbus_message_new_method_call( ++ kDesktopBusName, session_handle_, kSessionInterfaceName, "Close"); ++ if (message) { ++ GError* error = nullptr; ++ g_dbus_connection_send_message(connection_, message, ++ G_DBUS_SEND_MESSAGE_FLAGS_NONE, ++ /*out_serial=*/nullptr, &error); ++ if (error) { ++ LOG(LS_ERROR) << "Failed to close the session: " << error->message; ++ g_error_free(error); ++ } ++ g_object_unref(message); ++ } ++ } ++ ++ g_free(start_handle_); ++ g_free(sources_handle_); ++ g_free(session_handle_); ++ g_free(portal_handle_); ++ ++ if (proxy_) { ++ g_clear_object(&proxy_); ++ } ++ ++ if (connection_) { ++ g_clear_object(&connection_); ++ } ++} ++ ++void ScreenCapturerPipeWire::InitPipeWire() { ++ pw_init(/*argc=*/nullptr, /*argc=*/nullptr); ++ ++ pw_loop_ = pw_loop_new(/*properties=*/nullptr); ++ pw_main_loop_ = pw_thread_loop_new(pw_loop_, "pipewire-main-loop"); ++ ++ pw_core_ = pw_core_new(pw_loop_, /*properties=*/nullptr); ++ pw_core_type_ = pw_core_get_type(pw_core_); ++ pw_remote_ = pw_remote_new(pw_core_, nullptr, 0); ++ ++ InitPipeWireTypes(); ++ ++ // Initialize event handlers, remote end and stream-related. ++ pw_remote_events_.version = PW_VERSION_REMOTE_EVENTS; ++ pw_remote_events_.state_changed = &OnStateChanged; ++ ++ pw_stream_events_.version = PW_VERSION_STREAM_EVENTS; ++ pw_stream_events_.state_changed = &OnStreamStateChanged; ++ pw_stream_events_.format_changed = &OnStreamFormatChanged; ++ pw_stream_events_.process = &OnStreamProcess; ++ ++ pw_remote_add_listener(pw_remote_, &spa_remote_listener_, &pw_remote_events_, ++ this); ++ pw_remote_connect_fd(pw_remote_, pw_fd_); ++ ++ if (pw_thread_loop_start(pw_main_loop_) < 0) { ++ LOG(LS_ERROR) << "Failed to start main PipeWire loop"; ++ } ++} ++ ++void ScreenCapturerPipeWire::InitPipeWireTypes() { ++ spa_type_map* map = pw_core_type_->map; ++ pw_type_ = new PipeWireType(); ++ ++ spa_type_media_type_map(map, &pw_type_->media_type); ++ spa_type_media_subtype_map(map, &pw_type_->media_subtype); ++ spa_type_format_video_map(map, &pw_type_->format_video); ++ spa_type_video_format_map(map, &pw_type_->video_format); ++} ++ ++void ScreenCapturerPipeWire::CreateReceivingStream() { ++ spa_rectangle pwMinScreenBounds = spa_rectangle{1, 1}; ++ spa_rectangle pwScreenBounds = ++ spa_rectangle{static_cast(desktop_size_.width()), ++ static_cast(desktop_size_.height())}; ++ ++ spa_fraction pwFrameRateMin = spa_fraction{0, 1}; ++ spa_fraction pwFrameRateMax = spa_fraction{60, 1}; ++ ++ pw_properties* reuseProps = pw_properties_new("pipewire.client.reuse", "1", ++ /*end of varargs*/ nullptr); ++ pw_stream_ = pw_stream_new(pw_remote_, "webrtc-consume-stream", reuseProps); ++ ++ uint8_t buffer[1024] = {}; ++ const spa_pod* params[1]; ++ spa_pod_builder builder = spa_pod_builder{buffer, sizeof(buffer)}; ++ params[0] = reinterpret_cast(spa_pod_builder_object(&builder, ++ // id to enumerate formats ++ pw_core_type_->param.idEnumFormat, pw_core_type_->spa_format, ++ "I", pw_type_->media_type.video, ++ "I", pw_type_->media_subtype.raw, ++ // Video format: specified as id or enum (I), preferred format is BGRx, then allowed ++ // formats are enumerated (e) and the format is undecided (u) to allow negotiation ++ ":", pw_type_->format_video.format, "Ieu", ++ pw_type_->video_format.BGRx, SPA_POD_PROP_ENUM(2, pw_type_->video_format.RGBx, ++ pw_type_->video_format.BGRx), ++ // Video size: specified as rectangle (R), preferred size is specified as first ++ // parameter, then allowed size is defined as range (r) from min and max values ++ // and the format is undecided (u) to allow negotiation ++ ":", pw_type_->format_video.size, "Rru", ++ &pwScreenBounds, 2, &pwMinScreenBounds, &pwScreenBounds, ++ // Frame rate: specified as fraction (F) and set to minimum frame rate value ++ ":", pw_type_->format_video.framerate, "F", &pwFrameRateMin, ++ // Max frame rate: specified as fraction (F), preferred frame rate is set to maximum ++ // value, then allowed frame rate is defined as range (r) from min and max values ++ // and it is undecided (u) to allow negotiation ++ ":", pw_type_->format_video.max_framerate, "Fru", ++ &pwFrameRateMax, 2, &pwFrameRateMin, &pwFrameRateMax)); ++ ++ pw_stream_add_listener(pw_stream_, &spa_stream_listener_, &pw_stream_events_, ++ this); ++ pw_stream_flags flags = static_cast( ++ PW_STREAM_FLAG_AUTOCONNECT | PW_STREAM_FLAG_INACTIVE | PW_STREAM_FLAG_MAP_BUFFERS); ++ if (pw_stream_connect(pw_stream_, PW_DIRECTION_INPUT, nullptr, flags, params, ++ 1) != 0) { ++ LOG(LS_ERROR) << "Could not connect receiving stream."; ++ } ++} ++ ++void ScreenCapturerPipeWire::HandleBuffer(pw_buffer* buffer) { ++ spa_buffer* spaBuffer = buffer->buffer; ++ void* src = nullptr; ++ ++ if (!(src = spaBuffer->datas[0].data)) { ++ return; ++ } ++ ++ uint32_t maxSize = spaBuffer->datas[0].maxsize; ++ int32_t srcStride = spaBuffer->datas[0].chunk->stride; ++ if (srcStride != (desktop_size_.width() * kBytesPerPixel)) { ++ LOG(LS_ERROR) << "Got buffer with stride different from screen stride: " ++ << srcStride ++ << " != " << (desktop_size_.width() * kBytesPerPixel); ++ return; ++ } ++ ++ uint8_t* tempFrame = static_cast(malloc(maxSize)); ++ std::memcpy(tempFrame, src, maxSize); ++ ++ ConvertRGBxToBGRxIfNeeded(tempFrame, maxSize); ++ ++ if (!current_frame_) { ++ current_frame_ = static_cast(malloc(maxSize)); ++ } ++ ++ RTC_DCHECK(current_frame_ != nullptr); ++ std::memcpy(current_frame_, tempFrame, maxSize); ++ ++ free(tempFrame); ++} ++ ++void ScreenCapturerPipeWire::ConvertRGBxToBGRxIfNeeded(uint8_t* frame, uint32_t size) ++{ ++ // If both sides decided to go with the RGBx format we need to convert it to ++ // BGRx to match color format expected by WebRTC. ++ if (spa_video_format_->format == pw_type_->video_format.RGBx) { ++ // Change color format for KDE KWin which uses RGBx and not BGRx. ++ for (uint32_t i = 0; i < size; i += 4) { ++ uint8_t tempR = frame[i]; ++ uint8_t tempB = frame[i + 2]; ++ frame[i] = tempB; ++ frame[i + 2] = tempR; ++ } ++ } ++} ++ ++guint ScreenCapturerPipeWire::SetupRequestResponseSignal( ++ const gchar* object_path, ++ GDBusSignalCallback callback) { ++ return g_dbus_connection_signal_subscribe( ++ connection_, kDesktopBusName, kRequestInterfaceName, "Response", ++ object_path, /*arg0=*/nullptr, G_DBUS_SIGNAL_FLAGS_NO_MATCH_RULE, ++ callback, this, /*user_data_free_func=*/nullptr); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnProxyRequested(GObject* /*object*/, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ GError* error = nullptr; ++ that->proxy_ = g_dbus_proxy_new_finish(result, &error); ++ if (!that->proxy_) { ++ LOG(LS_ERROR) << "Failed to create a proxy for the screen cast portal: " ++ << error->message; ++ g_error_free(error); ++ return; ++ } ++ that->connection_ = g_dbus_proxy_get_connection(that->proxy_); ++ ++ LOG(LS_INFO) << "Created proxy for the screen cast portal."; ++ that->SessionRequest(); ++} ++ ++// static ++gchar* ScreenCapturerPipeWire::PrepareSignalHandle(GDBusConnection* connection, ++ const gchar* token) { ++ gchar* sender = g_strdup(g_dbus_connection_get_unique_name(connection) + 1); ++ for (int i = 0; sender[i]; i++) { ++ if (sender[i] == '.') { ++ sender[i] = '_'; ++ } ++ } ++ ++ gchar* handle = g_strconcat(kDesktopRequestObjectPath, "/", sender, "/", ++ token, /*end of varargs*/ nullptr); ++ g_free(sender); ++ ++ return handle; ++} ++ ++void ScreenCapturerPipeWire::SessionRequest() { ++ GVariantBuilder builder; ++ gchar* variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ variant_string = ++ g_strdup_printf("webrtc_session%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "session_handle_token", ++ g_variant_new_string(variant_string)); ++ g_free(variant_string); ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string)); ++ ++ portal_handle_ = PrepareSignalHandle(connection_, variant_string); ++ session_request_signal_id_ = SetupRequestResponseSignal( ++ portal_handle_, OnSessionRequestResponseSignal); ++ g_free(variant_string); ++ ++ LOG(LS_INFO) << "Screen cast session requested."; ++ g_dbus_proxy_call( ++ proxy_, "CreateSession", g_variant_new("(a{sv})", &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, ++ reinterpret_cast(OnSessionRequested), this); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnSessionRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ GError* error = nullptr; ++ GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); ++ if (!variant) { ++ LOG(LS_ERROR) << "Failed to create a screen cast session: " ++ << error->message; ++ g_error_free(error); ++ return; ++ } ++ LOG(LS_INFO) << "Initializing the screen cast session."; ++ ++ gchar* handle = nullptr; ++ g_variant_get_child(variant, 0, "o", &handle); ++ g_variant_unref(variant); ++ if (!handle) { ++ LOG(LS_ERROR) << "Failed to initialize the screen cast session."; ++ if (that->session_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->session_request_signal_id_); ++ that->session_request_signal_id_ = 0; ++ } ++ return; ++ } ++ ++ // Support for older xdg-desktop-portal version. ++ if (g_strcmp0(that->portal_handle_, handle) != 0) { ++ if (that->session_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->session_request_signal_id_); ++ that->session_request_signal_id_ = 0; ++ } ++ ++ g_clear_pointer(&that->portal_handle_, g_free); ++ that->portal_handle_ = handle; ++ ++ that->session_request_signal_id_ = that->SetupRequestResponseSignal( ++ that->portal_handle_, OnSessionRequestResponseSignal); ++ } ++ ++ g_free(handle); ++ ++ LOG(LS_INFO) << "Subscribing to the screen cast session."; ++} ++ ++// static ++void ScreenCapturerPipeWire::OnSessionRequestResponseSignal( ++ GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ LOG(LS_INFO) << "Received response for the screen cast session subscription."; ++ ++ guint32 portal_response; ++ GVariant* response_data; ++ g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); ++ g_variant_lookup(response_data, "session_handle", "s", ++ &that->session_handle_); ++ g_variant_unref(response_data); ++ ++ if (!that->session_handle_ || portal_response) { ++ LOG(LS_ERROR) << "Failed to request the screen cast session subscription."; ++ return; ++ } ++ ++ that->SourcesRequest(); ++} ++ ++void ScreenCapturerPipeWire::SourcesRequest() { ++ GVariantBuilder builder; ++ gchar* variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ // We want to record monitor content. ++ g_variant_builder_add(&builder, "{sv}", "types", g_variant_new_uint32(1)); ++ // We don't want to allow selection of multiple sources. ++ g_variant_builder_add(&builder, "{sv}", "multiple", ++ g_variant_new_boolean(false)); ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string)); ++ ++ sources_handle_ = PrepareSignalHandle(connection_, variant_string); ++ sources_request_signal_id_ = SetupRequestResponseSignal( ++ sources_handle_, OnSourcesRequestResponseSignal); ++ g_free(variant_string); ++ ++ LOG(LS_INFO) << "Requesting sources from the screen cast session."; ++ g_dbus_proxy_call( ++ proxy_, "SelectSources", ++ g_variant_new("(oa{sv})", session_handle_, &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, ++ reinterpret_cast(OnSourcesRequested), this); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnSourcesRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ GError* error = nullptr; ++ GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); ++ if (!variant) { ++ LOG(LS_ERROR) << "Failed to request the sources: " << error->message; ++ g_error_free(error); ++ return; ++ } ++ ++ LOG(LS_INFO) << "Sources requested from the screen cast session."; ++ ++ gchar* handle = nullptr; ++ g_variant_get_child(variant, 0, "o", &handle); ++ g_variant_unref(variant); ++ if (!handle) { ++ LOG(LS_ERROR) << "Failed to initialize the screen cast session."; ++ if (that->sources_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->sources_request_signal_id_); ++ that->sources_request_signal_id_ = 0; ++ } ++ return; ++ } ++ ++ // Support for older xdg-desktop-portal version. ++ if (g_strcmp0(that->sources_handle_, handle) != 0) { ++ if (that->sources_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->sources_request_signal_id_); ++ that->sources_request_signal_id_ = 0; ++ } ++ ++ g_clear_pointer(&that->sources_handle_, g_free); ++ that->sources_handle_ = handle; ++ ++ that->sources_request_signal_id_ = that->SetupRequestResponseSignal( ++ that->sources_handle_, OnSourcesRequestResponseSignal); ++ } ++ ++ g_free(handle); ++ ++ LOG(LS_INFO) << "Subscribed to sources signal."; ++} ++ ++// static ++void ScreenCapturerPipeWire::OnSourcesRequestResponseSignal( ++ GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ LOG(LS_INFO) << "Received sources signal from session."; ++ that->StartRequest(); ++} ++ ++void ScreenCapturerPipeWire::StartRequest() { ++ GVariantBuilder builder; ++ gchar* variant_string; ++ ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ variant_string = g_strdup_printf("webrtc%d", g_random_int_range(0, G_MAXINT)); ++ g_variant_builder_add(&builder, "{sv}", "handle_token", ++ g_variant_new_string(variant_string)); ++ ++ start_handle_ = PrepareSignalHandle(connection_, variant_string); ++ start_request_signal_id_ = ++ SetupRequestResponseSignal(start_handle_, OnStartRequestResponseSignal); ++ g_free(variant_string); ++ ++ // "Identifier for the application window", this is Wayland, so not "x11:...". ++ const gchar parent_window[] = ""; ++ ++ LOG(LS_INFO) << "Starting the screen cast session."; ++ g_dbus_proxy_call( ++ proxy_, "Start", ++ g_variant_new("(osa{sv})", session_handle_, parent_window, &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*cancellable=*/nullptr, ++ reinterpret_cast(OnStartRequested), this); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnStartRequested(GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ GError* error = nullptr; ++ GVariant* variant = g_dbus_proxy_call_finish(that->proxy_, result, &error); ++ if (!variant) { ++ LOG(LS_ERROR) << "Failed to start the screen cast session: " ++ << error->message; ++ g_error_free(error); ++ return; ++ } ++ ++ LOG(LS_INFO) << "Initializing the start of the screen cast session."; ++ ++ gchar* handle = nullptr; ++ g_variant_get_child(variant, 0, "o", &handle); ++ g_variant_unref(variant); ++ if (!handle) { ++ LOG(LS_ERROR) ++ << "Failed to initialize the start of the screen cast session."; ++ if (that->start_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->start_request_signal_id_); ++ that->start_request_signal_id_ = 0; ++ } ++ return; ++ } ++ ++ // Support for older xdg-desktop-portal version. ++ if (g_strcmp0(that->start_handle_, handle) != 0) { ++ if (that->start_request_signal_id_) { ++ g_dbus_connection_signal_unsubscribe(connection, ++ that->start_request_signal_id_); ++ that->start_request_signal_id_ = 0; ++ } ++ ++ g_clear_pointer(&that->start_handle_, g_free); ++ that->start_handle_ = handle; ++ ++ that->start_request_signal_id_ = that->SetupRequestResponseSignal( ++ that->start_handle_, OnStartRequestResponseSignal); ++ } ++ ++ g_free(handle); ++ ++ LOG(LS_INFO) << "Subscribed to the start signal."; ++} ++ ++// static ++void ScreenCapturerPipeWire::OnStartRequestResponseSignal( ++ GDBusConnection* connection, ++ const gchar* sender_name, ++ const gchar* object_path, ++ const gchar* interface_name, ++ const gchar* signal_name, ++ GVariant* parameters, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ LOG(LS_INFO) << "Start signal received."; ++ guint32 portal_response; ++ GVariant* response_data; ++ GVariantIter* iter = nullptr; ++ g_variant_get(parameters, "(u@a{sv})", &portal_response, &response_data); ++ if (portal_response || !response_data) { ++ LOG(LS_ERROR) << "Failed to start the screen cast session."; ++ return; ++ } ++ ++ // Array of PipeWire streams. See ++ // https://github.com/flatpak/xdg-desktop-portal/blob/master/data/org.freedesktop.portal.ScreenCast.xml ++ // documentation for . ++ if (g_variant_lookup(response_data, "streams", "a(ua{sv})", &iter)) { ++ GVariant* variant; ++ ++ while (g_variant_iter_next(iter, "@(ua{sv})", &variant)) { ++ guint32 stream_id; ++ gint32 width; ++ gint32 height; ++ GVariant* options; ++ ++ g_variant_get(variant, "(u@a{sv})", &stream_id, &options); ++ RTC_DCHECK(options != nullptr); ++ ++ g_variant_lookup(options, "size", "(ii)", &width, &height); ++ ++ that->desktop_size_.set(width, height); ++ ++ g_variant_unref(options); ++ g_variant_unref(variant); ++ } ++ } ++ g_variant_iter_free(iter); ++ g_variant_unref(response_data); ++ ++ that->OpenPipeWireRemote(); ++} ++ ++void ScreenCapturerPipeWire::OpenPipeWireRemote() { ++ GVariantBuilder builder; ++ g_variant_builder_init(&builder, G_VARIANT_TYPE_VARDICT); ++ ++ LOG(LS_INFO) << "Opening the PipeWire remote."; ++ ++ g_dbus_proxy_call_with_unix_fd_list( ++ proxy_, "OpenPipeWireRemote", ++ g_variant_new("(oa{sv})", session_handle_, &builder), ++ G_DBUS_CALL_FLAGS_NONE, /*timeout=*/-1, /*fd_list=*/nullptr, ++ /*cancellable=*/nullptr, ++ reinterpret_cast(OnOpenPipeWireRemoteRequested), ++ this); ++} ++ ++// static ++void ScreenCapturerPipeWire::OnOpenPipeWireRemoteRequested( ++ GDBusConnection* connection, ++ GAsyncResult* result, ++ gpointer user_data) { ++ ScreenCapturerPipeWire* that = ++ static_cast(user_data); ++ RTC_DCHECK(that); ++ ++ GError* error = nullptr; ++ GUnixFDList* outlist = nullptr; ++ GVariant* variant = g_dbus_proxy_call_with_unix_fd_list_finish( ++ that->proxy_, &outlist, result, &error); ++ if (!variant) { ++ LOG(LS_ERROR) << "Failed to open the PipeWire remote: " << error->message; ++ g_error_free(error); ++ return; ++ } ++ ++ gint32 index; ++ g_variant_get(variant, "(h)", &index); ++ ++ if ((that->pw_fd_ = g_unix_fd_list_get(outlist, index, &error)) == -1) { ++ LOG(LS_ERROR) << "Failed to get file descriptor from the list: " ++ << error->message; ++ g_error_free(error); ++ g_variant_unref(variant); ++ return; ++ } ++ ++ g_variant_unref(variant); ++ g_object_unref(outlist); ++ ++ that->InitPipeWire(); ++ LOG(LS_INFO) << "PipeWire remote opened."; ++} ++ ++void ScreenCapturerPipeWire::Start(Callback* callback) { ++ RTC_DCHECK(!callback_); ++ RTC_DCHECK(callback); ++ ++ callback_ = callback; ++} ++ ++void ScreenCapturerPipeWire::Stop() { ++ callback_ = nullptr; ++ if (pw_main_loop_) { ++ pw_thread_loop_stop(pw_main_loop_); ++ } ++} ++ ++void ScreenCapturerPipeWire::CaptureFrame() { ++ if (!current_frame_) { ++ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); ++ return; ++ } ++ ++ std::unique_ptr result(new BasicDesktopFrame(desktop_size_)); ++ result->CopyPixelsFrom( ++ current_frame_, (desktop_size_.width() * kBytesPerPixel), ++ DesktopRect::MakeWH(desktop_size_.width(), desktop_size_.height())); ++ if (!result) { ++ callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); ++ return; ++ } ++ callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); ++} ++ ++bool ScreenCapturerPipeWire::GetSourceList(SourceList* sources) { ++ RTC_DCHECK(sources->size() == 0); ++ sources->push_back({0}); ++ return true; ++} ++ ++bool ScreenCapturerPipeWire::SelectSource(SourceId id) { ++ // TODO ++ return true; ++} ++ ++} // namespace ++ ++// static ++std::unique_ptr ++ScreenCapturerPipeWire::CreateRawScreenCapturer( ++ const DesktopCaptureOptions& options) { ++ std::unique_ptr capturer( ++ new ScreenCapturerPipeWire()); ++ ++ return std::move(capturer); ++} ++ ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc +index 7692f8b70181..ba137853456c 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/screen_capturer_x11.cc +@@ -34,18 +34,20 @@ + namespace webrtc { + namespace { + +-// A class to perform video frame capturing for Linux. ++// A class to perform video frame capturing for Linux on X11. + // + // If XDamage is used, this class sets DesktopFrame::updated_region() according + // to the areas reported by XDamage. Otherwise this class does not detect + // DesktopFrame::updated_region(), the field is always set to the entire frame + // rectangle. ScreenCapturerDifferWrapper should be used if that functionality + // is necessary. +-class ScreenCapturerLinux : public DesktopCapturer, ++class ScreenCapturerX11 : public DesktopCapturer, + public SharedXDisplay::XEventHandler { + public: +- ScreenCapturerLinux(); +- ~ScreenCapturerLinux() override; ++ ScreenCapturerX11(); ++ ~ScreenCapturerX11() override; ++ ++ static std::unique_ptr CreateRawScreenCapturer(const DesktopCaptureOptions& options); + + // TODO(ajwong): Do we really want this to be synchronous? + bool Init(const DesktopCaptureOptions& options); +@@ -118,14 +120,13 @@ class ScreenCapturerLinux : public DesktopCapturer, + // current with the last buffer used. + DesktopRegion last_invalid_region_; + +- RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerLinux); ++ RTC_DISALLOW_COPY_AND_ASSIGN(ScreenCapturerX11); + }; +- +-ScreenCapturerLinux::ScreenCapturerLinux() { ++ScreenCapturerX11::ScreenCapturerX11() { + helper_.SetLogGridSize(4); + } + +-ScreenCapturerLinux::~ScreenCapturerLinux() { ++ScreenCapturerX11::~ScreenCapturerX11() { + options_.x_display()->RemoveEventHandler(ConfigureNotify, this); + if (use_damage_) { + options_.x_display()->RemoveEventHandler( +@@ -134,7 +135,7 @@ ScreenCapturerLinux::~ScreenCapturerLinux() { + DeinitXlib(); + } + +-bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) { ++bool ScreenCapturerX11::Init(const DesktopCaptureOptions& options) { + options_ = options; + + root_window_ = RootWindow(display(), DefaultScreen(display())); +@@ -177,7 +178,7 @@ bool ScreenCapturerLinux::Init(const DesktopCaptureOptions& options) { + return true; + } + +-void ScreenCapturerLinux::InitXDamage() { ++void ScreenCapturerX11::InitXDamage() { + // Our use of XDamage requires XFixes. + if (!has_xfixes_) { + return; +@@ -218,18 +219,18 @@ void ScreenCapturerLinux::InitXDamage() { + LOG(LS_INFO) << "Using XDamage extension."; + } + +-void ScreenCapturerLinux::Start(Callback* callback) { ++void ScreenCapturerX11::Start(Callback* callback) { + RTC_DCHECK(!callback_); + RTC_DCHECK(callback); + + callback_ = callback; + } + +-void ScreenCapturerLinux::Stop() { ++void ScreenCapturerX11::Stop() { + callback_ = NULL; + } + +-void ScreenCapturerLinux::CaptureFrame() { ++void ScreenCapturerX11::CaptureFrame() { + int64_t capture_start_time_nanos = rtc::TimeNanos(); + + queue_.MoveToNextFrame(); +@@ -243,6 +244,7 @@ void ScreenCapturerLinux::CaptureFrame() { + // in a good shape. + if (!x_server_pixel_buffer_.is_initialized()) { + // We failed to initialize pixel buffer. ++ LOG(LS_ERROR) << "Pixel buffer is not initialized."; + callback_->OnCaptureResult(Result::ERROR_PERMANENT, nullptr); + return; + } +@@ -258,6 +260,7 @@ void ScreenCapturerLinux::CaptureFrame() { + + std::unique_ptr result = CaptureScreen(); + if (!result) { ++ LOG(LS_WARNING) << "Temporarily failed to capture screen."; + callback_->OnCaptureResult(Result::ERROR_TEMPORARY, nullptr); + return; + } +@@ -268,19 +271,19 @@ void ScreenCapturerLinux::CaptureFrame() { + callback_->OnCaptureResult(Result::SUCCESS, std::move(result)); + } + +-bool ScreenCapturerLinux::GetSourceList(SourceList* sources) { ++bool ScreenCapturerX11::GetSourceList(SourceList* sources) { + RTC_DCHECK(sources->size() == 0); + // TODO(jiayl): implement screen enumeration. + sources->push_back({0}); + return true; + } + +-bool ScreenCapturerLinux::SelectSource(SourceId id) { ++bool ScreenCapturerX11::SelectSource(SourceId id) { + // TODO(jiayl): implement screen selection. + return true; + } + +-bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) { ++bool ScreenCapturerX11::HandleXEvent(const XEvent& event) { + if (use_damage_ && (event.type == damage_event_base_ + XDamageNotify)) { + const XDamageNotifyEvent* damage_event = + reinterpret_cast(&event); +@@ -295,7 +298,7 @@ bool ScreenCapturerLinux::HandleXEvent(const XEvent& event) { + return false; + } + +-std::unique_ptr ScreenCapturerLinux::CaptureScreen() { ++std::unique_ptr ScreenCapturerX11::CaptureScreen() { + std::unique_ptr frame = queue_.current_frame()->Share(); + RTC_DCHECK(x_server_pixel_buffer_.window_size().equals(frame->size())); + +@@ -345,25 +348,26 @@ std::unique_ptr ScreenCapturerLinux::CaptureScreen() { + // Doing full-screen polling, or this is the first capture after a + // screen-resolution change. In either case, need a full-screen capture. + DesktopRect screen_rect = DesktopRect::MakeSize(frame->size()); +- x_server_pixel_buffer_.CaptureRect(screen_rect, frame.get()); ++ if (!x_server_pixel_buffer_.CaptureRect(screen_rect, frame.get())) ++ return nullptr; + updated_region->SetRect(screen_rect); + } + + return std::move(frame); + } + +-void ScreenCapturerLinux::ScreenConfigurationChanged() { ++void ScreenCapturerX11::ScreenConfigurationChanged() { + // Make sure the frame buffers will be reallocated. + queue_.Reset(); + + helper_.ClearInvalidRegion(); + if (!x_server_pixel_buffer_.Init(display(), DefaultRootWindow(display()))) { + LOG(LS_ERROR) << "Failed to initialize pixel buffer after screen " +- "configuration change."; ++ "configuration change."; + } + } + +-void ScreenCapturerLinux::SynchronizeFrame() { ++void ScreenCapturerX11::SynchronizeFrame() { + // Synchronize the current buffer with the previous one since we do not + // capture the entire desktop. Note that encoder may be reading from the + // previous buffer at this time so thread access complaints are false +@@ -383,7 +387,7 @@ void ScreenCapturerLinux::SynchronizeFrame() { + } + } + +-void ScreenCapturerLinux::DeinitXlib() { ++void ScreenCapturerX11::DeinitXlib() { + if (gc_) { + XFreeGC(display(), gc_); + gc_ = nullptr; +@@ -407,17 +411,17 @@ void ScreenCapturerLinux::DeinitXlib() { + } // namespace + + // static +-std::unique_ptr DesktopCapturer::CreateRawScreenCapturer( ++std::unique_ptr ScreenCapturerX11::CreateRawScreenCapturer( + const DesktopCaptureOptions& options) { + if (!options.x_display()) + return nullptr; + +- std::unique_ptr capturer(new ScreenCapturerLinux()); ++ std::unique_ptr capturer(new ScreenCapturerX11()); + if (!capturer.get()->Init(options)) { + return nullptr; + } + +- return std::unique_ptr(capturer.release()); ++ return std::move(capturer); + } + + } // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc +new file mode 100644 +index 000000000000..fde7750c878b +--- /dev/null ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_linux.cc +@@ -0,0 +1,40 @@ ++/* ++ * Copyright (c) 2018 The WebRTC project authors. All Rights Reserved. ++ * ++ * Use of this source code is governed by a BSD-style license ++ * that can be found in the LICENSE file in the root of the source ++ * tree. An additional intellectual property rights grant can be found ++ * in the file PATENTS. All contributing project authors may ++ * be found in the AUTHORS file in the root of the source tree. ++ */ ++ ++#include "webrtc/modules/desktop_capture/desktop_capture_options.h" ++#include "webrtc/modules/desktop_capture/desktop_capturer.h" ++ ++#if defined(USE_PIPEWIRE) ++#include "webrtc/modules/desktop_capture/window_capturer_null.cc" ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++#include "webrtc/modules/desktop_capture/window_capturer_x11.cc" ++#endif // defined(USE_X11) ++ ++namespace webrtc { ++ ++// static ++std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( ++ const DesktopCaptureOptions& options) { ++#if defined(USE_PIPEWIRE) ++ if (DesktopCapturer::IsRunningUnderWayland()) { ++ return std::unique_ptr(new WindowCapturerNull()); ++ } ++#endif // defined(USE_PIPEWIRE) ++ ++#if defined(USE_X11) ++ return WindowCapturerX11::CreateRawWindowCapturer(options); ++#endif // defined(USE_X11) ++ ++ return nullptr; ++} ++ ++} // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc +index 0f0159bb5f16..f6ef0639a586 100755 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_null.cc +@@ -67,10 +67,12 @@ void WindowCapturerNull::CaptureFrame() { + + } // namespace + ++#if not defined(USE_PIPEWIRE) + // static + std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + return std::unique_ptr(new WindowCapturerNull()); + } ++#endif // not defined(USE_PIPEWIRE) + + } // namespace webrtc +diff --git a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc +index fecb329db546..d478604aa3de 100644 +--- a/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc ++++ b/media/webrtc/trunk/webrtc/modules/desktop_capture/window_capturer_x11.cc +@@ -32,11 +32,13 @@ namespace webrtc { + + namespace { + +-class WindowCapturerLinux : public DesktopCapturer, ++class WindowCapturerX11 : public DesktopCapturer, + public SharedXDisplay::XEventHandler { + public: +- WindowCapturerLinux(const DesktopCaptureOptions& options); +- ~WindowCapturerLinux() override; ++ WindowCapturerX11(const DesktopCaptureOptions& options); ++ ~WindowCapturerX11() override; ++ ++ static std::unique_ptr CreateRawWindowCapturer(const DesktopCaptureOptions& options); + + // DesktopCapturer interface. + void Start(Callback* callback) override; +@@ -78,10 +80,10 @@ class WindowCapturerLinux : public DesktopCapturer, + ::Window selected_window_ = 0; + XServerPixelBuffer x_server_pixel_buffer_; + +- RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerLinux); ++ RTC_DISALLOW_COPY_AND_ASSIGN(WindowCapturerX11); + }; + +-WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options) ++WindowCapturerX11::WindowCapturerX11(const DesktopCaptureOptions& options) + : x_display_(options.x_display()) { + // Create Atoms so we don't need to do it every time they are used. + wm_state_atom_ = XInternAtom(display(), "WM_STATE", True); +@@ -102,11 +104,11 @@ WindowCapturerLinux::WindowCapturerLinux(const DesktopCaptureOptions& options) + x_display_->AddEventHandler(ConfigureNotify, this); + } + +-WindowCapturerLinux::~WindowCapturerLinux() { ++WindowCapturerX11::~WindowCapturerX11() { + x_display_->RemoveEventHandler(ConfigureNotify, this); + } + +-bool WindowCapturerLinux::GetSourceList(SourceList* sources) { ++bool WindowCapturerX11::GetSourceList(SourceList* sources) { + SourceList result; + + XErrorTrap error_trap(display()); +@@ -159,7 +161,7 @@ bool WindowCapturerLinux::GetSourceList(SourceList* sources) { + return true; + } + +-bool WindowCapturerLinux::SelectSource(SourceId id) { ++bool WindowCapturerX11::SelectSource(SourceId id) { + if (!x_server_pixel_buffer_.Init(display(), id)) + return false; + +@@ -180,7 +182,7 @@ bool WindowCapturerLinux::SelectSource(SourceId id) { + return true; + } + +-bool WindowCapturerLinux::FocusOnSelectedSource() { ++bool WindowCapturerX11::FocusOnSelectedSource() { + if (!selected_window_) + return false; + +@@ -229,18 +231,18 @@ bool WindowCapturerLinux::FocusOnSelectedSource() { + return true; + } + +-void WindowCapturerLinux::Start(Callback* callback) { ++void WindowCapturerX11::Start(Callback* callback) { + assert(!callback_); + assert(callback); + + callback_ = callback; + } + +-void WindowCapturerLinux::Stop() { ++void WindowCapturerX11::Stop() { + callback_ = NULL; + } + +-void WindowCapturerLinux::CaptureFrame() { ++void WindowCapturerX11::CaptureFrame() { + x_display_->ProcessPendingXEvents(); + + if (!x_server_pixel_buffer_.IsWindowValid()) { +@@ -274,7 +276,7 @@ void WindowCapturerLinux::CaptureFrame() { + callback_->OnCaptureResult(Result::SUCCESS, std::move(frame)); + } + +-bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { ++bool WindowCapturerX11::HandleXEvent(const XEvent& event) { + if (event.type == ConfigureNotify) { + XConfigureEvent xce = event.xconfigure; + if (!DesktopSize(xce.width, xce.height).equals( +@@ -288,7 +290,7 @@ bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { + return false; + } + +-::Window WindowCapturerLinux::GetApplicationWindow(::Window window) { ++::Window WindowCapturerX11::GetApplicationWindow(::Window window) { + // Get WM_STATE property of the window. + XWindowProperty window_state(display(), window, wm_state_atom_); + +@@ -326,7 +328,7 @@ bool WindowCapturerLinux::HandleXEvent(const XEvent& event) { + return app_window; + } + +-bool WindowCapturerLinux::IsDesktopElement(::Window window) { ++bool WindowCapturerX11::IsDesktopElement(::Window window) { + if (window == 0) + return false; + +@@ -361,7 +363,7 @@ bool WindowCapturerLinux::IsDesktopElement(::Window window) { + return result; + } + +-bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) { ++bool WindowCapturerX11::GetWindowTitle(::Window window, std::string* title) { + int status; + bool result = false; + XTextProperty window_name; +@@ -392,7 +394,7 @@ bool WindowCapturerLinux::GetWindowTitle(::Window window, std::string* title) { + + } // namespace + +-int WindowCapturerLinux::GetWindowProcessID(::Window window) { ++int WindowCapturerX11::GetWindowProcessID(::Window window) { + // Get _NET_WM_PID property of the window. + Atom process_atom = XInternAtom(display(), "_NET_WM_PID", True); + XWindowProperty process_id(display(), window, process_atom); +@@ -401,11 +403,11 @@ int WindowCapturerLinux::GetWindowProcessID(::Window window) { + } + + // static +-std::unique_ptr DesktopCapturer::CreateRawWindowCapturer( ++std::unique_ptr WindowCapturerX11::CreateRawWindowCapturer( + const DesktopCaptureOptions& options) { + if (!options.x_display()) + return nullptr; +- return std::unique_ptr(new WindowCapturerLinux(options)); ++ return std::unique_ptr(new WindowCapturerX11(options)); + } + + } // namespace webrtc diff --git a/firefox-x11.desktop b/firefox-x11.desktop new file mode 100644 index 0000000..4d1dca5 --- /dev/null +++ b/firefox-x11.desktop @@ -0,0 +1,231 @@ +[Desktop Entry] +Version=1.0 +Name=Firefox on X11 +GenericName=Web Browser +Comment=Browse the Web +Exec=firefox-x11 --name firefox-x11 %u +Icon=firefox +Terminal=false +Type=Application +MimeType=text/html;text/xml;application/xhtml+xml;application/vnd.mozilla.xul+xml;text/mml;x-scheme-handler/http;x-scheme-handler/https; +StartupNotify=true +Categories=Network;WebBrowser; +Keywords=web;browser;internet; +Actions=new-window;new-private-window; + +[Desktop Action new-window] +Name=Open a New Window +Name[ach]=Dirica manyen +Name[af]=Nuwe venster +Name[an]=Nueva finestra +Name[ar]=نافذة جديدة +Name[as]=নতুন উইন্ডো +Name[ast]=Ventana nueva +Name[az]=Yeni Pəncərə +Name[be]=Новае акно +Name[bg]=Нов прозорец +Name[bn-BD]=নতুন উইন্ডো (N) +Name[bn-IN]=নতুন উইন্ডো +Name[br]=Prenestr nevez +Name[brx]=गोदान उइन्ड'(N) +Name[bs]=Novi prozor +Name[ca]=Finestra nova +Name[cak]=K'ak'a' tzuwäch +Name[cs]=Nové okno +Name[cy]=Ffenestr Newydd +Name[da]=Nyt vindue +Name[de]=Neues Fenster +Name[dsb]=Nowe wokno +Name[el]=Νέο παράθυρο +Name[en-GB]=New Window +Name[en-US]=New Window +Name[en-ZA]=New Window +Name[eo]=Nova fenestro +Name[es-AR]=Nueva ventana +Name[es-CL]=Nueva ventana +Name[es-ES]=Nueva ventana +Name[es-MX]=Nueva ventana +Name[et]=Uus aken +Name[eu]=Leiho berria +Name[fa]=پنجره جدید‌ +Name[ff]=Henorde Hesere +Name[fi]=Uusi ikkuna +Name[fr]=Nouvelle fenêtre +Name[fy-NL]=Nij finster +Name[ga-IE]=Fuinneog Nua +Name[gd]=Uinneag ùr +Name[gl]=Nova xanela +Name[gn]=Ovetã pyahu +Name[gu-IN]=નવી વિન્ડો +Name[he]=חלון חדש +Name[hi-IN]=नया विंडो +Name[hr]=Novi prozor +Name[hsb]=Nowe wokno +Name[hu]=Új ablak +Name[hy-AM]=Նոր Պատուհան +Name[id]=Jendela Baru +Name[is]=Nýr gluggi +Name[it]=Nuova finestra +Name[ja]=新しいウィンドウ +Name[ja-JP-mac]=新規ウインドウ +Name[ka]=ახალი ფანჯარა +Name[kk]=Жаңа терезе +Name[km]=បង្អួច​​​ថ្មី +Name[kn]=ಹೊಸ ಕಿಟಕಿ +Name[ko]=새 창 +Name[kok]=नवें जनेल +Name[ks]=نئئ وِنڈو +Name[lij]=Neuvo barcon +Name[lo]=ຫນ້າຕ່າງໃຫມ່ +Name[lt]=Naujas langas +Name[ltg]=Jauns lūgs +Name[lv]=Jauns logs +Name[mai]=नव विंडो +Name[mk]=Нов прозорец +Name[ml]=പുതിയ ജാലകം +Name[mr]=नवीन पटल +Name[ms]=Tetingkap Baru +Name[my]=ဝင်းဒိုးအသစ် +Name[nb-NO]=Nytt vindu +Name[ne-NP]=नयाँ सञ्झ्याल +Name[nl]=Nieuw venster +Name[nn-NO]=Nytt vindauge +Name[or]=ନୂତନ ୱିଣ୍ଡୋ +Name[pa-IN]=ਨਵੀਂ ਵਿੰਡੋ +Name[pl]=Nowe okno +Name[pt-BR]=Nova janela +Name[pt-PT]=Nova janela +Name[rm]=Nova fanestra +Name[ro]=Fereastră nouă +Name[ru]=Новое окно +Name[sat]=नावा विंडो (N) +Name[si]=නව කවුළුවක් +Name[sk]=Nové okno +Name[sl]=Novo okno +Name[son]=Zanfun taaga +Name[sq]=Dritare e Re +Name[sr]=Нови прозор +Name[sv-SE]=Nytt fönster +Name[ta]=புதிய சாளரம் +Name[te]=కొత్త విండో +Name[th]=หน้าต่างใหม่ +Name[tr]=Yeni pencere +Name[tsz]=Eraatarakua jimpani +Name[uk]=Нове вікно +Name[ur]=نیا دریچہ +Name[uz]=Yangi oyna +Name[vi]=Cửa sổ mới +Name[wo]=Palanteer bu bees +Name[xh]=Ifestile entsha +Name[zh-CN]=新建窗口 +Name[zh-TW]=開新視窗 +Exec=firefox-x11 --name firefox-x11 --new-window %u + +[Desktop Action new-private-window] +Name=Open a New Private Window +Name[ach]=Dirica manyen me mung +Name[af]=Nuwe privaatvenster +Name[an]=Nueva finestra privada +Name[ar]=نافذة خاصة جديدة +Name[as]=নতুন ব্যক্তিগত উইন্ডো +Name[ast]=Ventana privada nueva +Name[az]=Yeni Məxfi Pəncərə +Name[be]=Новае акно адасаблення +Name[bg]=Нов прозорец за поверително сърфиране +Name[bn-BD]=নতুন ব্যক্তিগত উইন্ডো +Name[bn-IN]=নতুন ব্যক্তিগত উইন্ডো +Name[br]=Prenestr merdeiñ prevez nevez +Name[brx]=गोदान प्राइभेट उइन्ड' +Name[bs]=Novi privatni prozor +Name[ca]=Finestra privada nova +Name[cak]=K'ak'a' ichinan tzuwäch +Name[cs]=Nové anonymní okno +Name[cy]=Ffenestr Breifat Newydd +Name[da]=Nyt privat vindue +Name[de]=Neues privates Fenster +Name[dsb]=Nowe priwatne wokno +Name[el]=Νέο παράθυρο ιδιωτικής περιήγησης +Name[en-GB]=New Private Window +Name[en-US]=New Private Window +Name[en-ZA]=New Private Window +Name[eo]=Nova privata fenestro +Name[es-AR]=Nueva ventana privada +Name[es-CL]=Nueva ventana privada +Name[es-ES]=Nueva ventana privada +Name[es-MX]=Nueva ventana privada +Name[et]=Uus privaatne aken +Name[eu]=Leiho pribatu berria +Name[fa]=پنجره ناشناس جدید +Name[ff]=Henorde Suturo Hesere +Name[fi]=Uusi yksityinen ikkuna +Name[fr]=Nouvelle fenêtre de navigation privée +Name[fy-NL]=Nij priveefinster +Name[ga-IE]=Fuinneog Nua Phríobháideach +Name[gd]=Uinneag phrìobhaideach ùr +Name[gl]=Nova xanela privada +Name[gn]=Ovetã ñemi pyahu +Name[gu-IN]=નવી ખાનગી વિન્ડો +Name[he]=חלון פרטי חדש +Name[hi-IN]=नयी निजी विंडो +Name[hr]=Novi privatni prozor +Name[hsb]=Nowe priwatne wokno +Name[hu]=Új privát ablak +Name[hy-AM]=Սկսել Գաղտնի դիտարկում +Name[id]=Jendela Mode Pribadi Baru +Name[is]=Nýr huliðsgluggi +Name[it]=Nuova finestra anonima +Name[ja]=新しいプライベートウィンドウ +Name[ja-JP-mac]=新規プライベートウインドウ +Name[ka]=ახალი პირადი ფანჯარა +Name[kk]=Жаңа жекелік терезе +Name[km]=បង្អួច​ឯកជន​ថ្មី +Name[kn]=ಹೊಸ ಖಾಸಗಿ ಕಿಟಕಿ +Name[ko]=새 사생활 보호 모드 +Name[kok]=नवो खाजगी विंडो +Name[ks]=نْو پرایوٹ وینڈو& +Name[lij]=Neuvo barcon privou +Name[lo]=ເປີດຫນ້າຕ່າງສວນຕົວຂື້ນມາໃຫມ່ +Name[lt]=Naujas privataus naršymo langas +Name[ltg]=Jauns privatais lūgs +Name[lv]=Jauns privātais logs +Name[mai]=नया निज विंडो (W) +Name[mk]=Нов приватен прозорец +Name[ml]=പുതിയ സ്വകാര്യ ജാലകം +Name[mr]=नवीन वैयक्तिक पटल +Name[ms]=Tetingkap Persendirian Baharu +Name[my]=New Private Window +Name[nb-NO]=Nytt privat vindu +Name[ne-NP]=नयाँ निजी सञ्झ्याल +Name[nl]=Nieuw privévenster +Name[nn-NO]=Nytt privat vindauge +Name[or]=ନୂତନ ବ୍ୟକ୍ତିଗତ ୱିଣ୍ଡୋ +Name[pa-IN]=ਨਵੀਂ ਪ੍ਰਾਈਵੇਟ ਵਿੰਡੋ +Name[pl]=Nowe okno prywatne +Name[pt-BR]=Nova janela privativa +Name[pt-PT]=Nova janela privada +Name[rm]=Nova fanestra privata +Name[ro]=Fereastră privată nouă +Name[ru]=Новое приватное окно +Name[sat]=नावा निजेराक् विंडो (W ) +Name[si]=නව පුද්ගලික කවුළුව (W) +Name[sk]=Nové okno v režime Súkromné prehliadanie +Name[sl]=Novo zasebno okno +Name[son]=Sutura zanfun taaga +Name[sq]=Dritare e Re Private +Name[sr]=Нови приватан прозор +Name[sv-SE]=Nytt privat fönster +Name[ta]=புதிய தனிப்பட்ட சாளரம் +Name[te]=కొత్త ఆంతరంగిక విండో +Name[th]=หน้าต่างส่วนตัวใหม่ +Name[tr]=Yeni gizli pencere +Name[tsz]=Juchiiti eraatarakua jimpani +Name[uk]=Приватне вікно +Name[ur]=نیا نجی دریچہ +Name[uz]=Yangi maxfiy oyna +Name[vi]=Cửa sổ riêng tư mới +Name[wo]=Panlanteeru biir bu bees +Name[xh]=Ifestile yangasese entsha +Name[zh-CN]=新建隐私浏览窗口 +Name[zh-TW]=新增隱私視窗 +Exec=firefox-x11 --private-window --name firefox-x11 %u + diff --git a/firefox-x11.sh.in b/firefox-x11.sh.in new file mode 100644 index 0000000..b8e42a8 --- /dev/null +++ b/firefox-x11.sh.in @@ -0,0 +1,7 @@ +#!/bin/bash +# +# Run Firefox on X11 backend +# + +export GDK_BACKEND=x11 +exec /usr/bin/firefox "$@" diff --git a/firefox.sh.in b/firefox.sh.in index f149564..adf98e9 100644 --- a/firefox.sh.in +++ b/firefox.sh.in @@ -67,6 +67,15 @@ MOZ_PROGRAM="$MOZ_DIST_BIN/$MOZ_FIREFOX_FILE" MOZ_LAUNCHER="$MOZ_DIST_BIN/run-mozilla.sh" ## +## Enable X11 backend by default? +## +if __DEFAULT_X11__; then + if ! [ "$GDK_BACKEND" ]; then + export GDK_BACKEND=x11 + fi +fi + +## ## Set MOZ_GRE_CONF ## MOZ_GRE_CONF=/etc/gre.d/gre.conf @@ -239,10 +248,6 @@ do esac done -if ! [ "$GDK_BACKEND" ]; then - export GDK_BACKEND=x11 -fi - # Run the browser debugging=0 if [ $debugging = 1 ] @@ -250,5 +255,4 @@ then echo $MOZ_LAUNCHER $script_args $MOZ_PROGRAM "$@" fi - exec $MOZ_LAUNCHER $script_args $MOZ_PROGRAM "$@" diff --git a/firefox.spec b/firefox.spec index 73dd672..584b1f8 100644 --- a/firefox.spec +++ b/firefox.spec @@ -1,12 +1,10 @@ # Use system nspr/nss? %global system_nss 1 -# Wayland backend is not finished yet, see -# https://bugzilla.mozilla.org/show_bug.cgi?id=635134 -# for details. -# -# Build with Wayland Gtk+ backend? -%global wayland_backend 1 +# Make Wayland backend default? +%if 0%{?fedora} > 29 +%global wayland_backend_default 1 +%endif # Use system sqlite? %global system_sqlite 0 @@ -94,7 +92,7 @@ Summary: Mozilla Firefox Web browser Name: firefox Version: 62.0.3 -Release: 1%{?pre_tag}%{?dist} +Release: 2%{?pre_tag}%{?dist} URL: https://www.mozilla.org/firefox/ License: MPLv1.1 or GPLv2+ or LGPLv2+ Source0: https://hg.mozilla.org/releases/mozilla-release/archive/firefox-%{version}%{?pre_version}.source.tar.xz @@ -112,6 +110,8 @@ Source26: distribution.ini Source27: google-api-key Source28: firefox-wayland.sh.in Source29: firefox-wayland.desktop +Source30: firefox-x11.sh.in +Source31: firefox-x11.desktop # Build patches Patch3: mozilla-build-arm.patch @@ -152,9 +152,11 @@ Patch419: rb244676.patch Patch420: rb246462.patch # Wayland specific upstream patches -Patch570: mozilla-1467125.patch Patch572: mozilla-1467128.patch Patch573: mozilla-1415078.patch +Patch574: firefox-pipewire.patch +Patch577: mozilla-1444437.patch +Patch580: mozilla-wayland-trunk.patch # Debian patches Patch500: mozilla-440908.patch @@ -199,6 +201,9 @@ BuildRequires: llvm BuildRequires: llvm-devel BuildRequires: clang BuildRequires: clang-libs +%if 0%{?fedora} > 27 +BuildRequires: pipewire-devel +%endif Requires: mozilla-filesystem Requires: p11-kit-trust @@ -272,7 +277,17 @@ debug %{name}, you want to install %{name}-debuginfo instead. %files -n %{crashreporter_pkg_name} -f debugcrashreporter.list %endif -%if %{?wayland_backend} +%if %{?wayland_backend_default} +%package x11 +Summary: Firefox X11 launcher. +Requires: %{name} +%description x11 +The firefox-x11 package contains launcher and desktop file +to run Firefox natively on X11. +%files x11 +%{_bindir}/firefox-x11 +%{_datadir}/applications/firefox-x11.desktop +%else %package wayland Summary: Firefox Wayland launcher. Requires: %{name} @@ -284,6 +299,7 @@ to run Firefox natively on Wayland. %{_datadir}/applications/firefox-wayland.desktop %endif + %if %{run_tests} %global testsuite_pkg_name mozilla-%{name}-testresults %package -n %{testsuite_pkg_name} @@ -328,10 +344,8 @@ This package contains results of tests executed during build. %patch406 -p1 -b .256180 %patch413 -p1 -b .1353817 %ifarch %{arm} -%patch415 -p1 -b .mozilla-1238661 +%patch415 -p1 -b .1238661 %endif -#%patch416 -p1 -b .1424422 -#%patch417 -p1 -b .bug1375074-save-restore-x28 %patch419 -p1 -b .rb244676 %patch420 -p1 -b .rb246462 @@ -341,19 +355,17 @@ This package contains results of tests executed during build. %endif # Wayland specific upstream patches -%if %{?wayland_backend} -%patch570 -p1 -b .mozilla-1467125 -%patch572 -p1 -b .mozilla-1467128 -%patch573 -p1 -b .mozilla-1415078 +%patch572 -p1 -b .1467128 +%patch573 -p1 -b .1415078 +%if 0%{?fedora} > 27 +%patch574 -p1 -b .firefox-pipewire %endif +%patch577 -p1 -b .1444437 +%patch580 -p1 -b .mozilla-wayland-trunk %{__rm} -f .mozconfig %{__cp} %{SOURCE10} .mozconfig -%if %{?wayland_backend} echo "ac_add_options --enable-default-toolkit=cairo-gtk3-wayland" >> .mozconfig -%else -echo "ac_add_options --enable-default-toolkit=cairo-gtk3" >> .mozconfig -%endif %if %{official_branding} echo "ac_add_options --enable-official-branding" >> .mozconfig %endif @@ -588,15 +600,26 @@ DESTDIR=%{buildroot} make -C objdir install %{__mkdir_p} %{buildroot}{%{_libdir},%{_bindir},%{_datadir}/applications} desktop-file-install --dir %{buildroot}%{_datadir}/applications %{SOURCE20} -%if %{?wayland_backend} +%if %{?wayland_backend_default} +desktop-file-install --dir %{buildroot}%{_datadir}/applications %{SOURCE31} +%else desktop-file-install --dir %{buildroot}%{_datadir}/applications %{SOURCE29} %endif # set up the firefox start script +%if %{?wayland_backend_default} +%global x11_state false +%else +%global x11_state true +%endif %{__rm} -rf %{buildroot}%{_bindir}/firefox -%{__cat} %{SOURCE21} > %{buildroot}%{_bindir}/firefox +%{__sed} -e 's/__DEFAULT_X11__/%{x11_state}/' %{SOURCE21} > %{buildroot}%{_bindir}/firefox + %{__chmod} 755 %{buildroot}%{_bindir}/firefox -%if %{?wayland_backend} +%if %{?wayland_backend_default} +%{__cat} %{SOURCE30} > %{buildroot}%{_bindir}/firefox-x11 +%{__chmod} 755 %{buildroot}%{_bindir}/firefox-x11 +%else %{__cat} %{SOURCE28} > %{buildroot}%{_bindir}/firefox-wayland %{__chmod} 755 %{buildroot}%{_bindir}/firefox-wayland %endif @@ -862,6 +885,11 @@ gtk-update-icon-cache %{_datadir}/icons/hicolor &>/dev/null || : #--------------------------------------------------------------------- %changelog +* Mon Oct 8 2018 Martin Stransky - 62.0.3-2 +- Added pipewire patch (mozbz#1496359) +- Added Wayland patches from Firefox 63 +- Enable Wayland backed by default on Fedora 30 + * Tue Oct 2 2018 Martin Stransky - 62.0.3-1 - Updated to latest upstream (62.0.3) diff --git a/mozilla-1444437.patch b/mozilla-1444437.patch new file mode 100644 index 0000000..79fda10 --- /dev/null +++ b/mozilla-1444437.patch @@ -0,0 +1,183 @@ + +# HG changeset patch +# User Martin Stransky +# Date 1530185851 -7200 +# Node ID 7dc09bb0f57c3397f68c424b2a4e781e89069517 +# Parent 15c95df467be553beb39f2e8102c206639e05fde +Bug 1444437 - [Wayland] Don't map mozcontainer subsurface until parent surface is commited, r?ashie + +MozReview-Commit-ID: 4qoyGH8VCAU + +diff --git a/widget/gtk/mozcontainer.cpp b/widget/gtk/mozcontainer.cpp +--- a/widget/gtk/mozcontainer.cpp ++++ b/widget/gtk/mozcontainer.cpp +@@ -207,17 +207,17 @@ moz_container_init (MozContainer *contai + gtk_widget_set_redraw_on_allocate(GTK_WIDGET(container), FALSE); + + #if defined(MOZ_WAYLAND) + { + container->subcompositor = nullptr; + container->surface = nullptr; + container->subsurface = nullptr; + container->eglwindow = nullptr; +- container->committed = false; ++ container->parent_surface_committed = false; + + GdkDisplay *gdk_display = gtk_widget_get_display(GTK_WIDGET(container)); + if (GDK_IS_WAYLAND_DISPLAY (gdk_display)) { + // Available as of GTK 3.8+ + static auto sGdkWaylandDisplayGetWlDisplay = + (wl_display *(*)(GdkDisplay *)) + dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display"); + +@@ -228,21 +228,22 @@ moz_container_init (MozContainer *contai + wl_display_roundtrip(display); + } + } + #endif + } + + #if defined(MOZ_WAYLAND) + static void +-moz_container_after_paint(GdkFrameClock *clock, MozContainer *container) ++moz_container_commited_handler(GdkFrameClock *clock, MozContainer *container) + { +- container->committed = true; +- g_signal_handlers_disconnect_by_func(clock, +- reinterpret_cast(moz_container_after_paint), container); ++ container->parent_surface_committed = true; ++ g_signal_handler_disconnect(clock, ++ container->parent_surface_committed_handler); ++ container->parent_surface_committed_handler = 0; + } + + /* We want to draw to GdkWindow owned by mContainer from Compositor thread but + * Gtk+ can be used in main thread only. So we create wayland wl_surface + * and attach it as an overlay to GdkWindow. + * + * see gtk_clutter_embed_ensure_subsurface() at gtk-clutter-embed.c + * for reference. +@@ -263,37 +264,44 @@ moz_container_map_surface(MozContainer * + + GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(container)); + if (GDK_IS_X11_DISPLAY(display)) + return false; + + if (container->subsurface && container->surface) + return true; + ++ if (!container->parent_surface_committed) { ++ if (!container->parent_surface_committed_handler) { ++ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); ++ GdkFrameClock *clock = sGdkWindowGetFrameClock(window); ++ container->parent_surface_committed_handler = ++ g_signal_connect_after(clock, "after-paint", ++ G_CALLBACK(moz_container_commited_handler), ++ container); ++ } ++ return false; ++ } ++ + if (!container->surface) { + struct wl_compositor *compositor; + compositor = sGdkWaylandDisplayGetWlCompositor(display); + container->surface = wl_compositor_create_surface(compositor); + } + + if (!container->subsurface) { + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + wl_surface* gtk_surface = sGdkWaylandWindowGetWlSurface(window); + if (!gtk_surface) { + // We requested the underlying wl_surface too early when container + // is not realized yet. We'll try again before first rendering + // to mContainer. + return false; + } + +- GdkFrameClock *clock = sGdkWindowGetFrameClock(window); +- g_signal_connect_after(clock, "after-paint", +- G_CALLBACK(moz_container_after_paint), +- container); +- + container->subsurface = + wl_subcompositor_get_subsurface (container->subcompositor, + container->surface, + gtk_surface); + gint x, y; + gdk_window_get_position(window, &x, &y); + wl_subsurface_set_position(container->subsurface, x, y); + wl_subsurface_set_desync(container->subsurface); +@@ -310,17 +318,29 @@ moz_container_map_surface(MozContainer * + } + + static void + moz_container_unmap_surface(MozContainer *container) + { + g_clear_pointer(&container->eglwindow, wl_egl_window_destroy); + g_clear_pointer(&container->subsurface, wl_subsurface_destroy); + g_clear_pointer(&container->surface, wl_surface_destroy); +- container->committed = false; ++ ++ if (container->parent_surface_committed_handler) { ++ static auto sGdkWindowGetFrameClock = ++ (GdkFrameClock *(*)(GdkWindow *)) ++ dlsym(RTLD_DEFAULT, "gdk_window_get_frame_clock"); ++ GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); ++ GdkFrameClock *clock = sGdkWindowGetFrameClock(window); ++ ++ g_signal_handler_disconnect(clock, ++ container->parent_surface_committed_handler); ++ container->parent_surface_committed_handler = 0; ++ } ++ container->parent_surface_committed = false; + } + + #endif + + void + moz_container_map (GtkWidget *widget) + { + MozContainer *container; +@@ -582,17 +602,17 @@ moz_container_get_wl_surface(MozContaine + if (!container->subsurface || !container->surface) { + GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(container)); + if (!gdk_window_is_visible(window)) + return nullptr; + + moz_container_map_surface(container); + } + +- return container->committed ? container->surface : nullptr; ++ return container->surface; + } + + struct wl_egl_window * + moz_container_get_wl_egl_window(MozContainer *container) + { + if (!container->eglwindow) { + struct wl_surface *wlsurf = moz_container_get_wl_surface(container); + if (!wlsurf) +diff --git a/widget/gtk/mozcontainer.h b/widget/gtk/mozcontainer.h +--- a/widget/gtk/mozcontainer.h ++++ b/widget/gtk/mozcontainer.h +@@ -68,17 +68,18 @@ struct _MozContainer + GtkContainer container; + GList *children; + + #ifdef MOZ_WAYLAND + struct wl_subcompositor *subcompositor; + struct wl_surface *surface; + struct wl_subsurface *subsurface; + struct wl_egl_window *eglwindow; +- gboolean committed; ++ gboolean parent_surface_committed; ++ gulong parent_surface_committed_handler; + #endif + }; + + struct _MozContainerClass + { + GtkContainerClass parent_class; + }; + + diff --git a/mozilla-1467125.patch b/mozilla-1467125.patch deleted file mode 100644 index 375ac49..0000000 --- a/mozilla-1467125.patch +++ /dev/null @@ -1,26 +0,0 @@ -diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp ---- a/widget/gtk/WindowSurfaceWayland.cpp -+++ b/widget/gtk/WindowSurfaceWayland.cpp -@@ -146,18 +146,19 @@ static StaticMutex gWaylandDisplaysMutex - // - // nsWaylandDisplay is our interface to wayland compositor. It provides wayland - // global objects as we need (wl_display, wl_shm) and operates wl_event_queue on - // compositor (not the main) thread. - static nsWaylandDisplay* WaylandDisplayGet(wl_display *aDisplay); - static void WaylandDisplayRelease(wl_display *aDisplay); - static void WaylandDisplayLoop(wl_display *aDisplay); - --// TODO: is the 60pfs loop correct? --#define EVENT_LOOP_DELAY (1000/60) -+// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with -+// compositor event loop. -+#define EVENT_LOOP_DELAY (1000/240) - - // Get WaylandDisplay for given wl_display and actual calling thread. - static nsWaylandDisplay* - WaylandDisplayGetLocked(wl_display *aDisplay, const StaticMutexAutoLock&) - { - nsWaylandDisplay* waylandDisplay = nullptr; - - int len = gWaylandDisplays.Count(); - diff --git a/mozilla-1467128.patch b/mozilla-1467128.patch index 75cf92c..d50b2e2 100644 --- a/mozilla-1467128.patch +++ b/mozilla-1467128.patch @@ -1,49 +1,19 @@ -diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h ---- a/gfx/thebes/gfxPlatformGtk.h -+++ b/gfx/thebes/gfxPlatformGtk.h -@@ -102,23 +102,42 @@ public: - #endif - - #ifdef MOZ_X11 - Display* GetCompositorDisplay() { - return mCompositorDisplay; - } - #endif // MOZ_X11 - -+#ifdef MOZ_WAYLAND -+ void SetWaylandLastVsync(uint32_t aVsyncTimestamp) { -+ mWaylandLastVsyncTimestamp = aVsyncTimestamp; -+ } -+ int64_t GetWaylandLastVsync() { -+ return mWaylandLastVsyncTimestamp; -+ } -+ void SetWaylandFrameDelay(int64_t aFrameDelay) { -+ mWaylandFrameDelay = aFrameDelay; -+ } -+ int64_t GetWaylandFrameDelay() { -+ return mWaylandFrameDelay; -+ } -+#endif -+ - protected: - bool CheckVariationFontSupport() override; - - int8_t mMaxGenericSubstitutions; - - private: - virtual void GetPlatformCMSOutputProfile(void *&mem, - size_t &size) override; - - #ifdef MOZ_X11 - Display* mCompositorDisplay; - #endif -+#ifdef MOZ_WAYLAND -+ int64_t mWaylandLastVsyncTimestamp; -+ int64_t mWaylandFrameDelay; -+#endif - }; - - #endif /* GFX_PLATFORM_GTK_H */ + +# HG changeset patch +# User Martin Stransky +# Date 1530270941 -7200 +# Node ID 338c0093263be6440a33b79a92801bd2b4658f79 +# Parent 84a6d5a0b551f5da116aab702dd38bb725bc8a08 +Bug 1467128 - [Wayland] Get VSync from Gtk/Wayland, r?lsalzman + +VSync on Wayland is a bit tricky as we can get only "last VSync" event signal with +CLOCK_MONOTONIC timestamp or none (if application is hidden/minimized). + +That means we should draw a next frame at "last Vsync + frame delay" time and also +approximate next VSync event when we don't get any. + +MozReview-Commit-ID: FI3Z4nkmDNK + diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp --- a/gfx/thebes/gfxPlatformGtk.cpp +++ b/gfx/thebes/gfxPlatformGtk.cpp @@ -334,26 +304,49 @@ diff --git a/gfx/thebes/gfxPlatformGtk.cpp b/gfx/thebes/gfxPlatformGtk.cpp NS_WARNING("SGI_video_sync unsupported. Falling back to software vsync."); } return gfxPlatform::CreateHardwareVsyncSource(); -diff --git a/widget/gtk/WindowSurfaceWayland.cpp b/widget/gtk/WindowSurfaceWayland.cpp ---- a/widget/gtk/WindowSurfaceWayland.cpp -+++ b/widget/gtk/WindowSurfaceWayland.cpp -@@ -546,16 +546,18 @@ WindowBackBuffer::Lock() - mWaylandDisplay->GetSurfaceFormat()); - } +diff --git a/gfx/thebes/gfxPlatformGtk.h b/gfx/thebes/gfxPlatformGtk.h +--- a/gfx/thebes/gfxPlatformGtk.h ++++ b/gfx/thebes/gfxPlatformGtk.h +@@ -102,23 +102,42 @@ public: + #endif - static void - frame_callback_handler(void *data, struct wl_callback *callback, uint32_t time) - { - auto surface = reinterpret_cast(data); - surface->FrameCallbackHandler(); + #ifdef MOZ_X11 + Display* GetCompositorDisplay() { + return mCompositorDisplay; + } + #endif // MOZ_X11 + ++#ifdef MOZ_WAYLAND ++ void SetWaylandLastVsync(uint32_t aVsyncTimestamp) { ++ mWaylandLastVsyncTimestamp = aVsyncTimestamp; ++ } ++ int64_t GetWaylandLastVsync() { ++ return mWaylandLastVsyncTimestamp; ++ } ++ void SetWaylandFrameDelay(int64_t aFrameDelay) { ++ mWaylandFrameDelay = aFrameDelay; ++ } ++ int64_t GetWaylandFrameDelay() { ++ return mWaylandFrameDelay; ++ } ++#endif + -+ gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time); - } + protected: + bool CheckVariationFontSupport() override; + + int8_t mMaxGenericSubstitutions; + + private: + virtual void GetPlatformCMSOutputProfile(void *&mem, + size_t &size) override; - static const struct wl_callback_listener frame_listener = { - frame_callback_handler + #ifdef MOZ_X11 + Display* mCompositorDisplay; + #endif ++#ifdef MOZ_WAYLAND ++ int64_t mWaylandLastVsyncTimestamp; ++ int64_t mWaylandFrameDelay; ++#endif }; - WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) - : mWindow(aWindow) - + #endif /* GFX_PLATFORM_GTK_H */ diff --git a/mozilla-1491824.patch b/mozilla-1491824.patch new file mode 100644 index 0000000..4bdca59 --- /dev/null +++ b/mozilla-1491824.patch @@ -0,0 +1,40 @@ +diff -up firefox-62.0.3/media/libyuv/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium.old firefox-62.0.3/media/libyuv/libyuv/tools_libyuv/autoroller/unittests/testdata/DEPS.chromium +diff -up firefox-62.0.3/media/webrtc/trunk/Makefile.old firefox-62.0.3/media/webrtc/trunk/Makefile +diff -up firefox-62.0.3/widget/gtk/nsClipboardWayland.cpp.old firefox-62.0.3/widget/gtk/nsClipboardWayland.cpp +--- firefox-62.0.3/widget/gtk/nsClipboardWayland.cpp.old 2018-10-05 12:52:40.067567770 +0200 ++++ firefox-62.0.3/widget/gtk/nsClipboardWayland.cpp 2018-09-21 12:39:40.477095989 +0200 +@@ -153,7 +153,7 @@ DataOffer::GetData(wl_display* aDisplay, + + GIOChannel *channel = g_io_channel_unix_new(pipe_fd[0]); + GError* error = nullptr; +- char* clipboardData; ++ char* clipboardData = nullptr; + + g_io_channel_set_encoding(channel, nullptr, &error); + if (!error) { +@@ -470,13 +470,17 @@ nsRetrievalContextWayland::SetClipboardD + // Delete existing clipboard data offer + mClipboardOffer = nullptr; + +- DataOffer* dataOffer = +- static_cast(g_hash_table_lookup(mActiveOffers, +- aWaylandDataOffer)); +- NS_ASSERTION(dataOffer, "We're missing clipboard data offer!"); +- if (dataOffer) { +- g_hash_table_remove(mActiveOffers, aWaylandDataOffer); +- mClipboardOffer = dataOffer; ++ // null aWaylandDataOffer indicates that our clipboard content ++ // is no longer valid and should be release. ++ if (aWaylandDataOffer != nullptr) { ++ DataOffer* dataOffer = ++ static_cast(g_hash_table_lookup(mActiveOffers, ++ aWaylandDataOffer)); ++ NS_ASSERTION(dataOffer, "We're missing stored clipboard data offer!"); ++ if (dataOffer) { ++ g_hash_table_remove(mActiveOffers, aWaylandDataOffer); ++ mClipboardOffer = dataOffer; ++ } + } + } + +diff -up firefox-62.0.3/widget/gtk/nsClipboardWayland.h.old firefox-62.0.3/widget/gtk/nsClipboardWayland.h diff --git a/mozilla-wayland-trunk.patch b/mozilla-wayland-trunk.patch new file mode 100644 index 0000000..8c3746a --- /dev/null +++ b/mozilla-wayland-trunk.patch @@ -0,0 +1,519 @@ +Wayland patches from Firefox 63. + +diff -up firefox-62.0.3/widget/gtk/mozcontainer.cpp.old firefox-62.0.3/widget/gtk/mozcontainer.cpp +--- firefox-62.0.3/widget/gtk/mozcontainer.cpp.old 2018-10-08 11:52:33.442449788 +0200 ++++ firefox-62.0.3/widget/gtk/mozcontainer.cpp 2018-08-21 14:22:12.668885853 +0200 +@@ -605,6 +605,15 @@ moz_container_get_wl_surface(MozContaine + return nullptr; + + moz_container_map_surface(container); ++ // Set the scale factor for the buffer right after we create it. ++ if (container->surface) { ++ static auto sGdkWindowGetScaleFactorPtr = (gint (*)(GdkWindow*)) ++ dlsym(RTLD_DEFAULT, "gdk_window_get_scale_factor"); ++ if (sGdkWindowGetScaleFactorPtr && window) { ++ gint scaleFactor = (*sGdkWindowGetScaleFactorPtr)(window); ++ wl_surface_set_buffer_scale(container->surface, scaleFactor); ++ } ++ } + } + + return container->surface; +diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp +--- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp.old 2018-10-08 11:52:47.907392869 +0200 ++++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.cpp 2018-09-06 11:01:18.801964790 +0200 +@@ -151,8 +151,9 @@ static nsWaylandDisplay* WaylandDisplayG + static void WaylandDisplayRelease(wl_display *aDisplay); + static void WaylandDisplayLoop(wl_display *aDisplay); + +-// TODO: is the 60pfs loop correct? +-#define EVENT_LOOP_DELAY (1000/60) ++// TODO: Bug 1467125 - We need to integrate wl_display_dispatch_queue_pending() with ++// compositor event loop. ++#define EVENT_LOOP_DELAY (1000/240) + + // Get WaylandDisplay for given wl_display and actual calling thread. + static nsWaylandDisplay* +@@ -523,7 +524,7 @@ WindowBackBuffer::Detach() + } + + bool +-WindowBackBuffer::SetImageDataFromBackBuffer( ++WindowBackBuffer::SetImageDataFromBuffer( + class WindowBackBuffer* aSourceBuffer) + { + if (!IsMatchingSize(aSourceBuffer)) { +@@ -550,6 +551,8 @@ frame_callback_handler(void *data, struc + { + auto surface = reinterpret_cast(data); + surface->FrameCallbackHandler(); ++ ++ gfxPlatformGtk::GetPlatform()->SetWaylandLastVsync(time); + } + + static const struct wl_callback_listener frame_listener = { +@@ -559,27 +562,40 @@ static const struct wl_callback_listener + WindowSurfaceWayland::WindowSurfaceWayland(nsWindow *aWindow) + : mWindow(aWindow) + , mWaylandDisplay(WaylandDisplayGet(aWindow->GetWaylandDisplay())) +- , mFrontBuffer(nullptr) +- , mBackBuffer(nullptr) ++ , mWaylandBuffer(nullptr) ++ , mBackupBuffer(nullptr) + , mFrameCallback(nullptr) +- , mFrameCallbackSurface(nullptr) ++ , mLastCommittedSurface(nullptr) + , mDisplayThreadMessageLoop(MessageLoop::current()) +- , mDirectWlBufferDraw(true) +- , mDelayedCommit(false) +- , mFullScreenDamage(false) ++ , mDelayedCommitHandle(nullptr) ++ , mDrawToWaylandBufferDirectly(true) ++ , mPendingCommit(false) ++ , mWaylandBufferFullScreenDamage(false) + , mIsMainThread(NS_IsMainThread()) ++ , mNeedScaleFactorUpdate(true) + { + } + + WindowSurfaceWayland::~WindowSurfaceWayland() + { +- delete mFrontBuffer; +- delete mBackBuffer; ++ if (mPendingCommit) { ++ NS_WARNING("Deleted WindowSurfaceWayland with a pending commit!"); ++ } ++ ++ if (mDelayedCommitHandle) { ++ // Delete reference to this to prevent WaylandBufferDelayCommitHandler() ++ // operate on released this. mDelayedCommitHandle itself will ++ // be released at WaylandBufferDelayCommitHandler(). ++ *mDelayedCommitHandle = nullptr; ++ } + + if (mFrameCallback) { + wl_callback_destroy(mFrameCallback); + } + ++ delete mWaylandBuffer; ++ delete mBackupBuffer; ++ + if (!mIsMainThread) { + // We can be destroyed from main thread even though we was created/used + // in compositor thread. We have to unref/delete WaylandDisplay in compositor +@@ -593,73 +609,64 @@ WindowSurfaceWayland::~WindowSurfaceWayl + } + } + +-void +-WindowSurfaceWayland::UpdateScaleFactor() +-{ +- wl_surface* waylandSurface = mWindow->GetWaylandSurface(); +- if (waylandSurface) { +- wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); +- } +-} +- + WindowBackBuffer* +-WindowSurfaceWayland::GetFrontBufferToDraw(int aWidth, int aHeight) ++WindowSurfaceWayland::GetWaylandBufferToDraw(int aWidth, int aHeight) + { +- if (!mFrontBuffer) { +- mFrontBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); +- mBackBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); +- return mFrontBuffer; ++ if (!mWaylandBuffer) { ++ mWaylandBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); ++ mBackupBuffer = new WindowBackBuffer(mWaylandDisplay, aWidth, aHeight); ++ return mWaylandBuffer; + } + +- if (!mFrontBuffer->IsAttached()) { +- if (!mFrontBuffer->IsMatchingSize(aWidth, aHeight)) { +- mFrontBuffer->Resize(aWidth, aHeight); ++ if (!mWaylandBuffer->IsAttached()) { ++ if (!mWaylandBuffer->IsMatchingSize(aWidth, aHeight)) { ++ mWaylandBuffer->Resize(aWidth, aHeight); + // There's a chance that scale factor has been changed + // when buffer size changed +- UpdateScaleFactor(); ++ mNeedScaleFactorUpdate = true; + } +- return mFrontBuffer; ++ return mWaylandBuffer; + } + + // Front buffer is used by compositor, draw to back buffer +- if (mBackBuffer->IsAttached()) { ++ if (mBackupBuffer->IsAttached()) { + NS_WARNING("No drawing buffer available"); + return nullptr; + } + +- MOZ_ASSERT(!mDelayedCommit, ++ MOZ_ASSERT(!mPendingCommit, + "Uncommitted buffer switch, screen artifacts ahead."); + +- WindowBackBuffer *tmp = mFrontBuffer; +- mFrontBuffer = mBackBuffer; +- mBackBuffer = tmp; ++ WindowBackBuffer *tmp = mWaylandBuffer; ++ mWaylandBuffer = mBackupBuffer; ++ mBackupBuffer = tmp; + +- if (mBackBuffer->IsMatchingSize(aWidth, aHeight)) { ++ if (mBackupBuffer->IsMatchingSize(aWidth, aHeight)) { + // Former front buffer has the same size as a requested one. + // Gecko may expect a content already drawn on screen so copy + // existing data to the new buffer. +- mFrontBuffer->SetImageDataFromBackBuffer(mBackBuffer); ++ mWaylandBuffer->SetImageDataFromBuffer(mBackupBuffer); + // When buffer switches we need to damage whole screen + // (https://bugzilla.redhat.com/show_bug.cgi?id=1418260) +- mFullScreenDamage = true; ++ mWaylandBufferFullScreenDamage = true; + } else { + // Former buffer has different size from the new request. Only resize + // the new buffer and leave gecko to render new whole content. +- mFrontBuffer->Resize(aWidth, aHeight); ++ mWaylandBuffer->Resize(aWidth, aHeight); + } + +- return mFrontBuffer; ++ return mWaylandBuffer; + } + + already_AddRefed +-WindowSurfaceWayland::LockFrontBuffer(int aWidth, int aHeight) ++WindowSurfaceWayland::LockWaylandBuffer(int aWidth, int aHeight) + { +- WindowBackBuffer* buffer = GetFrontBufferToDraw(aWidth, aHeight); ++ WindowBackBuffer* buffer = GetWaylandBufferToDraw(aWidth, aHeight); + if (buffer) { + return buffer->Lock(); + } + +- NS_WARNING("WindowSurfaceWayland::LockFrontBuffer(): No buffer available"); ++ NS_WARNING("WindowSurfaceWayland::LockWaylandBuffer(): No buffer available"); + return nullptr; + } + +@@ -687,8 +694,8 @@ WindowSurfaceWayland::LockImageSurface(c + A) Lock() is called to whole surface. In that case we don't need + to clip/buffer the drawing and we can return wl_buffer directly + for drawing. +- - mFrontBuffer is available - that's an ideal situation. +- - mFrontBuffer is locked by compositor - flip buffers and draw. ++ - mWaylandBuffer is available - that's an ideal situation. ++ - mWaylandBuffer is locked by compositor - flip buffers and draw. + - if we can't flip buffers - go B) + + B) Lock() is requested for part(s) of screen. We need to provide temporary +@@ -704,30 +711,30 @@ WindowSurfaceWayland::Lock(const LayoutD + gfx::IntSize lockSize(bounds.XMost(), bounds.YMost()); + + // Are we asked for entire nsWindow to draw? +- mDirectWlBufferDraw = (aRegion.GetNumRects() == 1 && +- bounds.x == 0 && bounds.y == 0 && +- lockSize.width == screenRect.width && +- lockSize.height == screenRect.height); +- +- if (mDirectWlBufferDraw) { +- RefPtr dt = LockFrontBuffer(screenRect.width, +- screenRect.height); ++ mDrawToWaylandBufferDirectly = (aRegion.GetNumRects() == 1 && ++ bounds.x == 0 && bounds.y == 0 && ++ lockSize.width == screenRect.width && ++ lockSize.height == screenRect.height); ++ ++ if (mDrawToWaylandBufferDirectly) { ++ RefPtr dt = LockWaylandBuffer(screenRect.width, ++ screenRect.height); + if (dt) { + return dt.forget(); + } + + // We don't have any front buffer available. Try indirect drawing + // to mImageSurface which is mirrored to front buffer at commit. +- mDirectWlBufferDraw = false; ++ mDrawToWaylandBufferDirectly = false; + } + + return LockImageSurface(lockSize); + } + + bool +-WindowSurfaceWayland::CommitImageSurface(const LayoutDeviceIntRegion& aRegion) ++WindowSurfaceWayland::CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion) + { +- MOZ_ASSERT(!mDirectWlBufferDraw); ++ MOZ_ASSERT(!mDrawToWaylandBufferDirectly); + + LayoutDeviceIntRect screenRect = mWindow->GetBounds(); + gfx::IntRect bounds = aRegion.GetBounds().ToUnknownRect(); +@@ -737,8 +744,8 @@ WindowSurfaceWayland::CommitImageSurface + return false; + } + +- RefPtr dt = LockFrontBuffer(screenRect.width, +- screenRect.height); ++ RefPtr dt = LockWaylandBuffer(screenRect.width, ++ screenRect.height); + RefPtr surf = + gfx::Factory::CreateSourceSurfaceForCairoSurface(mImageSurface->CairoSurface(), + mImageSurface->GetSize(), +@@ -766,92 +773,145 @@ WindowSurfaceWayland::CommitImageSurface + return true; + } + ++static void ++WaylandBufferDelayCommitHandler(WindowSurfaceWayland **aSurface) ++{ ++ if (*aSurface) { ++ (*aSurface)->DelayedCommitHandler(); ++ } else { ++ // Referenced WindowSurfaceWayland is already deleted. ++ // Do nothing but just release the mDelayedCommitHandle allocated at ++ // WindowSurfaceWayland::CommitWaylandBuffer(). ++ free(aSurface); ++ } ++} ++ + void +-WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) ++WindowSurfaceWayland::CommitWaylandBuffer() + { +- MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ MOZ_ASSERT(mPendingCommit, "Committing empty surface!"); + + wl_surface* waylandSurface = mWindow->GetWaylandSurface(); + if (!waylandSurface) { +- // Target window is already destroyed - don't bother to render there. +- NS_WARNING("WindowSurfaceWayland::Commit(): parent wl_surface is already hidden/deleted."); ++ // Target window is not created yet - delay the commit. This can happen only ++ // when the window is newly created and there's no active ++ // frame callback pending. ++ MOZ_ASSERT(!mFrameCallback || waylandSurface != mLastCommittedSurface, ++ "Missing wayland surface at frame callback!"); ++ ++ // Do nothing if there's already mDelayedCommitHandle pending. ++ if (!mDelayedCommitHandle) { ++ mDelayedCommitHandle = static_cast( ++ moz_xmalloc(sizeof(*mDelayedCommitHandle))); ++ *mDelayedCommitHandle = this; ++ ++ MessageLoop::current()->PostDelayedTask( ++ NewRunnableFunction("WaylandBackBufferCommit", ++ &WaylandBufferDelayCommitHandler, ++ mDelayedCommitHandle), ++ EVENT_LOOP_DELAY); ++ } + return; + } + wl_proxy_set_queue((struct wl_proxy *)waylandSurface, + mWaylandDisplay->GetEventQueue()); + +- if (!mDirectWlBufferDraw) { +- // We have new content at mImageSurface - copy data to mFrontBuffer first. +- CommitImageSurface(aInvalidRegion); ++ // We have an active frame callback request so handle it. ++ if (mFrameCallback) { ++ if (waylandSurface == mLastCommittedSurface) { ++ // We have an active frame callback pending from our recent surface. ++ // It means we should defer the commit to FrameCallbackHandler(). ++ return; ++ } ++ // If our stored wl_surface does not match the actual one it means the frame ++ // callback is no longer active and we should release it. ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ mLastCommittedSurface = nullptr; + } + +- if (mFullScreenDamage) { ++ if (mWaylandBufferFullScreenDamage) { + LayoutDeviceIntRect rect = mWindow->GetBounds(); + wl_surface_damage(waylandSurface, 0, 0, rect.width, rect.height); +- mFullScreenDamage = false; ++ mWaylandBufferFullScreenDamage = false; + } else { +- for (auto iter = aInvalidRegion.RectIter(); !iter.Done(); iter.Next()) { ++ gint scaleFactor = mWindow->GdkScaleFactor(); ++ for (auto iter = mWaylandBufferDamage.RectIter(); !iter.Done(); iter.Next()) { + const mozilla::LayoutDeviceIntRect &r = iter.Get(); +- wl_surface_damage(waylandSurface, r.x, r.y, r.width, r.height); ++ // We need to remove the scale factor because the wl_surface_damage ++ // also multiplies by current scale factor. ++ wl_surface_damage(waylandSurface, r.x/scaleFactor, r.y/scaleFactor, ++ r.width/scaleFactor, r.height/scaleFactor); + } + } + +- // Frame callback is always connected to actual wl_surface. When the surface +- // is unmapped/deleted the frame callback is never called. Unfortunatelly +- // we don't know if the frame callback is not going to be called. +- // But our mozcontainer code deletes wl_surface when the GdkWindow is hidden +- // creates a new one when is visible. +- if (mFrameCallback && mFrameCallbackSurface == waylandSurface) { +- // Do nothing here - we have a valid wl_surface and the buffer will be +- // commited to compositor in next frame callback event. +- mDelayedCommit = true; +- return; +- } else { +- if (mFrameCallback) { +- // Delete frame callback connected to obsoleted wl_surface. +- wl_callback_destroy(mFrameCallback); +- } ++ // Clear all back buffer damage as we're committing ++ // all requested regions. ++ mWaylandBufferDamage.SetEmpty(); ++ ++ mFrameCallback = wl_surface_frame(waylandSurface); ++ wl_callback_add_listener(mFrameCallback, &frame_listener, this); + +- mFrameCallback = wl_surface_frame(waylandSurface); +- wl_callback_add_listener(mFrameCallback, &frame_listener, this); +- mFrameCallbackSurface = waylandSurface; +- +- // There's no pending frame callback so we can draw immediately +- // and create frame callback for possible subsequent drawing. +- mFrontBuffer->Attach(waylandSurface); +- mDelayedCommit = false; ++ if (mNeedScaleFactorUpdate || mLastCommittedSurface != waylandSurface) { ++ wl_surface_set_buffer_scale(waylandSurface, mWindow->GdkScaleFactor()); ++ mNeedScaleFactorUpdate = false; + } ++ ++ mWaylandBuffer->Attach(waylandSurface); ++ mLastCommittedSurface = waylandSurface; ++ ++ // There's no pending commit, all changes are sent to compositor. ++ mPendingCommit = false; ++} ++ ++void ++WindowSurfaceWayland::Commit(const LayoutDeviceIntRegion& aInvalidRegion) ++{ ++ MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ ++ // We have new content at mImageSurface - copy data to mWaylandBuffer first. ++ if (!mDrawToWaylandBufferDirectly) { ++ CommitImageSurfaceToWaylandBuffer(aInvalidRegion); ++ } ++ ++ // If we're not at fullscreen damage add drawing area from aInvalidRegion ++ if (!mWaylandBufferFullScreenDamage) { ++ mWaylandBufferDamage.OrWith(aInvalidRegion); ++ } ++ ++ // We're ready to commit. ++ mPendingCommit = true; ++ CommitWaylandBuffer(); + } + + void + WindowSurfaceWayland::FrameCallbackHandler() + { + MOZ_ASSERT(mIsMainThread == NS_IsMainThread()); ++ MOZ_ASSERT(mFrameCallback != nullptr, ++ "FrameCallbackHandler() called without valid frame callback!"); ++ MOZ_ASSERT(mLastCommittedSurface != nullptr, ++ "FrameCallbackHandler() called without valid wl_surface!"); + +- if (mFrameCallback) { +- wl_callback_destroy(mFrameCallback); +- mFrameCallback = nullptr; +- mFrameCallbackSurface = nullptr; ++ wl_callback_destroy(mFrameCallback); ++ mFrameCallback = nullptr; ++ ++ if (mPendingCommit) { ++ CommitWaylandBuffer(); + } ++} + +- if (mDelayedCommit) { +- wl_surface* waylandSurface = mWindow->GetWaylandSurface(); +- if (!waylandSurface) { +- // Target window is already destroyed - don't bother to render there. +- NS_WARNING("WindowSurfaceWayland::FrameCallbackHandler(): parent wl_surface is already hidden/deleted."); +- return; +- } +- wl_proxy_set_queue((struct wl_proxy *)waylandSurface, +- mWaylandDisplay->GetEventQueue()); ++void ++WindowSurfaceWayland::DelayedCommitHandler() ++{ ++ MOZ_ASSERT(mDelayedCommitHandle != nullptr, "Missing mDelayedCommitHandle!"); + +- // Send pending surface to compositor and register frame callback +- // for possible subsequent drawing. +- mFrameCallback = wl_surface_frame(waylandSurface); +- wl_callback_add_listener(mFrameCallback, &frame_listener, this); +- mFrameCallbackSurface = waylandSurface; ++ *mDelayedCommitHandle = nullptr; ++ free(mDelayedCommitHandle); ++ mDelayedCommitHandle = nullptr; + +- mFrontBuffer->Attach(waylandSurface); +- mDelayedCommit = false; ++ if (mPendingCommit) { ++ CommitWaylandBuffer(); + } + } + +diff -up firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h +--- firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h.old 2018-10-08 11:52:52.154376159 +0200 ++++ firefox-62.0.3/widget/gtk/WindowSurfaceWayland.h 2018-09-06 11:01:18.802964787 +0200 +@@ -74,7 +74,7 @@ public: + bool IsAttached() { return mAttached; } + + bool Resize(int aWidth, int aHeight); +- bool SetImageDataFromBackBuffer(class WindowBackBuffer* aSourceBuffer); ++ bool SetImageDataFromBuffer(class WindowBackBuffer* aSourceBuffer); + + bool IsMatchingSize(int aWidth, int aHeight) + { +@@ -111,28 +111,32 @@ public: + already_AddRefed Lock(const LayoutDeviceIntRegion& aRegion) override; + void Commit(const LayoutDeviceIntRegion& aInvalidRegion) final; + void FrameCallbackHandler(); ++ void DelayedCommitHandler(); + + private: +- WindowBackBuffer* GetFrontBufferToDraw(int aWidth, int aHeight); +- void UpdateScaleFactor(); ++ WindowBackBuffer* GetWaylandBufferToDraw(int aWidth, int aHeight); + +- already_AddRefed LockFrontBuffer(int aWidth, int aHeight); ++ already_AddRefed LockWaylandBuffer(int aWidth, int aHeight); + already_AddRefed LockImageSurface(const gfx::IntSize& aLockSize); +- bool CommitImageSurface(const LayoutDeviceIntRegion& aRegion); ++ bool CommitImageSurfaceToWaylandBuffer(const LayoutDeviceIntRegion& aRegion); ++ void CommitWaylandBuffer(); + + // TODO: Do we need to hold a reference to nsWindow object? + nsWindow* mWindow; + nsWaylandDisplay* mWaylandDisplay; +- WindowBackBuffer* mFrontBuffer; +- WindowBackBuffer* mBackBuffer; ++ WindowBackBuffer* mWaylandBuffer; ++ LayoutDeviceIntRegion mWaylandBufferDamage; ++ WindowBackBuffer* mBackupBuffer; + RefPtr mImageSurface; + wl_callback* mFrameCallback; +- wl_surface* mFrameCallbackSurface; ++ wl_surface* mLastCommittedSurface; + MessageLoop* mDisplayThreadMessageLoop; +- bool mDirectWlBufferDraw; +- bool mDelayedCommit; +- bool mFullScreenDamage; ++ WindowSurfaceWayland** mDelayedCommitHandle; ++ bool mDrawToWaylandBufferDirectly; ++ bool mPendingCommit; ++ bool mWaylandBufferFullScreenDamage; + bool mIsMainThread; ++ bool mNeedScaleFactorUpdate; + }; + + } // namespace widget