diff --git a/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch new file mode 100644 index 0000000..d50b11b --- /dev/null +++ b/SOURCES/0001-backend-Add-getter-for-MetaScreenCast.patch @@ -0,0 +1,49 @@ +From 967d8236d81c8689f2fe60621ec7e66d88b43dea Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 17 Jun 2020 17:46:25 +0200 +Subject: [PATCH 1/4] backend: Add getter for MetaScreenCast + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-backend-private.h | 2 ++ + src/backends/meta-backend.c | 11 +++++++++++ + 2 files changed, 13 insertions(+) + +diff --git a/src/backends/meta-backend-private.h b/src/backends/meta-backend-private.h +index 81ec81e5f1..77f4da77c4 100644 +--- a/src/backends/meta-backend-private.h ++++ b/src/backends/meta-backend-private.h +@@ -138,6 +138,8 @@ MetaEgl * meta_backend_get_egl (MetaBackend *backend); + + #ifdef HAVE_REMOTE_DESKTOP + MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); ++ ++MetaScreenCast * meta_backend_get_screen_cast (MetaBackend *backend); + #endif + + gboolean meta_backend_grab_device (MetaBackend *backend, +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index 750a9248a8..b498b7aa44 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -965,6 +965,17 @@ meta_backend_get_remote_desktop (MetaBackend *backend) + + return priv->remote_desktop; + } ++ ++/** ++ * meta_backend_get_screen_cast: (skip) ++ */ ++MetaScreenCast * ++meta_backend_get_screen_cast (MetaBackend *backend) ++{ ++ MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); ++ ++ return priv->screen_cast; ++} + #endif /* HAVE_REMOTE_DESKTOP */ + + /** +-- +2.26.2 + diff --git a/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch new file mode 100644 index 0000000..427bb17 --- /dev/null +++ b/SOURCES/0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch @@ -0,0 +1,41 @@ +From 9f8564ce066aeb704341d6f926daec0045243b70 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:06:38 +0200 +Subject: [PATCH 1/2] monitor-manager-kms: Trigger hotplug processing on gpu + removal + +--- + src/backends/native/meta-monitor-manager-kms.c | 16 +++++++++++----- + 1 file changed, 11 insertions(+), 5 deletions(-) + +diff --git a/src/backends/native/meta-monitor-manager-kms.c b/src/backends/native/meta-monitor-manager-kms.c +index 9a0364441a..2819881576 100644 +--- a/src/backends/native/meta-monitor-manager-kms.c ++++ b/src/backends/native/meta-monitor-manager-kms.c +@@ -470,12 +470,18 @@ on_uevent (GUdevClient *client, + + if (!g_strcmp0 (seat_id, device_seat)) + handle_gpu_hotplug (manager_kms, device); +- } +- +- if (!g_udev_device_get_property_as_boolean (device, "HOTPLUG")) +- return; + +- handle_hotplug_event (manager); ++ handle_hotplug_event (manager); ++ } ++ else if (g_str_equal (action, "remove") && ++ g_udev_device_get_device_file (device) != NULL) ++ { ++ handle_hotplug_event (manager); ++ } ++ else if (g_udev_device_get_property_as_boolean (device, "HOTPLUG")) ++ { ++ handle_hotplug_event (manager); ++ } + } + + static void +-- +2.26.2 + diff --git a/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch new file mode 100644 index 0000000..2be8b13 --- /dev/null +++ b/SOURCES/0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch @@ -0,0 +1,108 @@ +From d107b52939ca0acb1f8dacf1275278edba64eebe Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 1 Oct 2019 11:53:57 +0200 +Subject: [PATCH] renderer: Add API to check whether renderer is hardware + accelerated + +Also expose an introspected variant via the MetaBackend. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/838 +--- + src/backends/meta-backend.c | 15 +++++++++++++++ + src/backends/meta-renderer.c | 27 +++++++++++++++++++++++++++ + src/backends/meta-renderer.h | 2 ++ + src/meta/meta-backend.h | 3 +++ + 4 files changed, 47 insertions(+) + +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index 72cfbdaf3..e61181f9a 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -985,6 +985,21 @@ meta_backend_get_remote_access_controller (MetaBackend *backend) + #endif + } + ++/** ++ * meta_backend_is_rendering_hardware_accelerated: ++ * @backend: A #MetaBackend ++ * ++ * Returns: %TRUE if the rendering is hardware accelerated, otherwise ++ * %FALSE. ++ */ ++gboolean ++meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend) ++{ ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ ++ return meta_renderer_is_hardware_accelerated (renderer); ++} ++ + /** + * meta_backend_grab_device: (skip) + */ +diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c +index 87ba9f9f0..470220fc8 100644 +--- a/src/backends/meta-renderer.c ++++ b/src/backends/meta-renderer.c +@@ -166,6 +166,33 @@ meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + return NULL; + } + ++gboolean ++meta_renderer_is_hardware_accelerated (MetaRenderer *renderer) ++{ ++ MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); ++ MetaBackend *backend = meta_get_backend (); ++ ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ CoglGpuInfo *info = &cogl_context->gpu; ++ ++ switch (info->architecture) ++ { ++ case COGL_GPU_INFO_ARCHITECTURE_UNKNOWN: ++ case COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE: ++ case COGL_GPU_INFO_ARCHITECTURE_SGX: ++ case COGL_GPU_INFO_ARCHITECTURE_MALI: ++ return TRUE; ++ case COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE: ++ case COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE: ++ case COGL_GPU_INFO_ARCHITECTURE_SWRAST: ++ return FALSE; ++ } ++ ++ g_assert_not_reached (); ++ return FALSE; ++} ++ + static void + meta_renderer_finalize (GObject *object) + { +diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h +index 478baee91..97bf36860 100644 +--- a/src/backends/meta-renderer.h ++++ b/src/backends/meta-renderer.h +@@ -59,4 +59,6 @@ GList * meta_renderer_get_views (MetaRenderer *renderer); + MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + MetaLogicalMonitor *logical_monitor); + ++gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer); ++ + #endif /* META_RENDERER_H */ +diff --git a/src/meta/meta-backend.h b/src/meta/meta-backend.h +index aaa6aae97..8edc0bf2c 100644 +--- a/src/meta/meta-backend.h ++++ b/src/meta/meta-backend.h +@@ -64,6 +64,9 @@ MetaSettings *meta_backend_get_settings (MetaBackend *backend); + META_EXPORT + MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend); + ++META_EXPORT ++gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend); ++ + META_EXPORT + void meta_clutter_init (void); + +-- +2.26.2 + diff --git a/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch new file mode 100644 index 0000000..a0f76ff --- /dev/null +++ b/SOURCES/0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch @@ -0,0 +1,40 @@ +From b32ae04c122f4f76ffad296c15ba00a13800db57 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 2 Jun 2020 16:33:05 +0000 +Subject: [PATCH 1/2] screen-cast-src: Destroy hash dmabuf table after stream + +The stream will clean up the buffers, so let it do that before we +destroy them under its feet. Note that it'll only do this after the +following PipeWire commit: + + commit fbaa4ddedd84afdffca16f090dcc4b0db8ccfc29 + Author: Wim Taymans + Date: Mon Jun 1 15:36:09 2020 +0200 + + stream: allow NULL param and 0 buffers in disconnect + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283 + + +(cherry picked from commit 97175f8fa14171606ecb95d0bf107ef8b2d71b74) +--- + src/backends/meta-screen-cast-stream-src.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 0500bfec5..ff4af440c 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -988,8 +988,8 @@ meta_screen_cast_stream_src_finalize (GObject *object) + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + +- g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); + g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); ++ g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); + g_source_destroy (&priv->pipewire_source->base); +-- +2.26.2 + diff --git a/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch new file mode 100644 index 0000000..095e0d2 --- /dev/null +++ b/SOURCES/0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch @@ -0,0 +1,101 @@ +From 639b7ba7f2729a95593c0b85d4789f76152e6099 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 18 Jun 2020 21:17:29 +0200 +Subject: [PATCH] stage/x11: Check that message is WM_PROTOCOLS before assuming + so + +When a touch sequence was rejected, we'd update the event timestamps of +incoming touch events to help with implementing grabs. This was done by +sending a ClientMessage with a counter, and comparing the counter to +decide whether we're seing a replayed event or not. + +This had the unforseen consequence that we would potentially end up +destroying all actors including the stage, since, when mutter receives a +ClientMessage event, it would assume that it's a WM_PROTOCOLS event, and +handle it as such. The problem with this approach is that it would +ignore fact that there might be other ClientMessage types sent to it, +for example the touch synchronization one. What could happen is that the +touch count value would match up with the value of the WM_DELETE_WINDOW +atom, clutter would treat this as WM_PROTOCOLS:WM_DELETE_WINDOW, which +it'd translate to clutter_actor_destroy(stage). + +Destroying the stage in such a way is not expected, and caused wierd +crashes in different places depending on what was going on. + +This commit make sure we only treat WM_PROTOCOLS client messages as +WM_PROTOCOLS client messages effectively avoiding the issue. + +This fixes crashes such as: + + #0 meta_window_get_buffer_rect (window=0x0, rect=rect@entry=0x7ffd7fc62e40) at core/window.c:4396 + #1 0x00007f1e2634837f in get_top_visible_window_actor (compositor=0x297d700, compositor=0x297d700) at compositor/compositor.c:1059 + #2 meta_compositor_sync_stack (compositor=0x297d700, stack=, stack@entry=0x26e3140) at compositor/compositor.c:1176 + #3 0x00007f1e263757ac in meta_stack_tracker_sync_stack (tracker=0x297dbc0) at core/stack-tracker.c:871 + #4 0x00007f1e26375899 in stack_tracker_sync_stack_later (data=) at core/stack-tracker.c:881 + #5 0x00007f1e26376914 in run_repaint_laters (laters_list=0x7f1e2663b7d8 ) at core/util.c:809 + #6 run_all_repaint_laters (data=) at core/util.c:826 + #7 0x00007f1e26b18325 in _clutter_run_repaint_functions (flags=flags@entry=CLUTTER_REPAINT_FLAGS_PRE_PAINT) at clutter-main.c:3448 + #8 0x00007f1e26b18fc5 in master_clock_update_stages (master_clock=0x32d6a80, stages=0x4e5a740) at clutter-master-clock-default.c:437 + #9 clutter_clock_dispatch (source=, callback=, user_data=) at clutter-master-clock-default.c:567 + #10 0x00007f1e27e48049 in g_main_dispatch (context=0x225b8d0) at gmain.c:3175 + #11 g_main_context_dispatch (context=context@entry=0x225b8d0) at gmain.c:3828 + #12 0x00007f1e27e483a8 in g_main_context_iterate (context=0x225b8d0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at gmain.c:3901 + #13 0x00007f1e27e4867a in g_main_loop_run (loop=0x24e29f0) at gmain.c:4097 + #14 0x00007f1e2636a3dc in meta_run () at core/main.c:666 + #15 0x000000000040219c in main (argc=1, argv=0x7ffd7fc63238) at ../src/main.c:534 + +and + + #0 0x00007f93943c1f25 in raise () at /usr/lib/libc.so.6 + #1 0x00007f93943ab897 in abort () at /usr/lib/libc.so.6 + #2 0x00007f9393e1e062 in g_assertion_message (domain=, file=, line=, func=0x7f93933e6860 <__func__.116322> "meta_x11_get_stage_window", + #3 0x00007f9393e4ab1d in g_assertion_message_expr () + #4 0x00007f939338ecd7 in meta_x11_get_stage_window (stage=) at ../mutter/src/backends/x11/meta-stage-x11.c:923 + #5 0x00007f939339e599 in meta_backend_x11_cm_translate_device_event (x11=, device_event=0x55bc8bcfd6b0) at ../mutter/src/backends/x11/cm/meta-backend-x11-cm.c:381 + #6 0x00007f939339f2e2 in meta_backend_x11_translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:179 + #7 0x00007f939339f2e2 in translate_device_event (device_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:208 + #8 0x00007f939339f2e2 in maybe_spoof_event_as_stage_event (input_event=0x55bc8bcfd6b0, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:284 + #9 0x00007f939339f2e2 in handle_input_event (event=0x7fff62d60490, x11=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:309 + #10 0x00007f939339f2e2 in handle_host_xevent (event=0x7fff62d60490, backend=0x55bc89dd5220) at ../mutter/src/backends/x11/meta-backend-x11.c:413 + #11 0x00007f939339f2e2 in x_event_source_dispatch (source=, callback=, user_data=) at ../mutter/src/backends/x11/meta-backend-x11.c:467 + #12 0x00007f9393e6c39e in g_main_dispatch (context=0x55bc89dd03e0) at ../glib/glib/gmain.c:3179 + #13 0x00007f9393e6c39e in g_main_context_dispatch (context=context@entry=0x55bc89dd03e0) at ../glib/glib/gmain.c:3844 + #14 0x00007f9393e6e1b1 in g_main_context_iterate (context=0x55bc89dd03e0, block=block@entry=1, dispatch=dispatch@entry=1, self=) at ../glib/glib/gmain.c:3917 + #15 0x00007f9393e6f0c3 in g_main_loop_run (loop=0x55bc8a042640) at ../glib/glib/gmain.c:4111 + #16 0x00007f9393369a0c in meta_run () at ../mutter/src/core/main.c:676 + #17 0x000055bc880f2426 in main (argc=, argv=) at ../gnome-shell/src/main.c:552 + +Related: https://gitlab.gnome.org/GNOME/mutter/-/issues/338 +Closes: https://gitlab.gnome.org/GNOME/mutter/-/issues/951 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1317 +--- + clutter/clutter/x11/clutter-stage-x11.c | 11 +++++++---- + 1 file changed, 7 insertions(+), 4 deletions(-) + +diff --git a/clutter/clutter/x11/clutter-stage-x11.c b/clutter/clutter/x11/clutter-stage-x11.c +index d043bcf31d..123078fc22 100644 +--- a/clutter/clutter/x11/clutter-stage-x11.c ++++ b/clutter/clutter/x11/clutter-stage-x11.c +@@ -1306,11 +1306,14 @@ clutter_stage_x11_translate_event (ClutterEventTranslator *translator, + _clutter_actor_get_debug_name (CLUTTER_ACTOR (stage)), + stage, + (unsigned int) stage_xwindow); +- if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) ++ if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS) + { +- event->any.type = CLUTTER_DELETE; +- event->any.stage = stage; +- res = CLUTTER_TRANSLATE_QUEUE; ++ if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) ++ { ++ event->any.type = CLUTTER_DELETE; ++ event->any.stage = stage; ++ res = CLUTTER_TRANSLATE_QUEUE; ++ } + } + break; + +-- +2.26.2 + diff --git a/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch new file mode 100644 index 0000000..df533eb --- /dev/null +++ b/SOURCES/0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch @@ -0,0 +1,35 @@ +From 24ddf60768412fd3f5f7b432449b9ed2ea0d18b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 18 Feb 2020 23:01:28 +0100 +Subject: [PATCH] window-actor: Don't show actor until meta_window_actor_show() + +By default clutter will show an actor as it is added to a parent. This +means that after we create the window actor, when it's added to the +window group, we implicitly show it. What we really want is to not show +it until the window is supposed to be shown, which happens when +meta_window_actor_show() is called, as showing prior to that, could +cause issues. + +Avoid the implicit show by setting the "show-on-set-parent" property on +the window actor to `FALSE` on window actor construction. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1066 +--- + src/compositor/compositor.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index a6ae55abb9..ce2c1b8a3b 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -810,6 +810,7 @@ meta_compositor_add_window (MetaCompositor *compositor, + + window_actor = g_object_new (window_actor_type, + "meta-window", window, ++ "show-on-set-parent", FALSE, + NULL); + + if (window->layer == META_LAYER_OVERRIDE_REDIRECT) +-- +2.26.2 + diff --git a/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch new file mode 100644 index 0000000..5a1c1ff --- /dev/null +++ b/SOURCES/0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch @@ -0,0 +1,28 @@ +From a192b9abd77aa14ae794850e41d210472f86b9b0 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 25 Jun 2020 10:09:48 +0200 +Subject: [PATCH 2/2] gpu-kms: Reset CRTC, mode and output list if no resources + +On device removal, the next resource retrieval will fail; handle this by +just clearing the CRTC, mode and outputs. +--- + src/backends/native/meta-gpu-kms.c | 3 +++ + 1 file changed, 3 insertions(+) + +diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c +index 93e509def5..dc93abb7b1 100644 +--- a/src/backends/native/meta-gpu-kms.c ++++ b/src/backends/native/meta-gpu-kms.c +@@ -871,6 +871,9 @@ meta_gpu_kms_read_current (MetaGpu *gpu, + local_error->message); + gpu_kms->resources_init_failed_before = TRUE; + } ++ meta_gpu_take_outputs (gpu, NULL); ++ meta_gpu_take_modes (gpu, NULL); ++ meta_gpu_take_crtcs (gpu, NULL); + return TRUE; + } + +-- +2.26.2 + diff --git a/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch new file mode 100644 index 0000000..bd9f592 --- /dev/null +++ b/SOURCES/0002-renderer-native-Add-API-to-get-primary-GPU.patch @@ -0,0 +1,46 @@ +From 63455680096e015eaf023760466593b6f42f9cf5 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 12 Sep 2019 11:50:34 +0200 +Subject: [PATCH 2/4] renderer/native: Add API to get primary GPU + +Will be used when acquiring scanouts from Wayland buffers. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/798 +--- + src/backends/native/meta-renderer-native.c | 6 ++++++ + src/backends/native/meta-renderer-native.h | 2 ++ + 2 files changed, 8 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index dbb88edb8e..25833b6cf6 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -317,6 +317,12 @@ meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms) + return renderer_gpu_data->gbm.device; + } + ++MetaGpuKms * ++meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native) ++{ ++ return renderer_native->primary_gpu_kms; ++} ++ + static MetaRendererNativeGpuData * + meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms) + { +diff --git a/src/backends/native/meta-renderer-native.h b/src/backends/native/meta-renderer-native.h +index 9eecdead1c..b59366e267 100644 +--- a/src/backends/native/meta-renderer-native.h ++++ b/src/backends/native/meta-renderer-native.h +@@ -53,6 +53,8 @@ struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms); + + gboolean meta_renderer_native_supports_mirroring (MetaRendererNative *renderer_native); + ++MetaGpuKms * meta_renderer_native_get_primary_gpu (MetaRendererNative *renderer_native); ++ + void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); + + int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); +-- +2.26.2 + diff --git a/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch new file mode 100644 index 0000000..4e383f2 --- /dev/null +++ b/SOURCES/0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch @@ -0,0 +1,44 @@ +From 2e4d3e22aff7cc8e41fa08d798c55a39a542476c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 2 Jun 2020 18:34:57 +0200 +Subject: [PATCH 2/2] renderer-native: Don't leak DMA buffer CoglFramebuffer + +When we created the DMA buffer backed CoglFramebuffer, we handed it over +to CoglDmaBufHandle which took its own reference. What we failed to do +was to release our own reference to it, effectively leaking it. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283 +(cherry picked from commit c823b5ddba18d30e1fdb74d6764cd40637dc4054) +--- + src/backends/native/meta-renderer-native.c | 8 ++++++-- + 1 file changed, 6 insertions(+), 2 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index ba98de650..dbb88edb8 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2633,6 +2633,7 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + case META_RENDERER_NATIVE_MODE_GBM: + { + CoglFramebuffer *dmabuf_fb; ++ CoglDmaBufHandle *dmabuf_handle; + struct gbm_bo *new_bo; + int dmabuf_fd = -1; + +@@ -2669,8 +2670,11 @@ meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, + if (!dmabuf_fb) + return NULL; + +- return cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, +- (GDestroyNotify) gbm_bo_destroy); ++ dmabuf_handle = ++ cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, ++ (GDestroyNotify) gbm_bo_destroy); ++ cogl_object_unref (dmabuf_fb); ++ return dmabuf_handle; + } + break; + #ifdef HAVE_EGL_DEVICE +-- +2.26.2 + diff --git a/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch new file mode 100644 index 0000000..8ef52a5 --- /dev/null +++ b/SOURCES/0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch @@ -0,0 +1,108 @@ +From 914fd2bec65c2e9928b03d5bc94930bc0151f998 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Wed, 17 Jun 2020 17:48:05 +0200 +Subject: [PATCH 3/4] screen-cast: Move DMA buffer allocation to MetaScreenCast + +The aim with centralizing it is to be able to apply global policy to DMA +buffer allocations, e.g. disabling due to various hueristics. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-screen-cast-stream-src.c | 18 ++++++-------- + src/backends/meta-screen-cast.c | 28 ++++++++++++++++++++++ + src/backends/meta-screen-cast.h | 4 ++++ + 3 files changed, 39 insertions(+), 11 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index ff4af440c1..b77186415f 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -649,10 +649,10 @@ on_stream_add_buffer (void *data, + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +- CoglContext *context = +- clutter_backend_get_cogl_context (clutter_get_default_backend ()); +- CoglRenderer *renderer = cogl_context_get_renderer (context); +- g_autoptr (GError) error = NULL; ++ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); ++ MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); ++ MetaScreenCast *screen_cast = ++ meta_screen_cast_session_get_screen_cast (session); + CoglDmaBufHandle *dmabuf_handle; + struct spa_buffer *spa_buffer = buffer->buffer; + struct spa_data *spa_data = spa_buffer->datas; +@@ -664,13 +664,9 @@ on_stream_add_buffer (void *data, + spa_data[0].mapoffset = 0; + spa_data[0].maxsize = stride * priv->video_format.size.height; + +- dmabuf_handle = cogl_renderer_create_dma_buf (renderer, +- priv->stream_width, +- priv->stream_height, +- &error); +- +- if (error) +- g_debug ("Error exporting DMA buffer handle: %s", error->message); ++ dmabuf_handle = meta_screen_cast_create_dma_buf_handle (screen_cast, ++ priv->stream_width, ++ priv->stream_height); + + if (dmabuf_handle) + { +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 46bc268389..5f1ca8b5ca 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -94,6 +94,34 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast) + return screen_cast->backend; + } + ++CoglDmaBufHandle * ++meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, ++ int width, ++ int height) ++{ ++ ClutterBackend *clutter_backend = ++ meta_backend_get_clutter_backend (screen_cast->backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); ++ g_autoptr (GError) error = NULL; ++ CoglDmaBufHandle *dmabuf_handle; ++ ++ dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer, ++ width, height, ++ &error); ++ if (!dmabuf_handle) ++ { ++ g_warning ("Failed to allocate DMA buffer, " ++ "disabling DMA buffer based screen casting: %s", ++ error->message); ++ screen_cast->disable_dma_bufs = TRUE; ++ return NULL; ++ } ++ ++ return dmabuf_handle; ++} ++ + static gboolean + register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, + const char *remote_desktop_session_id, +diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h +index a3b650cd80..fb5a38f34f 100644 +--- a/src/backends/meta-screen-cast.h ++++ b/src/backends/meta-screen-cast.h +@@ -50,6 +50,10 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + + MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); + ++CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, ++ int width, ++ int height); ++ + MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, + MetaDbusSessionWatcher *session_watcher); + +-- +2.26.2 + diff --git a/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch new file mode 100644 index 0000000..6963888 --- /dev/null +++ b/SOURCES/0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch @@ -0,0 +1,209 @@ +From a239886e159e6609c3e298effbd0243af8d0e333 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 16 Jun 2020 11:30:11 +0200 +Subject: [PATCH 4/4] screen-cast: Disable DMA buffer based screen casting for + QXL + +QXL doesn't support mmap():ing a DMA buffer allocated in mutter inside +the PipeWire stream consumer process. To make screen casting work again +on QXL, disable DMA buffer based screen casting for QXL. + +Eventually, it should be the client that renegotiates the supported +buffer types, but until then we need this list. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 +--- + src/backends/meta-screen-cast.c | 11 ++++++ + src/backends/meta-screen-cast.h | 2 ++ + src/backends/native/meta-backend-native.c | 41 +++++++++++++++++++++++ + src/backends/native/meta-gpu-kms.c | 26 ++++++++++++++ + src/backends/native/meta-gpu-kms.h | 2 ++ + 5 files changed, 82 insertions(+) + +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 5f1ca8b5ca..268155e7b3 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -46,6 +46,8 @@ struct _MetaScreenCast + + MetaDbusSessionWatcher *session_watcher; + MetaBackend *backend; ++ ++ gboolean disable_dma_bufs; + }; + + static void +@@ -94,6 +96,12 @@ meta_screen_cast_get_backend (MetaScreenCast *screen_cast) + return screen_cast->backend; + } + ++void ++meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast) ++{ ++ screen_cast->disable_dma_bufs = TRUE; ++} ++ + CoglDmaBufHandle * + meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + int width, +@@ -107,6 +115,9 @@ meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + g_autoptr (GError) error = NULL; + CoglDmaBufHandle *dmabuf_handle; + ++ if (screen_cast->disable_dma_bufs) ++ return NULL; ++ + dmabuf_handle = cogl_renderer_create_dma_buf (cogl_renderer, + width, height, + &error); +diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h +index fb5a38f34f..fa54be729f 100644 +--- a/src/backends/meta-screen-cast.h ++++ b/src/backends/meta-screen-cast.h +@@ -50,6 +50,8 @@ GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + + MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); + ++void meta_screen_cast_disable_dma_bufs (MetaScreenCast *screen_cast); ++ + CoglDmaBufHandle * meta_screen_cast_create_dma_buf_handle (MetaScreenCast *screen_cast, + int width, + int height); +diff --git a/src/backends/native/meta-backend-native.c b/src/backends/native/meta-backend-native.c +index c473681cb0..2bf7f5e7e2 100644 +--- a/src/backends/native/meta-backend-native.c ++++ b/src/backends/native/meta-backend-native.c +@@ -57,6 +57,10 @@ + #include "core/meta-border.h" + #include "meta/main.h" + ++#ifdef HAVE_REMOTE_DESKTOP ++#include "backends/meta-screen-cast.h" ++#endif ++ + struct _MetaBackendNative + { + MetaBackend parent; +@@ -327,6 +331,39 @@ meta_backend_native_create_clutter_backend (MetaBackend *backend) + return g_object_new (META_TYPE_CLUTTER_BACKEND_NATIVE, NULL); + } + ++#ifdef HAVE_REMOTE_DESKTOP ++static void ++maybe_disable_screen_cast_dma_bufs (MetaBackendNative *native) ++{ ++ MetaBackend *backend = META_BACKEND (native); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); ++ MetaGpuKms *primary_gpu; ++ const char *driver_name; ++ int i; ++ static const char *disable_dma_buf_drivers[] = { ++ "qxl", ++ }; ++ ++ primary_gpu = meta_renderer_native_get_primary_gpu (renderer_native); ++ driver_name = meta_gpu_kms_get_driver_name (primary_gpu); ++ ++ for (i = 0; i < G_N_ELEMENTS (disable_dma_buf_drivers); i++) ++ { ++ if (g_strcmp0 (driver_name, disable_dma_buf_drivers[i]) == 0) ++ { ++ MetaScreenCast *screen_cast = meta_backend_get_screen_cast (backend); ++ ++ g_message ("The '%s' driver doesn't support DMA buffer screen sharing, disabling.", ++ driver_name); ++ ++ meta_screen_cast_disable_dma_bufs (screen_cast); ++ return; ++ } ++ } ++} ++#endif /* HAVE_REMOTE_DESKTOP */ ++ + static void + meta_backend_native_post_init (MetaBackend *backend) + { +@@ -338,6 +375,10 @@ meta_backend_native_post_init (MetaBackend *backend) + NULL, NULL); + clutter_evdev_set_relative_motion_filter (manager, relative_motion_filter, + meta_backend_get_monitor_manager (backend)); ++ ++#ifdef HAVE_REMOTE_DESKTOP ++ maybe_disable_screen_cast_dma_bufs (META_BACKEND_NATIVE (backend)); ++#endif + } + + static MetaMonitorManager * +diff --git a/src/backends/native/meta-gpu-kms.c b/src/backends/native/meta-gpu-kms.c +index c569b948ef..93e509def5 100644 +--- a/src/backends/native/meta-gpu-kms.c ++++ b/src/backends/native/meta-gpu-kms.c +@@ -66,6 +66,8 @@ struct _MetaGpuKms + char *file_path; + GSource *source; + ++ char *driver_name; ++ + clockid_t clock_id; + + drmModeConnector **connectors; +@@ -790,6 +792,27 @@ init_outputs (MetaGpuKms *gpu_kms, + setup_output_clones (gpu); + } + ++static void ++init_info (MetaGpuKms *gpu_kms) ++{ ++ drmVersion *drm_version; ++ ++ drm_version = drmGetVersion (gpu_kms->fd); ++ if (!drm_version) ++ return; ++ ++ gpu_kms->driver_name = g_strndup (drm_version->name, ++ drm_version->name_len); ++ ++ drmFreeVersion (drm_version); ++} ++ ++const char * ++meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms) ++{ ++ return gpu_kms->driver_name; ++} ++ + static gboolean + meta_kms_resources_init (MetaKmsResources *resources, + int fd, +@@ -865,6 +888,7 @@ meta_gpu_kms_read_current (MetaGpu *gpu, + init_crtcs (gpu_kms, &resources); + init_outputs (gpu_kms, &resources); + init_frame_clock (gpu_kms); ++ init_info (gpu_kms); + + meta_kms_resources_release (&resources); + +@@ -940,6 +964,8 @@ meta_gpu_kms_finalize (GObject *object) + + free_resources (gpu_kms); + ++ g_free (gpu_kms->driver_name); ++ + G_OBJECT_CLASS (meta_gpu_kms_parent_class)->finalize (object); + } + +diff --git a/src/backends/native/meta-gpu-kms.h b/src/backends/native/meta-gpu-kms.h +index 1f7a939e27..6096e58341 100644 +--- a/src/backends/native/meta-gpu-kms.h ++++ b/src/backends/native/meta-gpu-kms.h +@@ -108,4 +108,6 @@ MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu + + void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container); + ++const char * meta_gpu_kms_get_driver_name (MetaGpuKms *gpu_kms); ++ + #endif /* META_GPU_KMS_H */ +-- +2.26.2 + diff --git a/SOURCES/screen-cast-remote-desktop-improvements.patch b/SOURCES/screen-cast-remote-desktop-improvements.patch new file mode 100644 index 0000000..954602c --- /dev/null +++ b/SOURCES/screen-cast-remote-desktop-improvements.patch @@ -0,0 +1,5977 @@ +From ac409e38b820ebf07a5677a3b393933dd3cf668d Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 18:24:02 -0300 +Subject: [PATCH 01/49] clutter/stage-view: Move unexported functions to + private header + +Next commits will expose ClutterStageView as a public class, so +move the functions private to Clutter to a private header. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage-view-private.h | 37 ++++++++++++++++++++ + clutter/clutter/clutter-stage-view.c | 1 + + clutter/clutter/clutter-stage-view.h | 13 ------- + clutter/clutter/clutter-stage.c | 1 + + clutter/clutter/cogl/clutter-stage-cogl.c | 1 + + clutter/clutter/meson.build | 1 + + 6 files changed, 41 insertions(+), 13 deletions(-) + create mode 100644 clutter/clutter/clutter-stage-view-private.h + +diff --git a/clutter/clutter/clutter-stage-view-private.h b/clutter/clutter/clutter-stage-view-private.h +new file mode 100644 +index 000000000..89c42599f +--- /dev/null ++++ b/clutter/clutter/clutter-stage-view-private.h +@@ -0,0 +1,37 @@ ++/* ++ * Copyright (C) 2019 Red Hat Inc. ++ * ++ * This library is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU Lesser General Public ++ * License as published by the Free Software Foundation; either ++ * version 2 of the License, or (at your option) any later version. ++ * ++ * This library is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ * Lesser General Public License for more details. ++ * ++ * You should have received a copy of the GNU Lesser General Public ++ * License along with this library. If not, see . ++ */ ++ ++#ifndef __CLUTTER_STAGE_VIEW_PRIVATE_H__ ++#define __CLUTTER_STAGE_VIEW_PRIVATE_H__ ++ ++#include "clutter/clutter-stage-view.h" ++ ++void clutter_stage_view_blit_offscreen (ClutterStageView *view, ++ const cairo_rectangle_int_t *clip); ++ ++gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); ++ ++void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, ++ gboolean dirty); ++ ++gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); ++ ++void clutter_stage_view_set_dirty_projection (ClutterStageView *view, ++ gboolean dirty); ++ ++ ++#endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index c536ac720..e26e2fc07 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -18,6 +18,7 @@ + #include "clutter-build-config.h" + + #include "clutter/clutter-stage-view.h" ++#include "clutter/clutter-stage-view-private.h" + + #include + #include +diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h +index 126498625..0c3448511 100644 +--- a/clutter/clutter/clutter-stage-view.h ++++ b/clutter/clutter/clutter-stage-view.h +@@ -57,22 +57,9 @@ void clutter_stage_view_transform_to_onscreen (ClutterStageView *vie + gfloat *x, + gfloat *y); + +-void clutter_stage_view_blit_offscreen (ClutterStageView *view, +- const cairo_rectangle_int_t *clip); +- + CLUTTER_EXPORT + float clutter_stage_view_get_scale (ClutterStageView *view); + +-gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); +- +-void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, +- gboolean dirty); +- +-gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); +- +-void clutter_stage_view_set_dirty_projection (ClutterStageView *view, +- gboolean dirty); +- + CLUTTER_EXPORT + void clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, + CoglMatrix *matrix); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 1eea5b305..f254b5d49 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -72,6 +72,7 @@ + #include "clutter-private.h" + #include "clutter-stage-manager-private.h" + #include "clutter-stage-private.h" ++#include "clutter-stage-view-private.h" + #include "clutter-private.h" + + #include "cogl/cogl.h" +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index eab76e52f..d942d9d41 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -45,6 +45,7 @@ + #include "clutter-main.h" + #include "clutter-private.h" + #include "clutter-stage-private.h" ++#include "clutter-stage-view-private.h" + + typedef struct _ClutterStageViewCoglPrivate + { +diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build +index 671d790df..7feed24aa 100644 +--- a/clutter/clutter/meson.build ++++ b/clutter/clutter/meson.build +@@ -206,6 +206,7 @@ clutter_private_headers = [ + 'clutter-stage-manager-private.h', + 'clutter-stage-private.h', + 'clutter-stage-view.h', ++ 'clutter-stage-view-private.h', + 'clutter-stage-window.h', + ] + +-- +2.26.2 + + +From cc2cf250f91d4b4c939aa1c6f8dd9dad4d77d975 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 18:32:12 -0300 +Subject: [PATCH 02/49] clutter/stage-view: Annotate some functions + +The GIR parser cannot figure out the ownership model of +ClutterStageView.get_framebuffer() and .get_offscreen() +without them, and throws us a couple of warnings. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage-view.c | 16 ++++++++++++++++ + 1 file changed, 16 insertions(+) + +diff --git a/clutter/clutter/clutter-stage-view.c b/clutter/clutter/clutter-stage-view.c +index e26e2fc07..0fad6fc44 100644 +--- a/clutter/clutter/clutter-stage-view.c ++++ b/clutter/clutter/clutter-stage-view.c +@@ -66,6 +66,14 @@ clutter_stage_view_get_layout (ClutterStageView *view, + *rect = priv->layout; + } + ++/** ++ * clutter_stage_view_get_framebuffer: ++ * @view: a #ClutterStageView ++ * ++ * Retrieves the framebuffer of @view to draw to. ++ * ++ * Returns: (transfer none): a #CoglFramebuffer ++ */ + CoglFramebuffer * + clutter_stage_view_get_framebuffer (ClutterStageView *view) + { +@@ -80,6 +88,14 @@ clutter_stage_view_get_framebuffer (ClutterStageView *view) + return priv->framebuffer; + } + ++/** ++ * clutter_stage_view_get_onscreen: ++ * @view: a #ClutterStageView ++ * ++ * Retrieves the onscreen framebuffer of @view if available. ++ * ++ * Returns: (transfer none): a #CoglFramebuffer ++ */ + CoglFramebuffer * + clutter_stage_view_get_onscreen (ClutterStageView *view) + { +-- +2.26.2 + + +From c21e563398edccbd210defe367d89920b2d3f3d1 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 18:33:17 -0300 +Subject: [PATCH 03/49] clutter: Make ClutterStageView a public class + +As a compositor toolkit, it makes sense to allow consumers +of Clutter interact with the stage views themselves. As such, +ClutterStageView should be a public class. + +As such, it is now included in clutter.h and should not be +included directly. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage-view.h | 4 ++++ + clutter/clutter/clutter.h | 1 + + clutter/clutter/meson.build | 4 ++-- + 3 files changed, 7 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage-view.h b/clutter/clutter/clutter-stage-view.h +index 0c3448511..26bf10e79 100644 +--- a/clutter/clutter/clutter-stage-view.h ++++ b/clutter/clutter/clutter-stage-view.h +@@ -18,6 +18,10 @@ + #ifndef __CLUTTER_STAGE_VIEW_H__ + #define __CLUTTER_STAGE_VIEW_H__ + ++#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) ++#error "Only can be included directly." ++#endif ++ + #include + #include + #include +diff --git a/clutter/clutter/clutter.h b/clutter/clutter/clutter.h +index 231d8cd1b..ec846910f 100644 +--- a/clutter/clutter/clutter.h ++++ b/clutter/clutter/clutter.h +@@ -101,6 +101,7 @@ + #include "clutter-snap-constraint.h" + #include "clutter-stage.h" + #include "clutter-stage-manager.h" ++#include "clutter-stage-view.h" + #include "clutter-tap-action.h" + #include "clutter-test-utils.h" + #include "clutter-texture.h" +diff --git a/clutter/clutter/meson.build b/clutter/clutter/meson.build +index 7feed24aa..8e0484453 100644 +--- a/clutter/clutter/meson.build ++++ b/clutter/clutter/meson.build +@@ -75,6 +75,7 @@ clutter_headers = [ + 'clutter-snap-constraint.h', + 'clutter-stage.h', + 'clutter-stage-manager.h', ++ 'clutter-stage-view.h', + 'clutter-tap-action.h', + 'clutter-test-utils.h', + 'clutter-texture.h', +@@ -163,6 +164,7 @@ clutter_sources = [ + 'clutter-snap-constraint.c', + 'clutter-stage.c', + 'clutter-stage-manager.c', ++ 'clutter-stage-view.c', + 'clutter-stage-window.c', + 'clutter-tap-action.c', + 'clutter-test-utils.c', +@@ -205,7 +207,6 @@ clutter_private_headers = [ + 'clutter-settings-private.h', + 'clutter-stage-manager-private.h', + 'clutter-stage-private.h', +- 'clutter-stage-view.h', + 'clutter-stage-view-private.h', + 'clutter-stage-window.h', + ] +@@ -214,7 +215,6 @@ clutter_nonintrospected_sources = [ + 'clutter-easing.c', + 'clutter-event-translator.c', + 'clutter-id-pool.c', +- 'clutter-stage-view.c', + ] + + clutter_deprecated_headers = [ +-- +2.26.2 + + +From dfb1b9a61f2909be14be5a152f5eacb93cddafa2 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 18:39:34 -0300 +Subject: [PATCH 04/49] clutter/stage: Own clutter_stage_get_view_at() + +This function is exported as a Mutter-specific function, +but now that ClutterStageView is part of the public API, +ClutterStage can own this function. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage.h | 5 +++++ + 1 file changed, 5 insertions(+) + +diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h +index 5730af7bd..9d0fdd362 100644 +--- a/clutter/clutter/clutter-stage.h ++++ b/clutter/clutter/clutter-stage.h +@@ -30,6 +30,7 @@ + + #include + #include ++#include + + G_BEGIN_DECLS + +@@ -274,6 +275,10 @@ gboolean clutter_stage_capture (ClutterStage *stage, + cairo_rectangle_int_t *rect, + ClutterCapture **captures, + int *n_captures); ++CLUTTER_EXPORT ++ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage, ++ float x, ++ float y); + + G_END_DECLS + +-- +2.26.2 + + +From 29cd64c50a054384bf737ff12ee62770ae20b305 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 19:16:47 -0300 +Subject: [PATCH 05/49] clutter/stage: Emit after-paint after painting + +ClutterStage:after-paint is supposed to be emitted after all +painting is done, but before the frame is finished. However, +as it is right now, it is being emitted after each view is +painted -- on multi-monitor setups, after-frame is being +emitted multiple times. + +Send after-paint only once, after all views are painted and +before finishing the frame. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage-private.h | 1 + + clutter/clutter/clutter-stage.c | 5 +++++ + clutter/clutter/cogl/clutter-stage-cogl.c | 2 ++ + 3 files changed, 8 insertions(+) + +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index 4799c29e1..df0bf642b 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -40,6 +40,7 @@ void _clutter_stage_paint_view (ClutterStage + ClutterStageView *view, + const cairo_rectangle_int_t *clip); + ++void _clutter_stage_emit_after_paint (ClutterStage *stage); + void _clutter_stage_set_window (ClutterStage *stage, + ClutterStageWindow *stage_window); + ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index f254b5d49..2b437e1f6 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -690,6 +690,11 @@ _clutter_stage_paint_view (ClutterStage *stage, + return; + + clutter_stage_do_paint_view (stage, view, clip); ++} ++ ++void ++_clutter_stage_emit_after_paint (ClutterStage *stage) ++{ + g_signal_emit (stage, stage_signals[AFTER_PAINT], 0); + } + +diff --git a/clutter/clutter/cogl/clutter-stage-cogl.c b/clutter/clutter/cogl/clutter-stage-cogl.c +index d942d9d41..005c6f692 100644 +--- a/clutter/clutter/cogl/clutter-stage-cogl.c ++++ b/clutter/clutter/cogl/clutter-stage-cogl.c +@@ -936,6 +936,8 @@ clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) + clutter_stage_cogl_redraw_view (stage_window, view) || swap_event; + } + ++ _clutter_stage_emit_after_paint (stage_cogl->wrapper); ++ + _clutter_stage_window_finish_frame (stage_window); + + if (swap_event) +-- +2.26.2 + + +From 152945e1061d37d8e5899d2df9a24c25f46096ed Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 21:33:42 -0300 +Subject: [PATCH 06/49] clutter/stage: Add ClutterStage:paint-view + +Now that ClutterStageView is embraced as part of the public +set of Clutter classes, is it possible to give consumers +of this API more information and control over the drawing +routines of ClutterStage. + +Introduce ClutterStage:paint-view, a signal that is emitted +for painting a specific view. It's defined as a RUN_LAST +signal to give anyone connecting to it the ability to run +before the view is actually painted, or after (using the +G_CONNECT_AFTER flag, or g_signal_connect_after). + +This signal has a corresponding class handler, which allows +Mutter to have much finer control over the painting routines. +In fact, this will allow us to implement a "paint phase watcher" +mechanism in the following patches. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/clutter/clutter-stage.c | 47 ++++++++++++++++++++++++++++++++- + clutter/clutter/clutter-stage.h | 5 +++- + 2 files changed, 50 insertions(+), 2 deletions(-) + +diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c +index 2b437e1f6..34c4e0119 100644 +--- a/clutter/clutter/clutter-stage.c ++++ b/clutter/clutter/clutter-stage.c +@@ -148,6 +148,8 @@ struct _ClutterStagePrivate + gpointer paint_data; + GDestroyNotify paint_notify; + ++ cairo_rectangle_int_t view_clip; ++ + int update_freeze_count; + + guint relayout_pending : 1; +@@ -192,6 +194,7 @@ enum + DEACTIVATE, + DELETE_EVENT, + AFTER_PAINT, ++ PAINT_VIEW, + PRESENTED, + + LAST_SIGNAL +@@ -689,7 +692,15 @@ _clutter_stage_paint_view (ClutterStage *stage, + if (!priv->impl) + return; + +- clutter_stage_do_paint_view (stage, view, clip); ++ priv->view_clip = *clip; ++ ++ if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW], ++ 0, TRUE)) ++ g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view); ++ else ++ CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view); ++ ++ priv->view_clip = (cairo_rectangle_int_t) { 0 }; + } + + void +@@ -1901,6 +1912,16 @@ clutter_stage_finalize (GObject *object) + G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); + } + ++static void ++clutter_stage_real_paint_view (ClutterStage *stage, ++ ClutterStageView *view) ++{ ++ ClutterStagePrivate *priv = stage->priv; ++ const cairo_rectangle_int_t *clip = &priv->view_clip; ++ ++ clutter_stage_do_paint_view (stage, view, clip); ++} ++ + static void + clutter_stage_class_init (ClutterStageClass *klass) + { +@@ -1930,6 +1951,8 @@ clutter_stage_class_init (ClutterStageClass *klass) + actor_class->queue_redraw = clutter_stage_real_queue_redraw; + actor_class->apply_transform = clutter_stage_real_apply_transform; + ++ klass->paint_view = clutter_stage_real_paint_view; ++ + /** + * ClutterStage:fullscreen: + * +@@ -2257,6 +2280,28 @@ clutter_stage_class_init (ClutterStageClass *klass) + NULL, NULL, NULL, + G_TYPE_NONE, 0); + ++ /** ++ * ClutterStage::paint-view: ++ * @stage: the stage that received the event ++ * @view: a #ClutterStageView ++ * ++ * The ::paint-view signal is emitted before a #ClutterStageView is being ++ * painted. ++ * ++ * The view is painted in the default handler. Hence, if you want to perform ++ * some action after the view is painted, like reading the contents of the ++ * framebuffer, use g_signal_connect_after() or pass %G_CONNECT_AFTER. ++ */ ++ stage_signals[PAINT_VIEW] = ++ g_signal_new (I_("paint-view"), ++ G_TYPE_FROM_CLASS (gobject_class), ++ G_SIGNAL_RUN_LAST, ++ G_STRUCT_OFFSET (ClutterStageClass, paint_view), ++ NULL, NULL, ++ _clutter_marshal_VOID__OBJECT, ++ G_TYPE_NONE, 1, ++ CLUTTER_TYPE_STAGE_VIEW); ++ + /** + * ClutterStage::presented: (skip) + * @stage: the stage that received the event +diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h +index 9d0fdd362..9da63d211 100644 +--- a/clutter/clutter/clutter-stage.h ++++ b/clutter/clutter/clutter-stage.h +@@ -88,9 +88,12 @@ struct _ClutterStageClass + gboolean (* delete_event) (ClutterStage *stage, + ClutterEvent *event); + ++ void (* paint_view) (ClutterStage *stage, ++ ClutterStageView *view); ++ + /*< private >*/ + /* padding for future expansion */ +- gpointer _padding_dummy[31]; ++ gpointer _padding_dummy[30]; + }; + + /** +-- +2.26.2 + + +From 2da9db1546f025aa02e23136b40e96a8ba99d1c5 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 23:32:00 -0300 +Subject: [PATCH 07/49] clutter/tests: Connect to ClutterStage:paint-view + +ClutterStage:after-paint now does not guarantee a valid +implicit framebuffer pushed to the stack. Instead, use +the new 'paint-view' signal, that is emitted at a point +in the drawing routine where a framebuffer is pushed. + +In addition to that, stop using the implicit framebuffer +API and port the actor-shader-effect test to read from +the view's framebuffer directly. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + clutter/tests/conform/actor-shader-effect.c | 32 ++++++++++++--------- + 1 file changed, 18 insertions(+), 14 deletions(-) + +diff --git a/clutter/tests/conform/actor-shader-effect.c b/clutter/tests/conform/actor-shader-effect.c +index 93a43ea8b..ac99c5b40 100644 +--- a/clutter/tests/conform/actor-shader-effect.c ++++ b/clutter/tests/conform/actor-shader-effect.c +@@ -209,14 +209,16 @@ make_actor (GType shader_type) + } + + static guint32 +-get_pixel (int x, int y) ++get_pixel (CoglFramebuffer *fb, ++ int x, ++ int y) + { + guint8 data[4]; + +- cogl_read_pixels (x, y, 1, 1, +- COGL_READ_PIXELS_COLOR_BUFFER, +- COGL_PIXEL_FORMAT_RGBA_8888_PRE, +- data); ++ cogl_framebuffer_read_pixels (fb, ++ x, y, 1, 1, ++ COGL_PIXEL_FORMAT_RGBA_8888_PRE, ++ data); + + return (((guint32) data[0] << 16) | + ((guint32) data[1] << 8) | +@@ -224,19 +226,21 @@ get_pixel (int x, int y) + } + + static void +-paint_cb (ClutterStage *stage, +- gpointer data) ++view_painted_cb (ClutterStage *stage, ++ ClutterStageView *view, ++ gpointer data) + { ++ CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); + gboolean *was_painted = data; + + /* old shader effect */ +- g_assert_cmpint (get_pixel (0, 25), ==, 0xff0000); ++ g_assert_cmpint (get_pixel (fb, 0, 25), ==, 0xff0000); + /* new shader effect */ +- g_assert_cmpint (get_pixel (100, 25), ==, 0x00ffff); ++ g_assert_cmpint (get_pixel (fb, 100, 25), ==, 0x00ffff); + /* another new shader effect */ +- g_assert_cmpint (get_pixel (200, 25), ==, 0xff00ff); ++ g_assert_cmpint (get_pixel (fb, 200, 25), ==, 0xff00ff); + /* new shader effect */ +- g_assert_cmpint (get_pixel (300, 25), ==, 0x00ffff); ++ g_assert_cmpint (get_pixel (fb, 300, 25), ==, 0x00ffff); + + *was_painted = TRUE; + } +@@ -271,9 +275,9 @@ actor_shader_effect (void) + clutter_actor_show (stage); + + was_painted = FALSE; +- g_signal_connect (stage, "after-paint", +- G_CALLBACK (paint_cb), +- &was_painted); ++ g_signal_connect_after (stage, "paint-view", ++ G_CALLBACK (view_painted_cb), ++ &was_painted); + + while (!was_painted) + g_main_context_iteration (NULL, FALSE); +-- +2.26.2 + + +From 50271dfbdbb8926474a5cf3019c8552afd40f0da Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 21:43:05 -0300 +Subject: [PATCH 08/49] stage: Introduce MetaStageWatch and family + +MetaStageWatch, watch modes and the watch function are part +of the new stage view watching API. It's design does not +rely on signals on purpose; the number of signals that would +be emitted would be too high, and would impact performance. + +MetaStageWatch is an opaque structure outside of MetaStage. + +This will be used by the screencast code to monitor a single +view, which has a one-to-one relatioship to logical monitors. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + src/backends/meta-stage-private.h | 22 +++++++ + src/backends/meta-stage.c | 104 ++++++++++++++++++++++++++++++ + 2 files changed, 126 insertions(+) + +diff --git a/src/backends/meta-stage-private.h b/src/backends/meta-stage-private.h +index 639d2372c..963017688 100644 +--- a/src/backends/meta-stage-private.h ++++ b/src/backends/meta-stage-private.h +@@ -27,8 +27,21 @@ + + G_BEGIN_DECLS + ++typedef struct _MetaStageWatch MetaStageWatch; + typedef struct _MetaOverlay MetaOverlay; + ++typedef enum ++{ ++ META_STAGE_WATCH_BEFORE_PAINT, ++ META_STAGE_WATCH_AFTER_ACTOR_PAINT, ++ META_STAGE_WATCH_AFTER_OVERLAY_PAINT, ++ META_STAGE_WATCH_AFTER_PAINT, ++} MetaStageWatchPhase; ++ ++typedef void (* MetaStageWatchFunc) (MetaStage *stage, ++ ClutterStageView *view, ++ gpointer user_data); ++ + ClutterActor *meta_stage_new (MetaBackend *backend); + + MetaOverlay *meta_stage_create_cursor_overlay (MetaStage *stage); +@@ -43,6 +56,15 @@ void meta_stage_update_cursor_overlay (MetaStage *stage, + void meta_stage_set_active (MetaStage *stage, + gboolean is_active); + ++MetaStageWatch * meta_stage_watch_view (MetaStage *stage, ++ ClutterStageView *view, ++ MetaStageWatchPhase watch_mode, ++ MetaStageWatchFunc callback, ++ gpointer user_data); ++ ++void meta_stage_remove_watch (MetaStage *stage, ++ MetaStageWatch *watch); ++ + G_END_DECLS + + #endif /* META_STAGE_PRIVATE_H */ +diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c +index 47a00e51a..8809035d1 100644 +--- a/src/backends/meta-stage.c ++++ b/src/backends/meta-stage.c +@@ -30,6 +30,8 @@ + #include "meta/meta-monitor-manager.h" + #include "meta/util.h" + ++#define N_WATCH_MODES 4 ++ + enum + { + ACTORS_PAINTED, +@@ -39,6 +41,13 @@ enum + + static guint signals[N_SIGNALS]; + ++struct _MetaStageWatch ++{ ++ ClutterStageView *view; ++ MetaStageWatchFunc callback; ++ gpointer user_data; ++}; ++ + struct _MetaOverlay + { + gboolean enabled; +@@ -55,6 +64,9 @@ struct _MetaStage + { + ClutterStage parent; + ++ GPtrArray *watchers[N_WATCH_MODES]; ++ ClutterStageView *current_view; ++ + GList *overlays; + gboolean is_active; + }; +@@ -135,6 +147,7 @@ meta_stage_finalize (GObject *object) + { + MetaStage *stage = META_STAGE (object); + GList *l; ++ int i; + + l = stage->overlays; + while (l) +@@ -143,9 +156,33 @@ meta_stage_finalize (GObject *object) + l = g_list_delete_link (l, l); + } + ++ for (i = 0; i < N_WATCH_MODES; i++) ++ g_clear_pointer (&stage->watchers[i], g_ptr_array_unref); ++ + G_OBJECT_CLASS (meta_stage_parent_class)->finalize (object); + } + ++static void ++notify_watchers_for_mode (MetaStage *stage, ++ ClutterStageView *view, ++ MetaStageWatchPhase watch_phase) ++{ ++ GPtrArray *watchers; ++ int i; ++ ++ watchers = stage->watchers[watch_phase]; ++ ++ for (i = 0; i < watchers->len; i++) ++ { ++ MetaStageWatch *watch = g_ptr_array_index (watchers, i); ++ ++ if (watch->view && view != watch->view) ++ continue; ++ ++ watch->callback (stage, view, watch->user_data); ++ } ++} ++ + static void + meta_stage_paint (ClutterActor *actor) + { +@@ -154,10 +191,30 @@ meta_stage_paint (ClutterActor *actor) + + CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor); + ++ notify_watchers_for_mode (stage, stage->current_view, ++ META_STAGE_WATCH_AFTER_ACTOR_PAINT); ++ + g_signal_emit (stage, signals[ACTORS_PAINTED], 0); + + for (l = stage->overlays; l; l = l->next) + meta_overlay_paint (l->data); ++ ++ notify_watchers_for_mode (stage, stage->current_view, ++ META_STAGE_WATCH_AFTER_OVERLAY_PAINT); ++} ++ ++static void ++meta_stage_paint_view (ClutterStage *stage, ++ ClutterStageView *view) ++{ ++ MetaStage *meta_stage = META_STAGE (stage); ++ ++ notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_BEFORE_PAINT); ++ ++ meta_stage->current_view = view; ++ CLUTTER_STAGE_CLASS (meta_stage_parent_class)->paint_view (stage, view); ++ ++ notify_watchers_for_mode (meta_stage, view, META_STAGE_WATCH_AFTER_PAINT); + } + + static void +@@ -202,6 +259,7 @@ meta_stage_class_init (MetaStageClass *klass) + + stage_class->activate = meta_stage_activate; + stage_class->deactivate = meta_stage_deactivate; ++ stage_class->paint_view = meta_stage_paint_view; + + signals[ACTORS_PAINTED] = g_signal_new ("actors-painted", + G_TYPE_FROM_CLASS (klass), +@@ -214,7 +272,12 @@ meta_stage_class_init (MetaStageClass *klass) + static void + meta_stage_init (MetaStage *stage) + { ++ int i; ++ + clutter_stage_set_user_resizable (CLUTTER_STAGE (stage), FALSE); ++ ++ for (i = 0; i < N_WATCH_MODES; i++) ++ stage->watchers[i] = g_ptr_array_new_with_free_func (g_free); + } + + ClutterActor * +@@ -346,3 +409,44 @@ meta_stage_set_active (MetaStage *stage, + */ + clutter_stage_event (CLUTTER_STAGE (stage), &event); + } ++ ++MetaStageWatch * ++meta_stage_watch_view (MetaStage *stage, ++ ClutterStageView *view, ++ MetaStageWatchPhase watch_phase, ++ MetaStageWatchFunc callback, ++ gpointer user_data) ++{ ++ MetaStageWatch *watch; ++ GPtrArray *watchers; ++ ++ watch = g_new0 (MetaStageWatch, 1); ++ watch->view = view; ++ watch->callback = callback; ++ watch->user_data = user_data; ++ ++ watchers = stage->watchers[watch_phase]; ++ g_ptr_array_add (watchers, watch); ++ ++ return watch; ++} ++ ++void ++meta_stage_remove_watch (MetaStage *stage, ++ MetaStageWatch *watch) ++{ ++ GPtrArray *watchers; ++ gboolean removed = FALSE; ++ int i; ++ ++ for (i = 0; i < N_WATCH_MODES; i++) ++ { ++ watchers = stage->watchers[i]; ++ removed = g_ptr_array_remove_fast (watchers, watch); ++ ++ if (removed) ++ break; ++ } ++ ++ g_assert (removed); ++} +-- +2.26.2 + + +From 8c509454f193945f09ef7afb5a397df266d2fffd Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 17 Jun 2019 21:49:45 -0300 +Subject: [PATCH 09/49] screen-cast-monitor-stream-src: Watch monitors using + MetaStageWatch + +This uses the API introduced by the previous commit. By watching specific +monitors directly, and not whole stage views, we avoid showing artifacts +on multi-monitor setups. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/424 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/623 +--- + .../meta-screen-cast-monitor-stream-src.c | 65 +++++++++++++------ + 1 file changed, 44 insertions(+), 21 deletions(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index cb9823148..f582217e5 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -32,6 +32,7 @@ + #include "backends/meta-monitor.h" + #include "backends/meta-screen-cast-monitor-stream.h" + #include "backends/meta-screen-cast-session.h" ++#include "backends/meta-stage-private.h" + #include "clutter/clutter.h" + #include "clutter/clutter-mutter.h" + #include "core/boxes-private.h" +@@ -42,8 +43,9 @@ struct _MetaScreenCastMonitorStreamSrc + + gboolean cursor_bitmap_invalid; + +- gulong actors_painted_handler_id; +- gulong paint_handler_id; ++ MetaStageWatch *paint_watch; ++ MetaStageWatch *after_paint_watch; ++ + gulong cursor_moved_handler_id; + gulong cursor_changed_handler_id; + }; +@@ -113,10 +115,11 @@ meta_screen_cast_monitor_stream_src_get_specs (MetaScreenCastStreamSrc *src, + } + + static void +-stage_painted (ClutterActor *actor, +- MetaScreenCastMonitorStreamSrc *monitor_src) ++stage_painted (MetaStage *stage, ++ ClutterStageView *view, ++ gpointer user_data) + { +- MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); ++ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data); + + meta_screen_cast_stream_src_maybe_record_frame (src); + } +@@ -245,12 +248,28 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); + MetaBackend *backend = get_backend (monitor_src); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ++ MetaRendererView *view; ++ MetaMonitor *monitor; ++ MetaLogicalMonitor *logical_monitor; ++ MetaStage *meta_stage; ++ ClutterStageView *stage_view; + ClutterStage *stage; + MetaScreenCastStream *stream; + + stream = meta_screen_cast_stream_src_get_stream (src); + stage = get_stage (monitor_src); ++ meta_stage = META_STAGE (stage); ++ monitor = get_monitor (monitor_src); ++ logical_monitor = meta_monitor_get_logical_monitor (monitor); ++ view = meta_renderer_get_view_from_logical_monitor (renderer, ++ logical_monitor); ++ ++ if (view) ++ stage_view = CLUTTER_STAGE_VIEW (view); ++ else ++ stage_view = NULL; + + switch (meta_screen_cast_stream_get_cursor_mode (stream)) + { +@@ -265,17 +284,21 @@ meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) + monitor_src); + /* Intentional fall-through */ + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: +- monitor_src->actors_painted_handler_id = +- g_signal_connect (stage, "actors-painted", +- G_CALLBACK (stage_painted), +- monitor_src); ++ monitor_src->paint_watch = ++ meta_stage_watch_view (meta_stage, ++ stage_view, ++ META_STAGE_WATCH_AFTER_ACTOR_PAINT, ++ stage_painted, ++ monitor_src); + break; + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + inhibit_hw_cursor (monitor_src); +- monitor_src->paint_handler_id = +- g_signal_connect_after (stage, "paint", +- G_CALLBACK (stage_painted), +- monitor_src); ++ monitor_src->after_paint_watch = ++ meta_stage_watch_view (meta_stage, ++ stage_view, ++ META_STAGE_WATCH_AFTER_PAINT, ++ stage_painted, ++ monitor_src); + break; + } + +@@ -290,21 +313,21 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) + MetaBackend *backend = get_backend (monitor_src); + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); + ClutterStage *stage; ++ MetaStage *meta_stage; + + stage = get_stage (monitor_src); ++ meta_stage = META_STAGE (stage); + +- if (monitor_src->actors_painted_handler_id) ++ if (monitor_src->paint_watch) + { +- g_signal_handler_disconnect (stage, +- monitor_src->actors_painted_handler_id); +- monitor_src->actors_painted_handler_id = 0; ++ meta_stage_remove_watch (meta_stage, monitor_src->paint_watch); ++ monitor_src->paint_watch = NULL; + } + +- if (monitor_src->paint_handler_id) ++ if (monitor_src->after_paint_watch) + { +- g_signal_handler_disconnect (stage, +- monitor_src->paint_handler_id); +- monitor_src->paint_handler_id = 0; ++ meta_stage_remove_watch (meta_stage, monitor_src->after_paint_watch); ++ monitor_src->after_paint_watch = NULL; + uninhibit_hw_cursor (monitor_src); + } + +-- +2.26.2 + + +From 547e3f004f9d9235efc5dfd816f4a1214cf44616 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 26 Aug 2019 16:09:53 +0300 +Subject: [PATCH 10/49] window-actor: Add 'damaged' signal + +Make it possible to listen for damage on a window actor. For X11, the +signal is emitted when damage is reported; for Wayland, it is emitted +when any of the surfaces associated with the window is damaged. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 +--- + src/compositor/meta-window-actor-private.h | 2 ++ + src/compositor/meta-window-actor.c | 23 ++++++++++++++++++ + src/wayland/meta-wayland-surface.c | 27 +++++++++++++++++++--- + 3 files changed, 49 insertions(+), 3 deletions(-) + +diff --git a/src/compositor/meta-window-actor-private.h b/src/compositor/meta-window-actor-private.h +index 9c1c12d09..3df520ac5 100644 +--- a/src/compositor/meta-window-actor-private.h ++++ b/src/compositor/meta-window-actor-private.h +@@ -81,4 +81,6 @@ void meta_window_actor_stereo_notify (MetaWindowActor *actor, + + gboolean meta_window_actor_is_stereo (MetaWindowActor *actor); + ++void meta_window_actor_notify_damaged (MetaWindowActor *window_actor); ++ + #endif /* META_WINDOW_ACTOR_PRIVATE_H */ +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 11686d00b..f4eba6d42 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -116,6 +116,7 @@ enum + { + FIRST_FRAME, + EFFECTS_COMPLETED, ++ DAMAGED, + + LAST_SIGNAL + }; +@@ -226,6 +227,20 @@ meta_window_actor_class_init (MetaWindowActorClass *klass) + NULL, NULL, NULL, + G_TYPE_NONE, 0); + ++ /** ++ * MetaWindowActor::damaged: ++ * @actor: the #MetaWindowActor instance ++ * ++ * Notify that one or more of the surfaces of the window have been damaged. ++ */ ++ signals[DAMAGED] = ++ g_signal_new ("damaged", ++ G_TYPE_FROM_CLASS (object_class), ++ G_SIGNAL_RUN_LAST, ++ 0, ++ NULL, NULL, NULL, ++ G_TYPE_NONE, 0); ++ + pspec = g_param_spec_object ("meta-window", + "MetaWindow", + "The displayed MetaWindow", +@@ -1445,6 +1460,8 @@ meta_window_actor_process_x11_damage (MetaWindowActor *self, + event->area.y, + event->area.width, + event->area.height); ++ ++ meta_window_actor_notify_damaged (self); + } + + void +@@ -2053,3 +2070,9 @@ meta_window_actor_is_stereo (MetaWindowActor *self) + else + return FALSE; + } ++ ++void ++meta_window_actor_notify_damaged (MetaWindowActor *window_actor) ++{ ++ g_signal_emit (window_actor, signals[DAMAGED], 0); ++} +diff --git a/src/wayland/meta-wayland-surface.c b/src/wayland/meta-wayland-surface.c +index ddad1a45c..6ffcd6a7f 100644 +--- a/src/wayland/meta-wayland-surface.c ++++ b/src/wayland/meta-wayland-surface.c +@@ -669,6 +669,8 @@ void + meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, + MetaWaylandPendingState *pending) + { ++ gboolean had_damage = FALSE; ++ + if (surface->role) + { + meta_wayland_surface_role_pre_commit (surface->role, pending); +@@ -771,9 +773,12 @@ meta_wayland_surface_apply_pending_state (MetaWaylandSurface *surface, + + if (!cairo_region_is_empty (pending->surface_damage) || + !cairo_region_is_empty (pending->buffer_damage)) +- surface_process_damage (surface, +- pending->surface_damage, +- pending->buffer_damage); ++ { ++ surface_process_damage (surface, ++ pending->surface_damage, ++ pending->buffer_damage); ++ had_damage = TRUE; ++ } + + surface->offset_x += pending->dx; + surface->offset_y += pending->dy; +@@ -834,6 +839,22 @@ cleanup: + pending_state_reset (pending); + + g_list_foreach (surface->subsurfaces, parent_surface_state_applied, NULL); ++ ++ if (had_damage) ++ { ++ MetaWindow *toplevel_window; ++ ++ toplevel_window = meta_wayland_surface_get_toplevel_window (surface); ++ if (toplevel_window) ++ { ++ MetaWindowActor *toplevel_window_actor; ++ ++ toplevel_window_actor = ++ meta_window_actor_from_window (toplevel_window); ++ if (toplevel_window_actor) ++ meta_window_actor_notify_damaged (toplevel_window_actor); ++ } ++ } + } + + static void +-- +2.26.2 + + +From 27ada1ee57ac85d77cea9a1aee71b77f4939439f Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 26 Aug 2019 16:12:30 +0300 +Subject: [PATCH 11/49] screen-cast/window: Use window actor damaged signal + instead of paint + +We are really more interested in when a window is damaged, rather than +when it's painted, for screen casting windows. This also has the benefit +of not listening on the "paint" signal of the actor, meaning it'll open +doors for hacks currently necessary for taking a screenshot of a window +consisting of multiple surfaces. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 +--- + .../meta-screen-cast-window-stream-src.c | 45 +++++-------------- + 1 file changed, 11 insertions(+), 34 deletions(-) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index dbf330420..c31830b03 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -34,13 +34,11 @@ struct _MetaScreenCastWindowStreamSrc + + MetaScreenCastWindow *screen_cast_window; + +- unsigned long screen_cast_window_before_paint_handler_id; +- unsigned long screen_cast_window_after_paint_handler_id; ++ unsigned long screen_cast_window_damaged_handler_id; + unsigned long screen_cast_window_destroyed_handler_id; + unsigned long cursor_moved_handler_id; + unsigned long cursor_changed_handler_id; + +- gboolean actor_was_dirty; + gboolean cursor_bitmap_invalid; + }; + +@@ -255,15 +253,10 @@ meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_s + if (!window_src->screen_cast_window) + return; + +- if (window_src->screen_cast_window_before_paint_handler_id) ++ if (window_src->screen_cast_window_damaged_handler_id) + g_signal_handler_disconnect (window_src->screen_cast_window, +- window_src->screen_cast_window_before_paint_handler_id); +- window_src->screen_cast_window_before_paint_handler_id = 0; +- +- if (window_src->screen_cast_window_after_paint_handler_id) +- g_signal_handler_disconnect (window_src->screen_cast_window, +- window_src->screen_cast_window_after_paint_handler_id); +- window_src->screen_cast_window_after_paint_handler_id = 0; ++ window_src->screen_cast_window_damaged_handler_id); ++ window_src->screen_cast_window_damaged_handler_id = 0; + + if (window_src->screen_cast_window_destroyed_handler_id) + g_signal_handler_disconnect (window_src->screen_cast_window, +@@ -282,23 +275,12 @@ meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_s + } + + static void +-screen_cast_window_before_paint (MetaScreenCastWindow *screen_cast_window, +- MetaScreenCastWindowStreamSrc *window_src) +-{ +- window_src->actor_was_dirty = +- meta_screen_cast_window_has_damage (screen_cast_window); +-} +- +-static void +-screen_cast_window_after_paint (MetaWindowActor *actor, +- MetaScreenCastWindowStreamSrc *window_src) ++screen_cast_window_damaged (MetaWindowActor *actor, ++ MetaScreenCastWindowStreamSrc *window_src) + { +- if (window_src->actor_was_dirty) +- { +- MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); ++ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); + +- meta_screen_cast_stream_src_maybe_record_frame (src); +- } ++ meta_screen_cast_stream_src_maybe_record_frame (src); + } + + static void +@@ -378,16 +360,11 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + + window_src->screen_cast_window = META_SCREEN_CAST_WINDOW (window_actor); + +- window_src->screen_cast_window_before_paint_handler_id = ++ window_src->screen_cast_window_damaged_handler_id = + g_signal_connect (window_src->screen_cast_window, +- "paint", +- G_CALLBACK (screen_cast_window_before_paint), ++ "damaged", ++ G_CALLBACK (screen_cast_window_damaged), + window_src); +- window_src->screen_cast_window_after_paint_handler_id = +- g_signal_connect_after (window_src->screen_cast_window, +- "paint", +- G_CALLBACK (screen_cast_window_after_paint), +- window_src); + + window_src->screen_cast_window_destroyed_handler_id = + g_signal_connect (window_src->screen_cast_window, +-- +2.26.2 + + +From 2bf9b015901aa58826ebf96d941f9dbf803ec6b3 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 26 Aug 2019 16:29:33 +0300 +Subject: [PATCH 12/49] window-actor: Add API to get a cairo surface of the + window + +This currently uses a hack where it pushes a CoglFramebuffer backed by a +texture to the framebuffer stack, then calls clutter_actor_paint() on +the window actor causing it to render into the framebuffer. This has the +effect that all subsurfaces of a window will be drawn as part of the +window. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 +--- + src/compositor/meta-window-actor.c | 113 +++++++++++++++++++++++++++++ + src/meta/meta-window-actor.h | 4 + + 2 files changed, 117 insertions(+) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index f4eba6d42..9d215c745 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2076,3 +2076,116 @@ meta_window_actor_notify_damaged (MetaWindowActor *window_actor) + { + g_signal_emit (window_actor, signals[DAMAGED], 0); + } ++ ++cairo_surface_t * ++meta_window_actor_get_image (MetaWindowActor *self, ++ MetaRectangle *clip) ++{ ++ MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); ++ ClutterActor *actor = CLUTTER_ACTOR (self); ++ MetaBackend *backend = meta_get_backend (); ++ ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ float resource_scale; ++ float width, height; ++ CoglTexture2D *texture; ++ g_autoptr (GError) error = NULL; ++ CoglOffscreen *offscreen; ++ CoglFramebuffer *framebuffer; ++ CoglColor clear_color; ++ float x, y; ++ MetaRectangle scaled_clip; ++ cairo_surface_t *surface; ++ ++ if (!priv->surface) ++ return NULL; ++ ++ if (clutter_actor_get_n_children (actor) == 1) ++ { ++ MetaShapedTexture *stex; ++ ++ stex = meta_surface_actor_get_texture (priv->surface); ++ return meta_shaped_texture_get_image (stex, clip); ++ } ++ ++ clutter_actor_get_size (actor, &width, &height); ++ ++ if (width == 0 || height == 0) ++ return NULL; ++ ++ if (!clutter_actor_get_resource_scale (actor, &resource_scale)) ++ return NULL; ++ ++ width = ceilf (width * resource_scale); ++ height = ceilf (height * resource_scale); ++ ++ texture = cogl_texture_2d_new_with_size (cogl_context, width, height); ++ if (!texture) ++ return NULL; ++ ++ cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), ++ FALSE); ++ ++ offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); ++ framebuffer = COGL_FRAMEBUFFER (offscreen); ++ ++ cogl_object_unref (texture); ++ ++ if (!cogl_framebuffer_allocate (framebuffer, &error)) ++ { ++ g_warning ("Failed to allocate framebuffer for screenshot: %s", ++ error->message); ++ cogl_object_unref (framebuffer); ++ cogl_object_unref (texture); ++ return NULL; ++ } ++ ++ cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); ++ clutter_actor_get_position (actor, &x, &y); ++ ++ cogl_push_framebuffer (framebuffer); ++ ++ cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); ++ cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); ++ cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); ++ cogl_framebuffer_translate (framebuffer, -x, -y, 0); ++ ++ clutter_actor_paint (actor); ++ ++ cogl_pop_framebuffer (); ++ ++ if (clip) ++ { ++ meta_rectangle_scale_double (clip, resource_scale, ++ META_ROUNDING_STRATEGY_GROW, ++ &scaled_clip); ++ meta_rectangle_intersect (&scaled_clip, ++ &(MetaRectangle) { ++ .width = width, ++ .height = height, ++ }, ++ &scaled_clip); ++ } ++ else ++ { ++ scaled_clip = (MetaRectangle) { ++ .width = width, ++ .height = height, ++ }; ++ } ++ ++ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, ++ scaled_clip.width, scaled_clip.height); ++ cogl_framebuffer_read_pixels (framebuffer, ++ scaled_clip.x, scaled_clip.y, ++ scaled_clip.width, scaled_clip.height, ++ CLUTTER_CAIRO_FORMAT_ARGB32, ++ cairo_image_surface_get_data (surface)); ++ ++ cogl_object_unref (framebuffer); ++ ++ cairo_surface_mark_dirty (surface); ++ ++ return surface; ++} +diff --git a/src/meta/meta-window-actor.h b/src/meta/meta-window-actor.h +index 9ba164910..30a17c56b 100644 +--- a/src/meta/meta-window-actor.h ++++ b/src/meta/meta-window-actor.h +@@ -46,6 +46,10 @@ void meta_window_actor_sync_visibility (MetaWindowActor *self + META_EXPORT + gboolean meta_window_actor_is_destroyed (MetaWindowActor *self); + ++META_EXPORT ++cairo_surface_t * meta_window_actor_get_image (MetaWindowActor *self, ++ cairo_rectangle_int_t *clip); ++ + typedef enum + { + META_SHADOW_MODE_AUTO, +-- +2.26.2 + + +From 2aea9fb5c0b37f764911654e90d7d1917bf3aa0b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 26 Aug 2019 16:31:06 +0300 +Subject: [PATCH 13/49] window-actor: Use new get_image() API to screen casting + window content + +This fixes screen casting of windows consisting of multiple surfaces to +work. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/752 +--- + src/compositor/meta-window-actor.c | 4 +--- + 1 file changed, 1 insertion(+), 3 deletions(-) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 9d215c745..b82326600 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -1985,8 +1985,6 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, + uint8_t *data) + { + MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); +- MetaWindowActorPrivate *priv = +- meta_window_actor_get_instance_private (window_actor); + cairo_surface_t *image; + uint8_t *cr_data; + int cr_stride; +@@ -1997,7 +1995,7 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, + if (meta_window_actor_is_destroyed (window_actor)) + return; + +- image = meta_surface_actor_get_image (priv->surface, bounds); ++ image = meta_window_actor_get_image (window_actor, bounds); + cr_data = cairo_image_surface_get_data (image); + cr_width = cairo_image_surface_get_width (image); + cr_height = cairo_image_surface_get_height (image); +-- +2.26.2 + + +From 6edebf1a7beed7162f9a69270338010a77088b75 Mon Sep 17 00:00:00 2001 +From: Pascal Nowack +Date: Mon, 16 Dec 2019 19:00:19 +0100 +Subject: [PATCH 14/49] screen-cast: Fix window recording on HiDPI + +Using the same scale for the window as the +logical monitor only works correctly when having +the experimental 'scale-monitor-framebuffer' +feature enabled. +Without this experimental feature, the stream +will contain a black screen, where the actual +window only takes a small part of it. + +Therefore, use a scale of 1 for the non- +experimental case. + +Patch is based on commit 3fa6a92cc5dda6ab3939c3e982185f6caf453360. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/976 +--- + src/backends/meta-screen-cast-window-stream.c | 6 +++++- + 1 file changed, 5 insertions(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c +index 50d1806cd..b9a732cba 100644 +--- a/src/backends/meta-screen-cast-window-stream.c ++++ b/src/backends/meta-screen-cast-window-stream.c +@@ -226,11 +226,15 @@ meta_screen_cast_window_stream_initable_init (GInitable *initable, + G_CALLBACK (on_window_unmanaged), + window_stream); + ++ if (meta_is_stage_views_scaled ()) ++ scale = (int) ceilf (meta_logical_monitor_get_scale (logical_monitor)); ++ else ++ scale = 1; ++ + /* We cannot set the stream size to the exact size of the window, because + * windows can be resized, whereas streams cannot. + * So we set a size equals to the size of the logical monitor for the window. + */ +- scale = (int) ceil (meta_logical_monitor_get_scale (logical_monitor)); + window_stream->logical_width = logical_monitor->rect.width; + window_stream->logical_height = logical_monitor->rect.height; + window_stream->stream_width = logical_monitor->rect.width * scale; +-- +2.26.2 + + +From 077217b337f18619d3b3b878c5faae488cbafd15 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 4 Feb 2020 14:58:30 +0100 +Subject: [PATCH 15/49] window-actor: Ensure clipping in `capture_into()` + +The clip bounds passed in `meta_window_actor_capture_into()` represent +the actual allocated buffer size where the window actor image will be +eventually copied. + +As such, it is completely agnostic to the scaling factors that might +affect the different surface actors which compose the window actor. + +So instead of trying to compute the scale factor by which the given +clipping bounds need to be adjusted, simply clip the resulting image +based on the given bounds to make sure we never overflow the destination +buffer. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 +--- + src/compositor/meta-window-actor.c | 24 ++++++++++++++---------- + 1 file changed, 14 insertions(+), 10 deletions(-) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index b82326600..6d4aa6c1b 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2001,32 +2001,36 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, + cr_height = cairo_image_surface_get_height (image); + cr_stride = cairo_image_surface_get_stride (image); + +- if (cr_width < bounds->width || cr_height < bounds->height) ++ if (cr_width == bounds->width && cr_height == bounds->height) + { ++ memcpy (data, cr_data, cr_height * cr_stride); ++ } ++ else ++ { ++ int width = MIN (bounds->width, cr_width); ++ int height = MIN (bounds->height, cr_height); ++ int stride = width * bpp; + uint8_t *src, *dst; ++ + src = cr_data; + dst = data; + +- for (int i = 0; i < cr_height; i++) ++ for (int i = 0; i < height; i++) + { +- memcpy (dst, src, cr_stride); +- if (cr_width < bounds->width) +- memset (dst + cr_stride, 0, (bounds->width * bpp) - cr_stride); ++ memcpy (dst, src, stride); ++ if (width < bounds->width) ++ memset (dst + stride, 0, (bounds->width * bpp) - stride); + + src += cr_stride; + dst += bounds->width * bpp; + } + +- for (int i = cr_height; i < bounds->height; i++) ++ for (int i = height; i < bounds->height; i++) + { + memset (dst, 0, bounds->width * bpp); + dst += bounds->width * bpp; + } + } +- else +- { +- memcpy (data, cr_data, cr_height * cr_stride); +- } + + cairo_surface_destroy (image); + } +-- +2.26.2 + + +From 06dcbe104c58e1efd1d99fc7d4761698f9d32824 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Mon, 3 Feb 2020 14:18:57 +0100 +Subject: [PATCH 16/49] shaped-texture: Add `get_width()`/`get_height()` API + +Add an API to retrieve the content size of a shaped texture. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 +--- + src/compositor/meta-shaped-texture-private.h | 3 +++ + src/compositor/meta-shaped-texture.c | 20 ++++++++++++++++++++ + 2 files changed, 23 insertions(+) + +diff --git a/src/compositor/meta-shaped-texture-private.h b/src/compositor/meta-shaped-texture-private.h +index d0efdd4dc..362bbb93f 100644 +--- a/src/compositor/meta-shaped-texture-private.h ++++ b/src/compositor/meta-shaped-texture-private.h +@@ -53,4 +53,7 @@ void meta_shaped_texture_set_viewport_dst_size (MetaShapedTexture *stex, + int dst_height); + void meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex); + ++int meta_shaped_texture_get_width (MetaShapedTexture *stex); ++int meta_shaped_texture_get_height (MetaShapedTexture *stex); ++ + #endif +diff --git a/src/compositor/meta-shaped-texture.c b/src/compositor/meta-shaped-texture.c +index e77a32109..6ef2125c3 100644 +--- a/src/compositor/meta-shaped-texture.c ++++ b/src/compositor/meta-shaped-texture.c +@@ -1585,3 +1585,23 @@ meta_shaped_texture_new (void) + { + return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); + } ++ ++int ++meta_shaped_texture_get_width (MetaShapedTexture *stex) ++{ ++ g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); ++ ++ ensure_size_valid (stex); ++ ++ return stex->dst_width; ++} ++ ++int ++meta_shaped_texture_get_height (MetaShapedTexture *stex) ++{ ++ g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); ++ ++ ensure_size_valid (stex); ++ ++ return stex->dst_height; ++} +-- +2.26.2 + + +From 9a9a4466e79d61996304ca0c3e9b327c9b3abc25 Mon Sep 17 00:00:00 2001 +From: Olivier Fourdan +Date: Tue, 28 Jan 2020 11:13:41 +0100 +Subject: [PATCH 17/49] screen-cast-window: Use buffer bounds in place of frame + bounds + +The frame bounds as returned by `meta_window_actor_get_frame_bounds()` +would be used as cropping values when streaming a window content. + +But, as its name implies, it returns the actual frame bounds, whereas we +may want to include the whole buffer, to include client side shadows for +example. + +Rename the `get_frame_bounds()` API to `get_buffer_bounds()` (which was +previously partly removed with commit 11bd84789) and return the actual +buffer bounds to use as the cropping area when streaming a window. + +Fixes: 931934511 - "Implement MetaScreenCastWindow interface" +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1022 +Closes: https://gitlab.gnome.org/GNOME/mutter/issues/1018 +--- + .../meta-screen-cast-window-stream-src.c | 4 +-- + src/backends/meta-screen-cast-window.c | 8 +++--- + src/backends/meta-screen-cast-window.h | 8 +++--- + src/compositor/meta-window-actor.c | 26 ++++++------------- + 4 files changed, 18 insertions(+), 28 deletions(-) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index c31830b03..210ea0807 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -230,8 +230,8 @@ meta_screen_cast_window_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + MetaRectangle stream_rect; + +- meta_screen_cast_window_get_frame_bounds (window_src->screen_cast_window, +- crop_rect); ++ meta_screen_cast_window_get_buffer_bounds (window_src->screen_cast_window, ++ crop_rect); + + stream_rect.x = 0; + stream_rect.y = 0; +diff --git a/src/backends/meta-screen-cast-window.c b/src/backends/meta-screen-cast-window.c +index ce2bf82c9..91515ded8 100644 +--- a/src/backends/meta-screen-cast-window.c ++++ b/src/backends/meta-screen-cast-window.c +@@ -30,11 +30,11 @@ meta_screen_cast_window_default_init (MetaScreenCastWindowInterface *iface) + } + + void +-meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, +- MetaRectangle *bounds) ++meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds) + { +- META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_frame_bounds (screen_cast_window, +- bounds); ++ META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_buffer_bounds (screen_cast_window, ++ bounds); + } + + void +diff --git a/src/backends/meta-screen-cast-window.h b/src/backends/meta-screen-cast-window.h +index badd88224..69e5a34dc 100644 +--- a/src/backends/meta-screen-cast-window.h ++++ b/src/backends/meta-screen-cast-window.h +@@ -37,8 +37,8 @@ struct _MetaScreenCastWindowInterface + { + GTypeInterface parent_iface; + +- void (*get_frame_bounds) (MetaScreenCastWindow *screen_cast_window, +- MetaRectangle *bounds); ++ void (*get_buffer_bounds) (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds); + + void (*transform_relative_position) (MetaScreenCastWindow *screen_cast_window, + double x, +@@ -59,8 +59,8 @@ struct _MetaScreenCastWindowInterface + gboolean (*has_damage) (MetaScreenCastWindow *screen_cast_window); + }; + +-void meta_screen_cast_window_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, +- MetaRectangle *bounds); ++void meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds); + + void meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window, + double x, +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 6d4aa6c1b..81eb04c84 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -1877,29 +1877,19 @@ meta_window_actor_from_window (MetaWindow *window) + } + + static void +-meta_window_actor_get_frame_bounds (MetaScreenCastWindow *screen_cast_window, +- MetaRectangle *bounds) ++meta_window_actor_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds) + { + MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); + MetaWindowActorPrivate *priv = + meta_window_actor_get_instance_private (window_actor); +- MetaWindow *window; + MetaShapedTexture *stex; +- MetaRectangle buffer_rect; +- MetaRectangle frame_rect; +- double scale_x, scale_y; + + stex = meta_surface_actor_get_texture (priv->surface); +- clutter_actor_get_scale (CLUTTER_ACTOR (stex), &scale_x, &scale_y); +- +- window = priv->window; +- meta_window_get_buffer_rect (window, &buffer_rect); +- meta_window_get_frame_rect (window, &frame_rect); +- +- bounds->x = (int) floor ((frame_rect.x - buffer_rect.x) / scale_x); +- bounds->y = (int) floor ((frame_rect.y - buffer_rect.y) / scale_y); +- bounds->width = (int) ceil (frame_rect.width / scale_x); +- bounds->height = (int) ceil (frame_rect.height / scale_y); ++ *bounds = (MetaRectangle) { ++ .width = meta_shaped_texture_get_width (stex), ++ .height = meta_shaped_texture_get_height (stex) ++ }; + } + + static void +@@ -1917,7 +1907,7 @@ meta_window_actor_transform_relative_position (MetaScreenCastWindow *screen_cast + MetaRectangle bounds; + ClutterVertex v1 = { 0.f, }, v2 = { 0.f, }; + +- meta_window_actor_get_frame_bounds (screen_cast_window, &bounds); ++ meta_window_actor_get_buffer_bounds (screen_cast_window, &bounds); + + v1.x = CLAMP ((float) x, + bounds.x, +@@ -2044,7 +2034,7 @@ meta_window_actor_has_damage (MetaScreenCastWindow *screen_cast_window) + static void + screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) + { +- iface->get_frame_bounds = meta_window_actor_get_frame_bounds; ++ iface->get_buffer_bounds = meta_window_actor_get_buffer_bounds; + iface->transform_relative_position = meta_window_actor_transform_relative_position; + iface->transform_cursor_position = meta_window_actor_transform_cursor_position; + iface->capture_into = meta_window_actor_capture_into; +-- +2.26.2 + + +From c6f68fe763e736c5fd84913407427254c5dc7dea Mon Sep 17 00:00:00 2001 +From: Wim Taymans +Date: Tue, 14 Jan 2020 09:44:45 +0100 +Subject: [PATCH 18/49] screen-cast: Update to PipeWire 0.3 API + +Update to 0.3 API + +[jadahl: update Dockerfile to include new enough pipewire] +[jadahl: dropped Dockerfile changes for backport] + +Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1051 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1062 +--- + meson.build | 4 +- + src/backends/meta-screen-cast-stream-src.c | 255 ++++++++------------- + 2 files changed, 101 insertions(+), 158 deletions(-) + +diff --git a/meson.build b/meson.build +index b2239ed81..8ef592bc5 100644 +--- a/meson.build ++++ b/meson.build +@@ -43,7 +43,7 @@ libinput_req = '>= 1.4' + gbm_req = '>= 10.3' + + # screen cast version requirements +-libpipewire_req = '>= 0.2.5' ++libpipewire_req = '>= 0.3.0' + + gnome = import('gnome') + pkg = import('pkgconfig') +@@ -233,7 +233,7 @@ endif + + have_remote_desktop = get_option('remote_desktop') + if have_remote_desktop +- libpipewire_dep = dependency('libpipewire-0.2', version: libpipewire_req) ++ libpipewire_dep = dependency('libpipewire-0.3', version: libpipewire_req) + endif + + have_introspection = get_option('introspection') +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 82c5cba43..ba1ce94a7 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -29,6 +29,7 @@ + #include + #include + #include ++#include + #include + #include + +@@ -62,15 +63,6 @@ enum + + static guint signals[N_SIGNALS]; + +-typedef struct _MetaSpaType +-{ +- struct spa_type_media_type media_type; +- struct spa_type_media_subtype media_subtype; +- struct spa_type_format_video format_video; +- struct spa_type_video_format video_format; +- uint32_t meta_cursor; +-} MetaSpaType; +- + typedef struct _MetaPipeWireSource + { + GSource base; +@@ -82,19 +74,19 @@ typedef struct _MetaScreenCastStreamSrcPrivate + { + MetaScreenCastStream *stream; + ++ struct pw_context *pipewire_context; + struct pw_core *pipewire_core; +- struct pw_remote *pipewire_remote; +- struct pw_type *pipewire_type; + MetaPipeWireSource *pipewire_source; +- struct spa_hook pipewire_remote_listener; ++ struct spa_hook pipewire_core_listener; + + gboolean is_enabled; + + struct pw_stream *pipewire_stream; + struct spa_hook pipewire_stream_listener; ++ uint32_t node_id; + +- MetaSpaType spa_type; + struct spa_video_info_raw video_format; ++ int video_stride; + + uint64_t last_frame_timestamp_us; + +@@ -112,8 +104,6 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, + meta_screen_cast_stream_src_init_initable_iface) + G_ADD_PRIVATE (MetaScreenCastStreamSrc)) + +-#define PROP_RANGE(min, max) 2, (min), (max) +- + static void + meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, + int *width, +@@ -286,9 +276,6 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre + int x, + int y) + { +- MetaScreenCastStreamSrcPrivate *priv = +- meta_screen_cast_stream_src_get_instance_private (src); +- MetaSpaType *spa_type = &priv->spa_type; + struct spa_meta_bitmap *spa_meta_bitmap; + + spa_meta_cursor->id = 1; +@@ -300,7 +287,7 @@ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStre + spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, + spa_meta_cursor->bitmap_offset, + struct spa_meta_bitmap); +- spa_meta_bitmap->format = spa_type->video_format.RGBA; ++ spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; + spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); + + spa_meta_cursor->hotspot.x = 0; +@@ -317,9 +304,6 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc + int y, + float scale) + { +- MetaScreenCastStreamSrcPrivate *priv = +- meta_screen_cast_stream_src_get_instance_private (src); +- MetaSpaType *spa_type = &priv->spa_type; + CoglTexture *cursor_texture; + struct spa_meta_bitmap *spa_meta_bitmap; + int hotspot_x, hotspot_y; +@@ -346,7 +330,7 @@ meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc + spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, + spa_meta_cursor->bitmap_offset, + struct spa_meta_bitmap); +- spa_meta_bitmap->format = spa_type->video_format.RGBA; ++ spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; + spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); + + meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); +@@ -382,12 +366,10 @@ static void + add_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer) + { +- MetaScreenCastStreamSrcPrivate *priv = +- meta_screen_cast_stream_src_get_instance_private (src); +- MetaSpaType *spa_type = &priv->spa_type; + struct spa_meta_cursor *spa_meta_cursor; + +- spa_meta_cursor = spa_buffer_find_meta (spa_buffer, spa_type->meta_cursor); ++ spa_meta_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, ++ sizeof (*spa_meta_cursor)); + if (spa_meta_cursor) + meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor); + } +@@ -447,14 +429,14 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + { + data = spa_buffer->datas[0].data; + } +- else if (spa_buffer->datas[0].type == priv->pipewire_type->data.MemFd) ++ else if (spa_buffer->datas[0].type == SPA_DATA_MemFd) + { + map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, + PROT_READ | PROT_WRITE, MAP_SHARED, + spa_buffer->datas[0].fd, 0); + if (map == MAP_FAILED) + { +- g_warning ("Failed to mmap pipewire stream buffer: %s\n", ++ g_warning ("Failed to mmap pipewire stream buffer: %s", + strerror (errno)); + return; + } +@@ -469,28 +451,30 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + + if (meta_screen_cast_stream_src_record_frame (src, data)) + { +- struct spa_meta_video_crop *spa_meta_video_crop; ++ struct spa_meta_region *spa_meta_video_crop; + + spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; ++ spa_buffer->datas[0].chunk->stride = priv->video_stride; + + /* Update VideoCrop if needed */ + spa_meta_video_crop = +- spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop); ++ spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, ++ sizeof (*spa_meta_video_crop)); + if (spa_meta_video_crop) + { + if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) + { +- spa_meta_video_crop->x = crop_rect.x; +- spa_meta_video_crop->y = crop_rect.y; +- spa_meta_video_crop->width = crop_rect.width; +- spa_meta_video_crop->height = crop_rect.height; ++ spa_meta_video_crop->region.position.x = crop_rect.x; ++ spa_meta_video_crop->region.position.y = crop_rect.y; ++ spa_meta_video_crop->region.size.width = crop_rect.width; ++ spa_meta_video_crop->region.size.height = crop_rect.height; + } + else + { +- spa_meta_video_crop->x = 0; +- spa_meta_video_crop->y = 0; +- spa_meta_video_crop->width = priv->stream_width; +- spa_meta_video_crop->height = priv->stream_height; ++ spa_meta_video_crop->region.position.x = 0; ++ spa_meta_video_crop->region.position.y = 0; ++ spa_meta_video_crop->region.size.width = priv->stream_width; ++ spa_meta_video_crop->region.size.height = priv->stream_height; + } + } + } +@@ -555,7 +539,6 @@ on_stream_state_changed (void *data, + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +- uint32_t node_id; + + switch (state) + { +@@ -563,14 +546,12 @@ on_stream_state_changed (void *data, + g_warning ("pipewire stream error: %s", error_message); + meta_screen_cast_stream_src_notify_closed (src); + break; +- case PW_STREAM_STATE_CONFIGURE: +- node_id = pw_stream_get_node_id (priv->pipewire_stream); +- g_signal_emit (src, signals[READY], 0, (unsigned int) node_id); +- break; +- case PW_STREAM_STATE_UNCONNECTED: +- case PW_STREAM_STATE_CONNECTING: +- case PW_STREAM_STATE_READY: + case PW_STREAM_STATE_PAUSED: ++ if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) ++ { ++ priv->node_id = pw_stream_get_node_id (priv->pipewire_stream); ++ g_signal_emit (src, signals[READY], 0, (unsigned int) priv->node_id); ++ } + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + break; +@@ -578,68 +559,69 @@ on_stream_state_changed (void *data, + if (!meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_enable (src); + break; ++ case PW_STREAM_STATE_UNCONNECTED: ++ case PW_STREAM_STATE_CONNECTING: ++ break; + } + } + + static void +-on_stream_format_changed (void *data, +- const struct spa_pod *format) ++on_stream_param_changed (void *data, ++ uint32_t id, ++ const struct spa_pod *format) + { + MetaScreenCastStreamSrc *src = data; + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); +- struct pw_type *pipewire_type = priv->pipewire_type; + uint8_t params_buffer[1024]; + int32_t width, height, stride, size; + struct spa_pod_builder pod_builder; + const struct spa_pod *params[3]; + const int bpp = 4; + +- if (!format) +- { +- pw_stream_finish_format (priv->pipewire_stream, 0, NULL, 0); +- return; +- } ++ if (!format || id != SPA_PARAM_Format) ++ return; + + spa_format_video_raw_parse (format, +- &priv->video_format, +- &priv->spa_type.format_video); ++ &priv->video_format); + + width = priv->video_format.size.width; + height = priv->video_format.size.height; + stride = SPA_ROUND_UP_N (width * bpp, 4); + size = height * stride; + ++ priv->video_stride = stride; ++ + pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); + +- params[0] = spa_pod_builder_object ( ++ params[0] = spa_pod_builder_add_object ( + &pod_builder, +- pipewire_type->param.idBuffers, pipewire_type->param_buffers.Buffers, +- ":", pipewire_type->param_buffers.size, "i", size, +- ":", pipewire_type->param_buffers.stride, "i", stride, +- ":", pipewire_type->param_buffers.buffers, "iru", 16, PROP_RANGE (2, 16), +- ":", pipewire_type->param_buffers.align, "i", 16); +- +- params[1] = spa_pod_builder_object ( ++ SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, ++ SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16), ++ SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1), ++ SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), ++ SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), ++ SPA_PARAM_BUFFERS_align, SPA_POD_Int (16)); ++ ++ params[1] = spa_pod_builder_add_object ( + &pod_builder, +- pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, +- ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop, +- ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop)); ++ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, ++ SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), ++ SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_region))); + +- params[2] = spa_pod_builder_object ( ++ params[2] = spa_pod_builder_add_object ( + &pod_builder, +- pipewire_type->param.idMeta, pipewire_type->param_meta.Meta, +- ":", pipewire_type->param_meta.type, "I", priv->spa_type.meta_cursor, +- ":", pipewire_type->param_meta.size, "i", CURSOR_META_SIZE (64, 64)); ++ SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, ++ SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), ++ SPA_PARAM_META_size, SPA_POD_Int (CURSOR_META_SIZE (64, 64))); + +- pw_stream_finish_format (priv->pipewire_stream, 0, +- params, G_N_ELEMENTS (params)); ++ pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); + } + + static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .state_changed = on_stream_state_changed, +- .format_changed = on_stream_format_changed, ++ .param_changed = on_stream_param_changed, + }; + + static struct pw_stream * +@@ -652,8 +634,6 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + uint8_t buffer[1024]; + struct spa_pod_builder pod_builder = + SPA_POD_BUILDER_INIT (buffer, sizeof (buffer)); +- MetaSpaType *spa_type = &priv->spa_type; +- struct pw_type *pipewire_type = priv->pipewire_type; + float frame_rate; + MetaFraction frame_rate_fraction; + struct spa_fraction max_framerate; +@@ -661,7 +641,9 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + const struct spa_pod *params[1]; + int result; + +- pipewire_stream = pw_stream_new (priv->pipewire_remote, ++ priv->node_id = SPA_ID_INVALID; ++ ++ pipewire_stream = pw_stream_new (priv->pipewire_core, + "meta-screen-cast-src", + NULL); + if (!pipewire_stream) +@@ -682,17 +664,17 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + max_framerate = SPA_FRACTION (frame_rate_fraction.num, + frame_rate_fraction.denom); + +- params[0] = spa_pod_builder_object ( ++ params[0] = spa_pod_builder_add_object ( + &pod_builder, +- pipewire_type->param.idEnumFormat, pipewire_type->spa_format, +- "I", spa_type->media_type.video, +- "I", spa_type->media_subtype.raw, +- ":", spa_type->format_video.format, "I", spa_type->video_format.BGRx, +- ":", spa_type->format_video.size, "R", &SPA_RECTANGLE (priv->stream_width, +- priv->stream_height), +- ":", spa_type->format_video.framerate, "F", &SPA_FRACTION (0, 1), +- ":", spa_type->format_video.max_framerate, "Fru", &max_framerate, +- PROP_RANGE (&min_framerate, ++ SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, ++ SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_video), ++ SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw), ++ SPA_FORMAT_VIDEO_format, SPA_POD_Id (SPA_VIDEO_FORMAT_BGRx), ++ SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (priv->stream_width, ++ priv->stream_height)), ++ SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)), ++ SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&max_framerate, ++ &min_framerate, + &max_framerate)); + + pw_stream_add_listener (pipewire_stream, +@@ -702,7 +684,7 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + + result = pw_stream_connect (pipewire_stream, + PW_DIRECTION_OUTPUT, +- NULL, ++ SPA_ID_INVALID, + (PW_STREAM_FLAG_DRIVER | + PW_STREAM_FLAG_MAP_BUFFERS), + params, G_N_ELEMENTS (params)); +@@ -717,40 +699,18 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + } + + static void +-on_state_changed (void *data, +- enum pw_remote_state old, +- enum pw_remote_state state, +- const char *error_message) ++on_core_error (void *data, ++ uint32_t id, ++ int seq, ++ int res, ++ const char *message) + { + MetaScreenCastStreamSrc *src = data; +- MetaScreenCastStreamSrcPrivate *priv = +- meta_screen_cast_stream_src_get_instance_private (src); +- struct pw_stream *pipewire_stream; +- GError *error = NULL; + +- switch (state) +- { +- case PW_REMOTE_STATE_ERROR: +- g_warning ("pipewire remote error: %s\n", error_message); +- meta_screen_cast_stream_src_notify_closed (src); +- break; +- case PW_REMOTE_STATE_CONNECTED: +- pipewire_stream = create_pipewire_stream (src, &error); +- if (!pipewire_stream) +- { +- g_warning ("Could not create pipewire stream: %s", error->message); +- g_error_free (error); +- meta_screen_cast_stream_src_notify_closed (src); +- } +- else +- { +- priv->pipewire_stream = pipewire_stream; +- } +- break; +- case PW_REMOTE_STATE_UNCONNECTED: +- case PW_REMOTE_STATE_CONNECTING: +- break; +- } ++ g_warning ("pipewire remote error: id:%u %s", id, message); ++ ++ if (id == PW_ID_CORE && res == -EPIPE) ++ meta_screen_cast_stream_src_notify_closed (src); + } + + static gboolean +@@ -793,17 +753,6 @@ static GSourceFuncs pipewire_source_funcs = + pipewire_loop_source_finalize + }; + +-static void +-init_spa_type (MetaSpaType *type, +- struct spa_type_map *map) +-{ +- spa_type_media_type_map (map, &type->media_type); +- spa_type_media_subtype_map (map, &type->media_subtype); +- spa_type_format_video_map (map, &type->format_video); +- spa_type_video_format_map (map, &type->video_format); +- type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor); +-} +- + static MetaPipeWireSource * + create_pipewire_source (void) + { +@@ -829,9 +778,9 @@ create_pipewire_source (void) + return pipewire_source; + } + +-static const struct pw_remote_events remote_events = { +- PW_VERSION_REMOTE_EVENTS, +- .state_changed = on_state_changed, ++static const struct pw_core_events core_events = { ++ PW_VERSION_CORE_EVENTS, ++ .error = on_core_error, + }; + + static gboolean +@@ -851,37 +800,31 @@ meta_screen_cast_stream_src_initable_init (GInitable *initable, + return FALSE; + } + +- priv->pipewire_core = pw_core_new (priv->pipewire_source->pipewire_loop, +- NULL); +- if (!priv->pipewire_core) ++ priv->pipewire_context = pw_context_new (priv->pipewire_source->pipewire_loop, ++ NULL, 0); ++ if (!priv->pipewire_context) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +- "Failed to create pipewire core"); ++ "Failed to create pipewire context"); + return FALSE; + } + +- priv->pipewire_remote = pw_remote_new (priv->pipewire_core, NULL, 0); +- if (!priv->pipewire_remote) ++ priv->pipewire_core = pw_context_connect (priv->pipewire_context, NULL, 0); ++ if (!priv->pipewire_core) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +- "Couldn't creat pipewire remote"); ++ "Couldn't connect pipewire context"); + return FALSE; + } + +- pw_remote_add_listener (priv->pipewire_remote, +- &priv->pipewire_remote_listener, +- &remote_events, +- src); ++ pw_core_add_listener (priv->pipewire_core, ++ &priv->pipewire_core_listener, ++ &core_events, ++ src); + +- priv->pipewire_type = pw_core_get_type (priv->pipewire_core); +- init_spa_type (&priv->spa_type, priv->pipewire_type->map); +- +- if (pw_remote_connect (priv->pipewire_remote) != 0) +- { +- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, +- "Couldn't connect pipewire remote"); +- return FALSE; +- } ++ priv->pipewire_stream = create_pipewire_stream (src, error); ++ if (!priv->pipewire_stream) ++ return FALSE; + + return TRUE; + } +@@ -912,8 +855,8 @@ meta_screen_cast_stream_src_finalize (GObject *object) + meta_screen_cast_stream_src_disable (src); + + g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); +- g_clear_pointer (&priv->pipewire_remote, pw_remote_destroy); +- g_clear_pointer (&priv->pipewire_core, pw_core_destroy); ++ g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); ++ g_clear_pointer (&priv->pipewire_context, pw_context_destroy); + g_source_destroy (&priv->pipewire_source->base); + + G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object); +-- +2.26.2 + + +From b29e2f833357a0b29bb42999b4d6be725be067d1 Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Mon, 10 Jun 2019 17:07:55 +0300 +Subject: [PATCH 19/49] egl: Introduce meta_egl_create_dmabuf_image + +This bit of code was more or less duplicated in meta-renderer-native-gles3.c +and meta-wayland-dma-buf.c. Start consolidating the two implementations by +moving the *-gles3.c function into meta-egl.c and generalizing it so it could +also accommodate the meta-wayland-dma-buf.c usage. + +The workaround in the *-gles3.c implementation is moved to the caller. It is +the caller's responsibility to check for the existence of the appropriate EGL +extensions. + +Commit 6f59e4858e24c828e3ab0e611d36dfaaded1b272 worked around the lack of +EGL_EXT_image_dma_buf_import_modifiers with the assumption that if the modifier +is linear, there is no need to pass it into EGL. The problem is that not +passing a modifier explicitly to EGL invokes implementation-defined behaviour, +so we should not have that workaround in meta-egl.c. + +This patch intends to be pure refactoring, no behavioral changes. The one +change is the addition of g_assert to catch overwriting arbitrary memory. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 +--- + src/backends/meta-egl.c | 94 ++++++++++++- + src/backends/meta-egl.h | 13 ++ + .../native/meta-renderer-native-gles3.c | 125 +++--------------- + 3 files changed, 127 insertions(+), 105 deletions(-) + +diff --git a/src/backends/meta-egl.c b/src/backends/meta-egl.c +index a28eef4ca..fdeff4f77 100644 +--- a/src/backends/meta-egl.c ++++ b/src/backends/meta-egl.c +@@ -1,7 +1,8 @@ + /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ + + /* +- * Copyright (C) 2016 Red Hat Inc. ++ * Copyright (C) 2016, 2017 Red Hat Inc. ++ * Copyright (C) 2018, 2019 DisplayLink (UK) Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -575,6 +576,97 @@ meta_egl_destroy_image (MetaEgl *egl, + return TRUE; + } + ++EGLImageKHR ++meta_egl_create_dmabuf_image (MetaEgl *egl, ++ EGLDisplay egl_display, ++ unsigned int width, ++ unsigned int height, ++ uint32_t drm_format, ++ uint32_t n_planes, ++ const int *fds, ++ const uint32_t *strides, ++ const uint32_t *offsets, ++ const uint64_t *modifiers, ++ GError **error) ++{ ++ EGLint attribs[37]; ++ int atti = 0; ++ ++ /* This requires the Mesa commit in ++ * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or ++ * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). ++ * Otherwise Mesa closes the fd behind our back and re-importing ++ * will fail. ++ * https://bugs.freedesktop.org/show_bug.cgi?id=76188 ++ */ ++ ++ attribs[atti++] = EGL_WIDTH; ++ attribs[atti++] = width; ++ attribs[atti++] = EGL_HEIGHT; ++ attribs[atti++] = height; ++ attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; ++ attribs[atti++] = drm_format; ++ ++ if (n_planes > 0) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; ++ attribs[atti++] = fds[0]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; ++ attribs[atti++] = offsets[0]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; ++ attribs[atti++] = strides[0]; ++ if (modifiers) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; ++ attribs[atti++] = modifiers[0] & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; ++ attribs[atti++] = modifiers[0] >> 32; ++ } ++ } ++ ++ if (n_planes > 1) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; ++ attribs[atti++] = fds[1]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; ++ attribs[atti++] = offsets[1]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; ++ attribs[atti++] = strides[1]; ++ if (modifiers) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; ++ attribs[atti++] = modifiers[1] & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; ++ attribs[atti++] = modifiers[1] >> 32; ++ } ++ } ++ ++ if (n_planes > 2) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; ++ attribs[atti++] = fds[2]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; ++ attribs[atti++] = offsets[2]; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; ++ attribs[atti++] = strides[2]; ++ if (modifiers) ++ { ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; ++ attribs[atti++] = modifiers[2] & 0xFFFFFFFF; ++ attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; ++ attribs[atti++] = modifiers[2] >> 32; ++ } ++ } ++ ++ attribs[atti++] = EGL_NONE; ++ g_assert (atti <= G_N_ELEMENTS (attribs)); ++ ++ return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, ++ EGL_LINUX_DMA_BUF_EXT, NULL, ++ attribs, ++ error); ++} ++ + gboolean + meta_egl_make_current (MetaEgl *egl, + EGLDisplay display, +diff --git a/src/backends/meta-egl.h b/src/backends/meta-egl.h +index 81b53b32d..4591e7d85 100644 +--- a/src/backends/meta-egl.h ++++ b/src/backends/meta-egl.h +@@ -2,6 +2,7 @@ + + /* + * Copyright (C) 2016 Red Hat Inc. ++ * Copyright (C) 2019 DisplayLink (UK) Ltd. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as +@@ -101,6 +102,18 @@ gboolean meta_egl_destroy_image (MetaEgl *egl, + EGLImageKHR image, + GError **error); + ++EGLImageKHR meta_egl_create_dmabuf_image (MetaEgl *egl, ++ EGLDisplay egl_display, ++ unsigned int width, ++ unsigned int height, ++ uint32_t drm_format, ++ uint32_t n_planes, ++ const int *fds, ++ const uint32_t *strides, ++ const uint32_t *offsets, ++ const uint64_t *modifiers, ++ GError **error); ++ + EGLSurface meta_egl_create_window_surface (MetaEgl *egl, + EGLDisplay display, + EGLConfig config, +diff --git a/src/backends/native/meta-renderer-native-gles3.c b/src/backends/native/meta-renderer-native-gles3.c +index 7afea8648..740b52ef6 100644 +--- a/src/backends/native/meta-renderer-native-gles3.c ++++ b/src/backends/native/meta-renderer-native-gles3.c +@@ -45,101 +45,6 @@ + #error "Somehow included OpenGL headers when we shouldn't have" + #endif + +-static EGLImageKHR +-create_egl_image (MetaEgl *egl, +- EGLDisplay egl_display, +- EGLContext egl_context, +- unsigned int width, +- unsigned int height, +- uint32_t n_planes, +- uint32_t *strides, +- uint32_t *offsets, +- uint64_t *modifiers, +- uint32_t format, +- int fd, +- GError **error) +-{ +- EGLint attribs[37]; +- int atti = 0; +- gboolean has_modifier; +- +- /* This requires the Mesa commit in +- * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or +- * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). +- * Otherwise Mesa closes the fd behind our back and re-importing +- * will fail. +- * https://bugs.freedesktop.org/show_bug.cgi?id=76188 +- */ +- +- attribs[atti++] = EGL_WIDTH; +- attribs[atti++] = width; +- attribs[atti++] = EGL_HEIGHT; +- attribs[atti++] = height; +- attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; +- attribs[atti++] = format; +- +- has_modifier = (modifiers[0] != DRM_FORMAT_MOD_INVALID && +- modifiers[0] != DRM_FORMAT_MOD_LINEAR); +- +- if (n_planes > 0) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; +- attribs[atti++] = fd; +- attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; +- attribs[atti++] = offsets[0]; +- attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; +- attribs[atti++] = strides[0]; +- if (has_modifier) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; +- attribs[atti++] = modifiers[0] & 0xFFFFFFFF; +- attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; +- attribs[atti++] = modifiers[0] >> 32; +- } +- } +- +- if (n_planes > 1) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; +- attribs[atti++] = fd; +- attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; +- attribs[atti++] = offsets[1]; +- attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; +- attribs[atti++] = strides[1]; +- if (has_modifier) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; +- attribs[atti++] = modifiers[1] & 0xFFFFFFFF; +- attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; +- attribs[atti++] = modifiers[1] >> 32; +- } +- } +- +- if (n_planes > 2) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; +- attribs[atti++] = fd; +- attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; +- attribs[atti++] = offsets[2]; +- attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; +- attribs[atti++] = strides[2]; +- if (has_modifier) +- { +- attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; +- attribs[atti++] = modifiers[2] & 0xFFFFFFFF; +- attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; +- attribs[atti++] = modifiers[2] >> 32; +- } +- } +- +- attribs[atti++] = EGL_NONE; +- +- return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, +- EGL_LINUX_DMA_BUF_EXT, NULL, +- attribs, +- error); +-} +- + static void + paint_egl_image (MetaGles3 *gles3, + EGLImageKHR egl_image, +@@ -195,8 +100,10 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, + uint32_t strides[4] = { 0 }; + uint32_t offsets[4] = { 0 }; + uint64_t modifiers[4] = { 0 }; ++ int fds[4] = { -1, -1, -1, -1 }; + uint32_t format; + EGLImageKHR egl_image; ++ gboolean use_modifiers; + + shared_bo_fd = gbm_bo_get_fd (shared_bo); + if (shared_bo_fd < 0) +@@ -216,17 +123,27 @@ meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, + strides[i] = gbm_bo_get_stride_for_plane (shared_bo, i); + offsets[i] = gbm_bo_get_offset (shared_bo, i); + modifiers[i] = gbm_bo_get_modifier (shared_bo); ++ fds[i] = shared_bo_fd; + } + +- egl_image = create_egl_image (egl, +- egl_display, +- egl_context, +- width, height, +- n_planes, +- strides, offsets, +- modifiers, format, +- shared_bo_fd, +- error); ++ /* Workaround for https://gitlab.gnome.org/GNOME/mutter/issues/18 */ ++ if (modifiers[0] == DRM_FORMAT_MOD_LINEAR || ++ modifiers[0] == DRM_FORMAT_MOD_INVALID) ++ use_modifiers = FALSE; ++ else ++ use_modifiers = TRUE; ++ ++ egl_image = meta_egl_create_dmabuf_image (egl, ++ egl_display, ++ width, ++ height, ++ format, ++ n_planes, ++ fds, ++ strides, ++ offsets, ++ use_modifiers ? modifiers : NULL, ++ error); + close (shared_bo_fd); + + if (!egl_image) +-- +2.26.2 + + +From 5af71a6c4a723951b26597ee710462b18867d113 Mon Sep 17 00:00:00 2001 +From: Pekka Paalanen +Date: Thu, 29 Nov 2018 13:37:16 +0200 +Subject: [PATCH 20/49] renderer/native: Add meta_dumb_buffer_ensure_dmabuf_fd + +Follow-up work will use this in an attempt to use the primary GPU to +copy into secondary GPU dumb buffers. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/615 +--- + src/backends/native/meta-renderer-native.c | 36 ++++++++++++++++++++++ + 1 file changed, 36 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index ffb64a6bd..76cc79e23 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -133,6 +133,7 @@ typedef struct _MetaDumbBuffer + int height; + int stride_bytes; + uint32_t drm_format; ++ int dmabuf_fd; + } MetaDumbBuffer; + + typedef struct _MetaOnscreenNativeSecondaryGpuState +@@ -242,6 +243,10 @@ init_dumb_fb (MetaDumbBuffer *dumb_fb, + uint32_t format, + GError **error); + ++static int ++meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, ++ MetaGpuKms *gpu_kms); ++ + static MetaEgl * + meta_renderer_native_get_egl (MetaRendererNative *renderer_native); + +@@ -2892,6 +2897,7 @@ init_dumb_fb (MetaDumbBuffer *dumb_fb, + dumb_fb->height = height; + dumb_fb->stride_bytes = create_arg.pitch; + dumb_fb->drm_format = format; ++ dumb_fb->dmabuf_fd = -1; + + return TRUE; + +@@ -2909,6 +2915,33 @@ err_ioctl: + return FALSE; + } + ++static int ++meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, ++ MetaGpuKms *gpu_kms) ++{ ++ int ret; ++ int kms_fd; ++ int dmabuf_fd; ++ ++ if (dumb_fb->dmabuf_fd != -1) ++ return dumb_fb->dmabuf_fd; ++ ++ kms_fd = meta_gpu_kms_get_fd (gpu_kms); ++ ++ ret = drmPrimeHandleToFD (kms_fd, dumb_fb->handle, DRM_CLOEXEC, ++ &dmabuf_fd); ++ if (ret) ++ { ++ g_debug ("Failed to export dumb drm buffer: %s", ++ g_strerror (errno)); ++ return -1; ++ } ++ ++ dumb_fb->dmabuf_fd = dmabuf_fd; ++ ++ return dumb_fb->dmabuf_fd; ++} ++ + static void + release_dumb_fb (MetaDumbBuffer *dumb_fb, + MetaGpuKms *gpu_kms) +@@ -2919,6 +2952,9 @@ release_dumb_fb (MetaDumbBuffer *dumb_fb, + if (!dumb_fb->map) + return; + ++ if (dumb_fb->dmabuf_fd != -1) ++ close (dumb_fb->dmabuf_fd); ++ + munmap (dumb_fb->map, dumb_fb->map_size); + dumb_fb->map = NULL; + +-- +2.26.2 + + +From a6e2e1cb5fd25e152bbc3347078b0e9261a94ceb Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 16 Jul 2019 18:27:38 +0200 +Subject: [PATCH 21/49] cogl/texture: Make is_get_data_supported() a bool on + the texture + +Comparing the gl target is not enough. More on that later. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 +--- + cogl/cogl/cogl-texture-2d-private.h | 1 + + cogl/cogl/cogl-texture-2d.c | 1 + + cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 6 ++---- + 3 files changed, 4 insertions(+), 4 deletions(-) + +diff --git a/cogl/cogl/cogl-texture-2d-private.h b/cogl/cogl/cogl-texture-2d-private.h +index 450d156f1..c1e6dff50 100644 +--- a/cogl/cogl/cogl-texture-2d-private.h ++++ b/cogl/cogl/cogl-texture-2d-private.h +@@ -47,6 +47,7 @@ struct _CoglTexture2D + gboolean auto_mipmap; + gboolean mipmaps_dirty; + gboolean is_foreign; ++ gboolean is_get_data_supported; + + /* TODO: factor out these OpenGL specific members into some form + * of driver private state. */ +diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c +index 76f0e3a87..76cb75a5c 100644 +--- a/cogl/cogl/cogl-texture-2d.c ++++ b/cogl/cogl/cogl-texture-2d.c +@@ -107,6 +107,7 @@ _cogl_texture_2d_create_base (CoglContext *ctx, + + tex_2d->mipmaps_dirty = TRUE; + tex_2d->auto_mipmap = TRUE; ++ tex_2d->is_get_data_supported = TRUE; + + tex_2d->gl_target = GL_TEXTURE_2D; + +diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +index e36c3523e..a9329ad50 100644 +--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c ++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +@@ -514,6 +514,7 @@ allocate_custom_egl_image_external (CoglTexture2D *tex_2d, + + tex_2d->internal_format = internal_format; + tex_2d->gl_target = GL_TEXTURE_EXTERNAL_OES; ++ tex_2d->is_get_data_supported = FALSE; + + return TRUE; + } +@@ -840,10 +841,7 @@ _cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, + gboolean + _cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d) + { +- if (tex_2d->gl_target == GL_TEXTURE_EXTERNAL_OES) +- return FALSE; +- else +- return TRUE; ++ return tex_2d->is_get_data_supported; + } + + void +-- +2.26.2 + + +From 50108eca1503c8883370f0f877cab8d25f89ad2e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 16 Jul 2019 18:29:29 +0200 +Subject: [PATCH 22/49] cogl/texture: Add EGLImage texture import flags + +The flags are 'none', and 'no-get-data' meaning get_data() is not +supported. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 +--- + cogl/cogl/cogl-texture-2d.c | 3 +++ + cogl/cogl/cogl-texture-2d.h | 7 +++++++ + cogl/cogl/cogl-texture-private.h | 1 + + cogl/cogl/driver/gl/cogl-texture-2d-gl.c | 2 ++ + cogl/cogl/winsys/cogl-winsys-egl-x11.c | 1 + + src/wayland/meta-wayland-buffer.c | 3 +++ + src/wayland/meta-wayland-dma-buf.c | 3 +++ + 7 files changed, 20 insertions(+) + +diff --git a/cogl/cogl/cogl-texture-2d.c b/cogl/cogl/cogl-texture-2d.c +index 76cb75a5c..6544f8a62 100644 +--- a/cogl/cogl/cogl-texture-2d.c ++++ b/cogl/cogl/cogl-texture-2d.c +@@ -242,6 +242,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, + int height, + CoglPixelFormat format, + EGLImageKHR image, ++ CoglEglImageFlags flags, + CoglError **error) + { + CoglTextureLoader *loader; +@@ -262,6 +263,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, + loader->src.egl_image.width = width; + loader->src.egl_image.height = height; + loader->src.egl_image.format = format; ++ loader->src.egl_image.flags = flags; + + tex = _cogl_texture_2d_create_base (ctx, width, height, format, loader); + +@@ -436,6 +438,7 @@ cogl_wayland_texture_2d_new_from_buffer (CoglContext *ctx, + width, height, + internal_format, + image, ++ COGL_EGL_IMAGE_FLAG_NONE, + error); + _cogl_egl_destroy_image (ctx, image); + return tex; +diff --git a/cogl/cogl/cogl-texture-2d.h b/cogl/cogl/cogl-texture-2d.h +index 29b8b2d6b..44b9d4636 100644 +--- a/cogl/cogl/cogl-texture-2d.h ++++ b/cogl/cogl/cogl-texture-2d.h +@@ -65,6 +65,12 @@ G_BEGIN_DECLS + typedef struct _CoglTexture2D CoglTexture2D; + #define COGL_TEXTURE_2D(X) ((CoglTexture2D *)X) + ++typedef enum _CoglEglImageFlags ++{ ++ COGL_EGL_IMAGE_FLAG_NONE = 0, ++ COGL_EGL_IMAGE_FLAG_NO_GET_DATA = 1 << 0, ++} CoglEglImageFlags; ++ + /** + * cogl_texture_2d_get_gtype: + * +@@ -244,6 +250,7 @@ cogl_egl_texture_2d_new_from_image (CoglContext *ctx, + int height, + CoglPixelFormat format, + EGLImageKHR image, ++ CoglEglImageFlags flags, + CoglError **error); + + typedef gboolean (*CoglTexture2DEGLImageExternalAlloc) (CoglTexture2D *tex_2d, +diff --git a/cogl/cogl/cogl-texture-private.h b/cogl/cogl/cogl-texture-private.h +index 05a0045f8..794d0d7e7 100644 +--- a/cogl/cogl/cogl-texture-private.h ++++ b/cogl/cogl/cogl-texture-private.h +@@ -184,6 +184,7 @@ typedef struct _CoglTextureLoader + int width; + int height; + CoglPixelFormat format; ++ CoglEglImageFlags flags; + } egl_image; + #endif + #if defined (COGL_HAS_EGL_SUPPORT) +diff --git a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +index a9329ad50..34432ccba 100644 +--- a/cogl/cogl/driver/gl/cogl-texture-2d-gl.c ++++ b/cogl/cogl/driver/gl/cogl-texture-2d-gl.c +@@ -328,6 +328,8 @@ allocate_from_egl_image (CoglTexture2D *tex_2d, + } + + tex_2d->internal_format = internal_format; ++ tex_2d->is_get_data_supported = ++ !(loader->src.egl_image.flags & COGL_EGL_IMAGE_FLAG_NO_GET_DATA); + + _cogl_texture_set_allocated (tex, + internal_format, +diff --git a/cogl/cogl/winsys/cogl-winsys-egl-x11.c b/cogl/cogl/winsys/cogl-winsys-egl-x11.c +index 4f0a12543..6a89206a1 100644 +--- a/cogl/cogl/winsys/cogl-winsys-egl-x11.c ++++ b/cogl/cogl/winsys/cogl-winsys-egl-x11.c +@@ -802,6 +802,7 @@ _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) + tex->height, + texture_format, + egl_tex_pixmap->image, ++ COGL_EGL_IMAGE_FLAG_NONE, + NULL)); + + tex_pixmap->winsys = egl_tex_pixmap; +diff --git a/src/wayland/meta-wayland-buffer.c b/src/wayland/meta-wayland-buffer.c +index f45679d3a..cdaad26eb 100644 +--- a/src/wayland/meta-wayland-buffer.c ++++ b/src/wayland/meta-wayland-buffer.c +@@ -289,6 +289,7 @@ egl_image_buffer_attach (MetaWaylandBuffer *buffer, + int format, width, height, y_inverted; + CoglPixelFormat cogl_format; + EGLImageKHR egl_image; ++ CoglEglImageFlags flags; + CoglTexture2D *texture_2d; + + if (buffer->egl_image.texture) +@@ -343,10 +344,12 @@ egl_image_buffer_attach (MetaWaylandBuffer *buffer, + if (egl_image == EGL_NO_IMAGE_KHR) + return FALSE; + ++ flags = COGL_EGL_IMAGE_FLAG_NONE; + texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context, + width, height, + cogl_format, + egl_image, ++ flags, + error); + + meta_egl_destroy_image (egl, egl_display, egl_image, NULL); +diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c +index e49fba9cf..786b88304 100644 +--- a/src/wayland/meta-wayland-dma-buf.c ++++ b/src/wayland/meta-wayland-dma-buf.c +@@ -77,6 +77,7 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, + MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf; + CoglPixelFormat cogl_format; + EGLImageKHR egl_image; ++ CoglEglImageFlags flags; + CoglTexture2D *texture; + EGLint attribs[64]; + int attr_idx = 0; +@@ -184,11 +185,13 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, + if (egl_image == EGL_NO_IMAGE_KHR) + return FALSE; + ++ flags = COGL_EGL_IMAGE_FLAG_NONE; + texture = cogl_egl_texture_2d_new_from_image (cogl_context, + dma_buf->width, + dma_buf->height, + cogl_format, + egl_image, ++ flags, + error); + + meta_egl_destroy_image (egl, egl_display, egl_image, NULL); +-- +2.26.2 + + +From cca085b00c029debaed80c070e316a3a7ff7e44d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 16 Jul 2019 18:30:12 +0200 +Subject: [PATCH 23/49] dma-buf: Mark DMA-BUF textures as paint-only + +Reading pixels directly from a texture imported from a DMA-BUF EGLImage +may result compressed textures to be transferred into non-compressed +texture. This may have side effects causing it to be rendered +incorrectly in subsequent paints. + +Avoid this by passing the no-get-data flag to the texture creator +function, eventually causing mutter to use an intermediate offscreen +framebuffer when reading pixels from such textures. + +https://bugs.freedesktop.org/show_bug.cgi?id=111140 +https://gitlab.freedesktop.org/xorg/xserver/issues/545 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/687 +--- + src/wayland/meta-wayland-dma-buf.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/wayland/meta-wayland-dma-buf.c b/src/wayland/meta-wayland-dma-buf.c +index 786b88304..5661fb6ff 100644 +--- a/src/wayland/meta-wayland-dma-buf.c ++++ b/src/wayland/meta-wayland-dma-buf.c +@@ -185,7 +185,7 @@ meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, + if (egl_image == EGL_NO_IMAGE_KHR) + return FALSE; + +- flags = COGL_EGL_IMAGE_FLAG_NONE; ++ flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; + texture = cogl_egl_texture_2d_new_from_image (cogl_context, + dma_buf->width, + dma_buf->height, +-- +2.26.2 + + +From 8e25a750e9084cbc4f7e7048453ef33dfa9ea314 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 24 Feb 2020 12:44:52 -0300 +Subject: [PATCH 24/49] cogl/framebuffer: Add cogl_framebuffer_flush + +In future patches, we'll create additional CoglFramebuffers that +will be shared via DMA-Buf with PipeWire. When recording frames, +we'll blit the current onscreen framebuffer into the shared one. + +However, that presents a problem: cogl_framebuffer_blit() mimics +glBlitFramebuffer() semantics, and doesn't do an implicit flush +of the GPU command stream. As a consequence, clients may receive +unblitted or incomplete framebuffers. + +We could use cogl_framebuffer_finish() to ensure the commands were +submitted to the GPU, but it is too harsh -- it blocks the CPU +completely until the commands are finished! + +Add cogl_framebuffer_flush(), which ensures the command stream is +submitted to the GPU without blocking the CPU. Even though we don't +use the framebuffer specifically, it may be useful in the future +for e.g. a potential Vulkan backend to have access to the framebuffer. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + cogl/cogl/cogl-driver.h | 3 +++ + cogl/cogl/cogl-framebuffer.c | 11 +++++++++++ + cogl/cogl/cogl-framebuffer.h | 12 ++++++++++++ + cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h | 3 +++ + cogl/cogl/driver/gl/cogl-framebuffer-gl.c | 6 ++++++ + cogl/cogl/driver/gl/gl/cogl-driver-gl.c | 1 + + cogl/cogl/driver/gl/gles/cogl-driver-gles.c | 1 + + cogl/cogl/driver/nop/cogl-driver-nop.c | 1 + + cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h | 3 +++ + cogl/cogl/driver/nop/cogl-framebuffer-nop.c | 5 +++++ + 10 files changed, 46 insertions(+) + +diff --git a/cogl/cogl/cogl-driver.h b/cogl/cogl/cogl-driver.h +index 86682d8a7..af92146d0 100644 +--- a/cogl/cogl/cogl-driver.h ++++ b/cogl/cogl/cogl-driver.h +@@ -87,6 +87,9 @@ struct _CoglDriverVtable + void + (* framebuffer_finish) (CoglFramebuffer *framebuffer); + ++ void ++ (* framebuffer_flush) (CoglFramebuffer *framebuffer); ++ + void + (* framebuffer_discard_buffers) (CoglFramebuffer *framebuffer, + unsigned long buffers); +diff --git a/cogl/cogl/cogl-framebuffer.c b/cogl/cogl/cogl-framebuffer.c +index 948cd112d..d64fc89fb 100644 +--- a/cogl/cogl/cogl-framebuffer.c ++++ b/cogl/cogl/cogl-framebuffer.c +@@ -1568,6 +1568,17 @@ cogl_framebuffer_finish (CoglFramebuffer *framebuffer) + ctx->driver_vtable->framebuffer_finish (framebuffer); + } + ++void ++cogl_framebuffer_flush (CoglFramebuffer *framebuffer) ++{ ++ ++ CoglContext *ctx = framebuffer->context; ++ ++ _cogl_framebuffer_flush_journal (framebuffer); ++ ++ ctx->driver_vtable->framebuffer_flush (framebuffer); ++} ++ + void + cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer) + { +diff --git a/cogl/cogl/cogl-framebuffer.h b/cogl/cogl/cogl-framebuffer.h +index 230a78627..38ada9feb 100644 +--- a/cogl/cogl/cogl-framebuffer.h ++++ b/cogl/cogl/cogl-framebuffer.h +@@ -1910,6 +1910,18 @@ cogl_blit_framebuffer (CoglFramebuffer *src, + int height, + GError **error); + ++/** ++ * cogl_framebuffer_flush: ++ * @framebuffer: A #CoglFramebuffer pointer ++ * ++ * Flushes @framebuffer to ensure the current batch of commands is ++ * submitted to the GPU. ++ * ++ * Unlike cogl_framebuffer_finish(), this does not block the CPU. ++ */ ++void ++cogl_framebuffer_flush (CoglFramebuffer *framebuffer); ++ + G_END_DECLS + + #endif /* __COGL_FRAMEBUFFER_H */ +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h +index 214f45f0f..bbd7b0e99 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h +@@ -61,6 +61,9 @@ _cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, + void + _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer); + ++void ++_cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer); ++ + void + _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers); +diff --git a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +index 90d08954d..6466fd6bc 100644 +--- a/cogl/cogl/driver/gl/cogl-framebuffer-gl.c ++++ b/cogl/cogl/driver/gl/cogl-framebuffer-gl.c +@@ -1115,6 +1115,12 @@ _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer) + GE (framebuffer->context, glFinish ()); + } + ++void ++_cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer) ++{ ++ GE (framebuffer->context, glFlush ()); ++} ++ + void + _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers) +diff --git a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +index e06e27961..716617b54 100644 +--- a/cogl/cogl/driver/gl/gl/cogl-driver-gl.c ++++ b/cogl/cogl/driver/gl/gl/cogl-driver-gl.c +@@ -668,6 +668,7 @@ _cogl_driver_gl = + _cogl_framebuffer_gl_clear, + _cogl_framebuffer_gl_query_bits, + _cogl_framebuffer_gl_finish, ++ _cogl_framebuffer_gl_flush, + _cogl_framebuffer_gl_discard_buffers, + _cogl_framebuffer_gl_draw_attributes, + _cogl_framebuffer_gl_draw_indexed_attributes, +diff --git a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +index bcb0bdf07..902bd0bd3 100644 +--- a/cogl/cogl/driver/gl/gles/cogl-driver-gles.c ++++ b/cogl/cogl/driver/gl/gles/cogl-driver-gles.c +@@ -447,6 +447,7 @@ _cogl_driver_gles = + _cogl_framebuffer_gl_clear, + _cogl_framebuffer_gl_query_bits, + _cogl_framebuffer_gl_finish, ++ _cogl_framebuffer_gl_flush, + _cogl_framebuffer_gl_discard_buffers, + _cogl_framebuffer_gl_draw_attributes, + _cogl_framebuffer_gl_draw_indexed_attributes, +diff --git a/cogl/cogl/driver/nop/cogl-driver-nop.c b/cogl/cogl/driver/nop/cogl-driver-nop.c +index b41a2bcc5..8cab69afa 100644 +--- a/cogl/cogl/driver/nop/cogl-driver-nop.c ++++ b/cogl/cogl/driver/nop/cogl-driver-nop.c +@@ -66,6 +66,7 @@ _cogl_driver_nop = + _cogl_framebuffer_nop_clear, + _cogl_framebuffer_nop_query_bits, + _cogl_framebuffer_nop_finish, ++ _cogl_framebuffer_nop_flush, + _cogl_framebuffer_nop_discard_buffers, + _cogl_framebuffer_nop_draw_attributes, + _cogl_framebuffer_nop_draw_indexed_attributes, +diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h +index 05bbde6b4..810b98413 100644 +--- a/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h ++++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h +@@ -64,6 +64,9 @@ _cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, + void + _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer); + ++void ++_cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer); ++ + void + _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers); +diff --git a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c +index 643e304ea..db9819524 100644 +--- a/cogl/cogl/driver/nop/cogl-framebuffer-nop.c ++++ b/cogl/cogl/driver/nop/cogl-framebuffer-nop.c +@@ -76,6 +76,11 @@ _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer) + { + } + ++void ++_cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer) ++{ ++} ++ + void + _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, + unsigned long buffers) +-- +2.26.2 + + +From 80affcf2cd04a0a05dd6cb45ea7475b12d9f6841 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 9 Dec 2019 10:03:07 -0300 +Subject: [PATCH 25/49] cogl/context: Add cogl_renderer_create_dma_buf() and + family + +This is a winsys-specific API that allows exporting a DMA buffer fd. +The CoglDmaBufHandle structure allows passing the ownership of the +DMA buffer to whoever is using it, so the winsys doesn't need to +manually track it. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + cogl/cogl/cogl-dma-buf-handle.c | 94 ++++++++++++++++++++++++++ + cogl/cogl/cogl-dma-buf-handle.h | 83 +++++++++++++++++++++++ + cogl/cogl/cogl-renderer.c | 14 ++++ + cogl/cogl/cogl-renderer.h | 21 ++++++ + cogl/cogl/cogl-types.h | 8 +++ + cogl/cogl/cogl.h | 1 + + cogl/cogl/meson.build | 2 + + cogl/cogl/winsys/cogl-winsys-private.h | 6 ++ + 8 files changed, 229 insertions(+) + create mode 100644 cogl/cogl/cogl-dma-buf-handle.c + create mode 100644 cogl/cogl/cogl-dma-buf-handle.h + +diff --git a/cogl/cogl/cogl-dma-buf-handle.c b/cogl/cogl/cogl-dma-buf-handle.c +new file mode 100644 +index 000000000..4a8f709f2 +--- /dev/null ++++ b/cogl/cogl/cogl-dma-buf-handle.c +@@ -0,0 +1,94 @@ ++/* ++ * Cogl ++ * ++ * A Low Level GPU Graphics and Utilities API ++ * ++ * Copyright (C) 2020 Endless, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, ++ * modify, merge, publish, distribute, sublicense, and/or sell copies ++ * of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ * Authors: ++ * Georges Basile Stavracas Neto ++ */ ++ ++#include "cogl-config.h" ++ ++#include "cogl-dma-buf-handle.h" ++#include "cogl-object.h" ++ ++#include ++ ++struct _CoglDmaBufHandle ++{ ++ CoglFramebuffer *framebuffer; ++ int dmabuf_fd; ++ gpointer user_data; ++ GDestroyNotify destroy_func; ++}; ++ ++CoglDmaBufHandle * ++cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, ++ int dmabuf_fd, ++ gpointer user_data, ++ GDestroyNotify destroy_func) ++{ ++ CoglDmaBufHandle *dmabuf_handle; ++ ++ g_assert (framebuffer); ++ g_assert (dmabuf_fd != -1); ++ ++ dmabuf_handle = g_new0 (CoglDmaBufHandle, 1); ++ dmabuf_handle->framebuffer = cogl_object_ref (framebuffer); ++ dmabuf_handle->dmabuf_fd = dmabuf_fd; ++ dmabuf_handle->user_data = user_data; ++ dmabuf_handle->destroy_func = destroy_func; ++ ++ return dmabuf_handle; ++} ++ ++void ++cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle) ++{ ++ g_return_if_fail (dmabuf_handle != NULL); ++ ++ g_clear_pointer (&dmabuf_handle->framebuffer, cogl_object_unref); ++ ++ if (dmabuf_handle->destroy_func) ++ g_clear_pointer (&dmabuf_handle->user_data, dmabuf_handle->destroy_func); ++ ++ if (dmabuf_handle->dmabuf_fd != -1) ++ close (dmabuf_handle->dmabuf_fd); ++ ++ g_free (dmabuf_handle); ++} ++ ++CoglFramebuffer * ++cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->framebuffer; ++} ++ ++int ++cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle) ++{ ++ return dmabuf_handle->dmabuf_fd; ++} ++ +diff --git a/cogl/cogl/cogl-dma-buf-handle.h b/cogl/cogl/cogl-dma-buf-handle.h +new file mode 100644 +index 000000000..25b9b0ccb +--- /dev/null ++++ b/cogl/cogl/cogl-dma-buf-handle.h +@@ -0,0 +1,83 @@ ++/* ++ * Cogl ++ * ++ * A Low Level GPU Graphics and Utilities API ++ * ++ * Copyright (C) 2020 Endless, Inc. ++ * ++ * Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation ++ * files (the "Software"), to deal in the Software without ++ * restriction, including without limitation the rights to use, copy, ++ * modify, merge, publish, distribute, sublicense, and/or sell copies ++ * of the Software, and to permit persons to whom the Software is ++ * furnished to do so, subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ * ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS ++ * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ++ * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN ++ * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE ++ * SOFTWARE. ++ * ++ * Authors: ++ * Georges Basile Stavracas Neto ++ */ ++ ++ ++#if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) ++#error "Only can be included directly." ++#endif ++ ++#ifndef __COGL_DMA_BUF_HANDLE_H__ ++#define __COGL_DMA_BUF_HANDLE_H__ ++ ++#include ++#include ++ ++/** ++ * cogl_dma_buf_handle_new: (skip) ++ */ ++CoglDmaBufHandle * ++cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, ++ int dmabuf_fd, ++ gpointer data, ++ GDestroyNotify destroy_func); ++ ++/** ++ * cogl_dma_buf_handle_free: (skip) ++ * ++ * Releases @dmabuf_handle; it is a programming error to release ++ * an already released handle. ++ */ ++void ++cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dma_buf_handle_get_framebuffer: (skip) ++ * ++ * Retrieves the #CoglFramebuffer, backed by an exported DMABuf buffer, ++ * of @dmabuf_handle. ++ * ++ * Returns: (transfer none): a #CoglFramebuffer ++ */ ++CoglFramebuffer * ++cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle); ++ ++/** ++ * cogl_dma_buf_handle_get_fd: (skip) ++ * ++ * Retrieves the file descriptor of @dmabuf_handle. ++ * ++ * Returns: a valid file descriptor ++ */ ++int ++cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle); ++ ++ ++#endif /* __COGL_DMA_BUF_HANDLE_H__ */ +diff --git a/cogl/cogl/cogl-renderer.c b/cogl/cogl/cogl-renderer.c +index 854aa1fdd..8c2ff0702 100644 +--- a/cogl/cogl/cogl-renderer.c ++++ b/cogl/cogl/cogl-renderer.c +@@ -838,3 +838,17 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, + for (l = renderer->outputs; l; l = l->next) + callback (l->data, user_data); + } ++ ++CoglDmaBufHandle * ++cogl_renderer_create_dma_buf (CoglRenderer *renderer, ++ int width, ++ int height, ++ GError **error) ++{ ++ const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); ++ ++ if (winsys->renderer_create_dma_buf) ++ return winsys->renderer_create_dma_buf (renderer, width, height, error); ++ ++ return NULL; ++} +diff --git a/cogl/cogl/cogl-renderer.h b/cogl/cogl/cogl-renderer.h +index 0b3ddb8f2..c5e904106 100644 +--- a/cogl/cogl/cogl-renderer.h ++++ b/cogl/cogl/cogl-renderer.h +@@ -418,6 +418,27 @@ cogl_renderer_foreach_output (CoglRenderer *renderer, + CoglOutputCallback callback, + void *user_data); + ++/** ++ * cogl_renderer_create_dma_buf: (skip) ++ * @renderer: A #CoglRenderer ++ * @width: width of the new ++ * @height: height of the new ++ * @error: (nullable): return location for a #GError ++ * ++ * Creates a new #CoglFramebuffer with @width x @height, and format ++ * hardcoded to XRGB, and exports the new framebuffer's DMA buffer ++ * handle. ++ * ++ * Returns: (nullable)(transfer full): a #CoglDmaBufHandle. The ++ * return result must be released with cogl_dma_buf_handle_free() ++ * after use. ++ */ ++CoglDmaBufHandle * ++cogl_renderer_create_dma_buf (CoglRenderer *renderer, ++ int width, ++ int height, ++ GError **error); ++ + G_END_DECLS + + #endif /* __COGL_RENDERER_H__ */ +diff --git a/cogl/cogl/cogl-types.h b/cogl/cogl/cogl-types.h +index 69d304cf0..1d420dc2c 100644 +--- a/cogl/cogl/cogl-types.h ++++ b/cogl/cogl/cogl-types.h +@@ -138,6 +138,14 @@ typedef int32_t CoglAngle; + typedef struct _CoglColor CoglColor; + typedef struct _CoglTextureVertex CoglTextureVertex; + ++/** ++ * CoglDmaBufHandle: (skip) ++ * ++ * An opaque type that tracks the lifetime of a DMA buffer fd. Release ++ * with cogl_dma_buf_handle_free(). ++ */ ++typedef struct _CoglDmaBufHandle CoglDmaBufHandle; ++ + /* Enum declarations */ + + #define COGL_A_BIT (1 << 4) +diff --git a/cogl/cogl/cogl.h b/cogl/cogl/cogl.h +index 565ea289a..065278d36 100644 +--- a/cogl/cogl/cogl.h ++++ b/cogl/cogl/cogl.h +@@ -61,6 +61,7 @@ + #include + #include + #include ++#include + #include + #include + #include +diff --git a/cogl/cogl/meson.build b/cogl/cogl/meson.build +index 8032669e4..1934e7574 100644 +--- a/cogl/cogl/meson.build ++++ b/cogl/cogl/meson.build +@@ -102,6 +102,7 @@ cogl_nonintrospected_headers = [ + 'cogl-renderer.h', + 'cogl-swap-chain.h', + 'cogl-onscreen-template.h', ++ 'cogl-dma-buf-handle.h', + 'cogl-display.h', + 'cogl-context.h', + 'cogl-snippet.h', +@@ -217,6 +218,7 @@ cogl_sources = [ + 'cogl-i18n-private.h', + 'cogl-debug.h', + 'cogl-debug-options.h', ++ 'cogl-dma-buf-handle.c', + 'cogl-gpu-info.c', + 'cogl-gpu-info-private.h', + 'cogl-context-private.h', +diff --git a/cogl/cogl/winsys/cogl-winsys-private.h b/cogl/cogl/winsys/cogl-winsys-private.h +index c6b2f3579..59c8945ce 100644 +--- a/cogl/cogl/winsys/cogl-winsys-private.h ++++ b/cogl/cogl/winsys/cogl-winsys-private.h +@@ -100,6 +100,12 @@ typedef struct _CoglWinsysVtable + void + (*display_destroy) (CoglDisplay *display); + ++ CoglDmaBufHandle * ++ (*renderer_create_dma_buf) (CoglRenderer *renderer, ++ int width, ++ int height, ++ GError **error); ++ + gboolean + (*context_init) (CoglContext *context, CoglError **error); + +-- +2.26.2 + + +From aee3b6d1933375b9b8fb144c523a6227cd935142 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 9 Dec 2019 10:04:56 -0300 +Subject: [PATCH 26/49] renderer-native: Move DMA buffer creation to an + auxiliary function + +This will be reused by the DMA buffer exporting function. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/native/meta-renderer-native.c | 92 +++++++++++++++++++--- + 1 file changed, 83 insertions(+), 9 deletions(-) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 76cc79e23..75d6ec4c2 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2214,6 +2214,89 @@ wait_for_pending_flips (CoglOnscreen *onscreen) + meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, NULL); + } + ++static CoglContext * ++cogl_context_from_renderer_native (MetaRendererNative *renderer_native) ++{ ++ MetaBackend *backend = backend_from_renderer_native (renderer_native); ++ ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ++ ++ return clutter_backend_get_cogl_context (clutter_backend); ++} ++ ++static CoglFramebuffer * ++create_dma_buf_framebuffer (MetaRendererNative *renderer_native, ++ int dmabuf_fd, ++ uint32_t width, ++ uint32_t height, ++ uint32_t stride, ++ uint32_t offset, ++ uint64_t modifier, ++ uint32_t drm_format, ++ GError **error) ++{ ++ CoglContext *cogl_context = ++ cogl_context_from_renderer_native (renderer_native); ++ CoglDisplay *cogl_display = cogl_context->display; ++ CoglRenderer *cogl_renderer = cogl_display->renderer; ++ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; ++ EGLDisplay egl_display = cogl_renderer_egl->edpy; ++ MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); ++ EGLImageKHR egl_image; ++ uint32_t strides[1]; ++ uint32_t offsets[1]; ++ uint64_t modifiers[1]; ++ CoglPixelFormat cogl_format; ++ CoglEglImageFlags flags; ++ CoglTexture2D *cogl_tex; ++ CoglOffscreen *cogl_fbo; ++ int ret; ++ ++ ret = cogl_pixel_format_from_drm_format (drm_format, &cogl_format, NULL); ++ g_assert (ret); ++ ++ strides[0] = stride; ++ offsets[0] = offset; ++ modifiers[0] = modifier; ++ egl_image = meta_egl_create_dmabuf_image (egl, ++ egl_display, ++ width, ++ height, ++ drm_format, ++ 1 /* n_planes */, ++ &dmabuf_fd, ++ strides, ++ offsets, ++ modifiers, ++ error); ++ if (egl_image == EGL_NO_IMAGE_KHR) ++ return NULL; ++ ++ flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; ++ cogl_tex = cogl_egl_texture_2d_new_from_image (cogl_context, ++ width, ++ height, ++ cogl_format, ++ egl_image, ++ flags, ++ error); ++ ++ meta_egl_destroy_image (egl, egl_display, egl_image, NULL); ++ ++ if (!cogl_tex) ++ return NULL; ++ ++ cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex)); ++ cogl_object_unref (cogl_tex); ++ ++ if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (cogl_fbo), error)) ++ { ++ cogl_object_unref (cogl_fbo); ++ return NULL; ++ } ++ ++ return COGL_FRAMEBUFFER (cogl_fbo); ++} ++ + static void + copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, + MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, +@@ -3457,15 +3540,6 @@ calculate_view_transform (MetaMonitorManager *monitor_manager, + return crtc_transform; + } + +-static CoglContext * +-cogl_context_from_renderer_native (MetaRendererNative *renderer_native) +-{ +- MetaBackend *backend = backend_from_renderer_native (renderer_native); +- ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); +- +- return clutter_backend_get_cogl_context (clutter_backend); +-} +- + static gboolean + should_force_shadow_fb (MetaRendererNative *renderer_native, + MetaGpuKms *primary_gpu) +-- +2.26.2 + + +From 1eab96d6e3813ec5745e882d9a195adf4a226ff6 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 9 Dec 2019 10:05:37 -0300 +Subject: [PATCH 27/49] renderer-native: Implement DMA buffer creation + +Create a new gbm_bo using the same given geometry, and export the new +bo's DMA buffer fd. The new bo lives as long as necessary to be used, +and reused, by PipeWire. + +Unfortunately, PipeWire doesn't support modifiers properly, so use the +linear format for now. For now, a hardcoded format of DRM_FORMAT_XRGB8888 +is set, so we don't need to negotiate the format with PipeWire early. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/native/meta-renderer-native.c | 68 ++++++++++++++++++++++ + 1 file changed, 68 insertions(+) + +diff --git a/src/backends/native/meta-renderer-native.c b/src/backends/native/meta-renderer-native.c +index 75d6ec4c2..ba98de650 100644 +--- a/src/backends/native/meta-renderer-native.c ++++ b/src/backends/native/meta-renderer-native.c +@@ -2618,6 +2618,73 @@ meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, + _cogl_winsys_egl_ensure_current (cogl_display); + } + ++static CoglDmaBufHandle * ++meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, ++ int width, ++ int height, ++ GError **error) ++{ ++ CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; ++ MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; ++ MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; ++ ++ switch (renderer_gpu_data->mode) ++ { ++ case META_RENDERER_NATIVE_MODE_GBM: ++ { ++ CoglFramebuffer *dmabuf_fb; ++ struct gbm_bo *new_bo; ++ int dmabuf_fd = -1; ++ ++ new_bo = gbm_bo_create (renderer_gpu_data->gbm.device, ++ width, height, DRM_FORMAT_XRGB8888, ++ GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); ++ ++ if (!new_bo) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, ++ "Failed to allocate buffer"); ++ return NULL; ++ } ++ ++ dmabuf_fd = gbm_bo_get_fd (new_bo); ++ ++ if (dmabuf_fd == -1) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, ++ "Failed to export buffer's DMA fd: %s", ++ g_strerror (errno)); ++ return NULL; ++ } ++ ++ dmabuf_fb = create_dma_buf_framebuffer (renderer_native, ++ dmabuf_fd, ++ width, height, ++ gbm_bo_get_stride (new_bo), ++ gbm_bo_get_offset (new_bo, 0), ++ DRM_FORMAT_MOD_LINEAR, ++ DRM_FORMAT_XRGB8888, ++ error); ++ ++ if (!dmabuf_fb) ++ return NULL; ++ ++ return cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, ++ (GDestroyNotify) gbm_bo_destroy); ++ } ++ break; ++#ifdef HAVE_EGL_DEVICE ++ case META_RENDERER_NATIVE_MODE_EGL_DEVICE: ++ break; ++#endif ++ } ++ ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, ++ "Current mode does not support exporting DMA buffers"); ++ ++ return NULL; ++} ++ + static gboolean + meta_renderer_native_init_egl_context (CoglContext *cogl_context, + GError **error) +@@ -3461,6 +3528,7 @@ get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer) + + vtable.renderer_connect = meta_renderer_native_connect; + vtable.renderer_disconnect = meta_renderer_native_disconnect; ++ vtable.renderer_create_dma_buf = meta_renderer_native_create_dma_buf; + + vtable.onscreen_init = meta_renderer_native_init_onscreen; + vtable.onscreen_deinit = meta_renderer_native_release_onscreen; +-- +2.26.2 + + +From a8503b2e8b5573f9decf97264a26877adf65c4c0 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 9 Dec 2019 10:07:29 -0300 +Subject: [PATCH 28/49] screen-cast-stream-src: Support DMA buffer sharing + +Implement PipeWire's add_buffer and remove buffer, try and export +a DMA buffer first and, on failure, fallback to memfd. + +When DMA buffers are successfully created and shared, blit the +framebuffer contents when drawing instead of downloading the pixels. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/meta-screen-cast-stream-src.c | 189 ++++++++++++++++++--- + src/backends/meta-screen-cast-stream-src.h | 2 + + 2 files changed, 164 insertions(+), 27 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index ba1ce94a7..0eb9f4d8c 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -25,6 +25,7 @@ + #include "backends/meta-screen-cast-stream-src.h" + + #include ++#include + #include + #include + #include +@@ -90,6 +91,8 @@ typedef struct _MetaScreenCastStreamSrcPrivate + + uint64_t last_frame_timestamp_us; + ++ GHashTable *dmabuf_handles; ++ + int stream_width; + int stream_height; + } MetaScreenCastStreamSrcPrivate; +@@ -139,6 +142,19 @@ meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, + return klass->record_frame (src, data); + } + ++static gboolean ++meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) ++{ ++ MetaScreenCastStreamSrcClass *klass = ++ META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); ++ ++ if (klass->blit_to_framebuffer) ++ return klass->blit_to_framebuffer (src, framebuffer); ++ ++ return FALSE; ++} ++ + static void + meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -394,6 +410,33 @@ maybe_record_cursor (MetaScreenCastStreamSrc *src, + g_assert_not_reached (); + } + ++static gboolean ++do_record_frame (MetaScreenCastStreamSrc *src, ++ struct spa_buffer *spa_buffer, ++ uint8_t *data) ++{ ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ if (spa_buffer->datas[0].data || ++ spa_buffer->datas[0].type == SPA_DATA_MemFd) ++ { ++ return meta_screen_cast_stream_src_record_frame (src, data); ++ } ++ else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) ++ { ++ CoglDmaBufHandle *dmabuf_handle = ++ g_hash_table_lookup (priv->dmabuf_handles, ++ GINT_TO_POINTER (spa_buffer->datas[0].fd)); ++ CoglFramebuffer *dmabuf_fbo = ++ cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); ++ ++ return meta_screen_cast_stream_src_blit_to_framebuffer (src, dmabuf_fbo); ++ } ++ ++ return FALSE; ++} ++ + void + meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + { +@@ -402,8 +445,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + MetaRectangle crop_rect; + struct pw_buffer *buffer; + struct spa_buffer *spa_buffer; +- uint8_t *map = NULL; +- uint8_t *data; ++ uint8_t *data = NULL; + uint64_t now_us; + + now_us = g_get_monotonic_time (); +@@ -424,32 +466,15 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + } + + spa_buffer = buffer->buffer; ++ data = spa_buffer->datas[0].data; + +- if (spa_buffer->datas[0].data) +- { +- data = spa_buffer->datas[0].data; +- } +- else if (spa_buffer->datas[0].type == SPA_DATA_MemFd) ++ if (spa_buffer->datas[0].type != SPA_DATA_DmaBuf && !data) + { +- map = mmap (NULL, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset, +- PROT_READ | PROT_WRITE, MAP_SHARED, +- spa_buffer->datas[0].fd, 0); +- if (map == MAP_FAILED) +- { +- g_warning ("Failed to mmap pipewire stream buffer: %s", +- strerror (errno)); +- return; +- } +- +- data = SPA_MEMBER (map, spa_buffer->datas[0].mapoffset, uint8_t); +- } +- else +- { +- g_warning ("Unhandled spa buffer type: %d", spa_buffer->datas[0].type); ++ g_critical ("Invalid buffer data"); + return; + } + +- if (meta_screen_cast_stream_src_record_frame (src, data)) ++ if (do_record_frame (src, spa_buffer, data)) + { + struct spa_meta_region *spa_meta_video_crop; + +@@ -487,9 +512,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + + priv->last_frame_timestamp_us = now_us; + +- if (map) +- munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset); +- + pw_stream_queue_buffer (priv->pipewire_stream, buffer); + } + +@@ -618,10 +640,116 @@ on_stream_param_changed (void *data, + pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); + } + ++static void ++on_stream_add_buffer (void *data, ++ struct pw_buffer *buffer) ++{ ++ MetaScreenCastStreamSrc *src = data; ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ CoglContext *context = ++ clutter_backend_get_cogl_context (clutter_get_default_backend ()); ++ CoglRenderer *renderer = cogl_context_get_renderer (context); ++ g_autoptr (GError) error = NULL; ++ CoglDmaBufHandle *dmabuf_handle; ++ struct spa_buffer *spa_buffer = buffer->buffer; ++ struct spa_data *spa_data = spa_buffer->datas; ++ const int bpp = 4; ++ int stride; ++ ++ stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); ++ ++ spa_data[0].mapoffset = 0; ++ spa_data[0].maxsize = stride * priv->video_format.size.height; ++ ++ dmabuf_handle = cogl_renderer_create_dma_buf (renderer, ++ priv->stream_width, ++ priv->stream_height, ++ &error); ++ ++ if (error) ++ g_debug ("Error exporting DMA buffer handle: %s", error->message); ++ ++ if (dmabuf_handle) ++ { ++ spa_data[0].type = SPA_DATA_DmaBuf; ++ spa_data[0].flags = SPA_DATA_FLAG_READWRITE; ++ spa_data[0].fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); ++ spa_data[0].data = NULL; ++ ++ g_hash_table_insert (priv->dmabuf_handles, ++ GINT_TO_POINTER (spa_data[0].fd), ++ dmabuf_handle); ++ } ++ else ++ { ++ unsigned int seals; ++ ++ /* Fallback to a memfd buffer */ ++ spa_data[0].type = SPA_DATA_MemFd; ++ spa_data[0].flags = SPA_DATA_FLAG_READWRITE; ++ spa_data[0].fd = memfd_create ("mutter-screen-cast-memfd", ++ MFD_CLOEXEC | MFD_ALLOW_SEALING); ++ if (spa_data[0].fd == -1) ++ { ++ g_critical ("Can't create memfd: %m"); ++ return; ++ } ++ spa_data[0].mapoffset = 0; ++ spa_data[0].maxsize = stride * priv->video_format.size.height; ++ ++ if (ftruncate (spa_data[0].fd, spa_data[0].maxsize) < 0) ++ { ++ g_critical ("Can't truncate to %d: %m", spa_data[0].maxsize); ++ return; ++ } ++ ++ seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; ++ if (fcntl (spa_data[0].fd, F_ADD_SEALS, seals) == -1) ++ g_warning ("Failed to add seals: %m"); ++ ++ spa_data[0].data = mmap (NULL, ++ spa_data[0].maxsize, ++ PROT_READ | PROT_WRITE, ++ MAP_SHARED, ++ spa_data[0].fd, ++ spa_data[0].mapoffset); ++ if (spa_data[0].data == MAP_FAILED) ++ { ++ g_critical ("Failed to mmap memory: %m"); ++ return; ++ } ++ } ++} ++ ++static void ++on_stream_remove_buffer (void *data, ++ struct pw_buffer *buffer) ++{ ++ MetaScreenCastStreamSrc *src = data; ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ struct spa_buffer *spa_buffer = buffer->buffer; ++ struct spa_data *spa_data = spa_buffer->datas; ++ ++ if (spa_data[0].type == SPA_DATA_DmaBuf) ++ { ++ if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd))) ++ g_critical ("Failed to remove non-exported DMA buffer"); ++ } ++ else if (spa_data[0].type == SPA_DATA_MemFd) ++ { ++ munmap (spa_data[0].data, spa_data[0].maxsize); ++ close (spa_data[0].fd); ++ } ++} ++ + static const struct pw_stream_events stream_events = { + PW_VERSION_STREAM_EVENTS, + .state_changed = on_stream_state_changed, + .param_changed = on_stream_param_changed, ++ .add_buffer = on_stream_add_buffer, ++ .remove_buffer = on_stream_remove_buffer, + }; + + static struct pw_stream * +@@ -686,7 +814,7 @@ create_pipewire_stream (MetaScreenCastStreamSrc *src, + PW_DIRECTION_OUTPUT, + SPA_ID_INVALID, + (PW_STREAM_FLAG_DRIVER | +- PW_STREAM_FLAG_MAP_BUFFERS), ++ PW_STREAM_FLAG_ALLOC_BUFFERS), + params, G_N_ELEMENTS (params)); + if (result != 0) + { +@@ -854,6 +982,7 @@ meta_screen_cast_stream_src_finalize (GObject *object) + if (meta_screen_cast_stream_src_is_enabled (src)) + meta_screen_cast_stream_src_disable (src); + ++ g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); + g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); +@@ -905,6 +1034,12 @@ meta_screen_cast_stream_src_get_property (GObject *object, + static void + meta_screen_cast_stream_src_init (MetaScreenCastStreamSrc *src) + { ++ MetaScreenCastStreamSrcPrivate *priv = ++ meta_screen_cast_stream_src_get_instance_private (src); ++ ++ priv->dmabuf_handles = ++ g_hash_table_new_full (NULL, NULL, NULL, ++ (GDestroyNotify) cogl_dma_buf_handle_free); + } + + static void +diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h +index fc0e5bc77..3f6a1af2b 100644 +--- a/src/backends/meta-screen-cast-stream-src.h ++++ b/src/backends/meta-screen-cast-stream-src.h +@@ -55,6 +55,8 @@ struct _MetaScreenCastStreamSrcClass + void (* disable) (MetaScreenCastStreamSrc *src); + gboolean (* record_frame) (MetaScreenCastStreamSrc *src, + uint8_t *data); ++ gboolean (* blit_to_framebuffer) (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer); + gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, +-- +2.26.2 + + +From b25135cdeda1ee1aa26bb645ba981478c823d413 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 9 Dec 2019 10:11:51 -0300 +Subject: [PATCH 29/49] monitor-stream-src: Implement blitting view + framebuffers + +Add the vfunc override that actually consume the new Cogl API. Every +view that fits into the logical monitor is rendered. + +Fixes https://gitlab.gnome.org/GNOME/mutter/issues/639 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + .../meta-screen-cast-monitor-stream-src.c | 62 +++++++++++++++++++ + 1 file changed, 62 insertions(+) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index f582217e5..0c7d8ec17 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -367,6 +367,66 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, + return TRUE; + } + ++static gboolean ++meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) ++{ ++ MetaScreenCastMonitorStreamSrc *monitor_src = ++ META_SCREEN_CAST_MONITOR_STREAM_SRC (src); ++ MetaBackend *backend = get_backend (monitor_src); ++ MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ MetaMonitor *monitor; ++ MetaLogicalMonitor *logical_monitor; ++ MetaRectangle logical_monitor_layout; ++ GList *l; ++ float view_scale; ++ ++ monitor = get_monitor (monitor_src); ++ logical_monitor = meta_monitor_get_logical_monitor (monitor); ++ logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); ++ ++ if (meta_is_stage_views_scaled ()) ++ view_scale = meta_logical_monitor_get_scale (logical_monitor); ++ else ++ view_scale = 1.0; ++ ++ for (l = meta_renderer_get_views (renderer); l; l = l->next) ++ { ++ ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); ++ g_autoptr (GError) error = NULL; ++ CoglFramebuffer *view_framebuffer; ++ MetaRectangle view_layout; ++ int x, y; ++ ++ clutter_stage_view_get_layout (view, &view_layout); ++ ++ if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) ++ continue; ++ ++ view_framebuffer = clutter_stage_view_get_framebuffer (view); ++ ++ x = (int) roundf ((view_layout.x - logical_monitor_layout.x) * view_scale); ++ y = (int) roundf ((view_layout.y - logical_monitor_layout.y) * view_scale); ++ ++ if (!cogl_blit_framebuffer (view_framebuffer, ++ framebuffer, ++ 0, 0, ++ x, y, ++ cogl_framebuffer_get_width (view_framebuffer), ++ cogl_framebuffer_get_height (view_framebuffer), ++ &error)) ++ { ++ g_warning ("Error blitting view into DMABuf framebuffer: %s", ++ error->message); ++ return FALSE; ++ } ++ } ++ ++ cogl_framebuffer_flush (framebuffer); ++ ++ return TRUE; ++} ++ + static void + meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -492,6 +552,8 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl + src_class->enable = meta_screen_cast_monitor_stream_src_enable; + src_class->disable = meta_screen_cast_monitor_stream_src_disable; + src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame; ++ src_class->blit_to_framebuffer = ++ meta_screen_cast_monitor_stream_src_blit_to_framebuffer; + src_class->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; + } +-- +2.26.2 + + +From 7806ab62bbf51e2ca884b17a314c74b38f3ae9c5 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Fri, 28 Feb 2020 12:23:15 -0300 +Subject: [PATCH 30/49] window-stream-source: Draw into DMA buffer image + +Much like monitor streaming, implement window streaming by +making the window actor draw itself with a paint context +that used the passed framebuffer. + +Now that all MetaScreenCastStreamSrc subclasses implement +blit_to_framebuffer, remove the conditional check from +meta_screen_cast_stream_src_blit_to_framebuffer(). + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/meta-screen-cast-stream-src.c | 5 +- + .../meta-screen-cast-window-stream-src.c | 21 +++++++ + src/backends/meta-screen-cast-window.c | 11 ++++ + src/backends/meta-screen-cast-window.h | 8 +++ + src/compositor/meta-window-actor.c | 63 +++++++++++++++++++ + 5 files changed, 104 insertions(+), 4 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 0eb9f4d8c..6f7551ad4 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -149,10 +149,7 @@ meta_screen_cast_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + +- if (klass->blit_to_framebuffer) +- return klass->blit_to_framebuffer (src, framebuffer); +- +- return FALSE; ++ return klass->blit_to_framebuffer (src, framebuffer); + } + + static void +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 210ea0807..3f9cab694 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -412,6 +412,25 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, + return TRUE; + } + ++static gboolean ++meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src, ++ CoglFramebuffer *framebuffer) ++{ ++ MetaScreenCastWindowStreamSrc *window_src = ++ META_SCREEN_CAST_WINDOW_STREAM_SRC (src); ++ MetaRectangle stream_rect; ++ ++ stream_rect.x = 0; ++ stream_rect.y = 0; ++ stream_rect.width = get_stream_width (window_src); ++ stream_rect.height = get_stream_height (window_src); ++ ++ return ++ meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, ++ &stream_rect, ++ framebuffer); ++} ++ + static void + meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +@@ -496,6 +515,8 @@ meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClas + src_class->enable = meta_screen_cast_window_stream_src_enable; + src_class->disable = meta_screen_cast_window_stream_src_disable; + src_class->record_frame = meta_screen_cast_window_stream_src_record_frame; ++ src_class->blit_to_framebuffer = ++ meta_screen_cast_window_stream_src_blit_to_framebuffer; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; + src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; + } +diff --git a/src/backends/meta-screen-cast-window.c b/src/backends/meta-screen-cast-window.c +index 91515ded8..50b65a5df 100644 +--- a/src/backends/meta-screen-cast-window.c ++++ b/src/backends/meta-screen-cast-window.c +@@ -78,6 +78,17 @@ meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window, + data); + } + ++gboolean ++meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds, ++ CoglFramebuffer *framebuffer) ++{ ++ MetaScreenCastWindowInterface *iface = ++ META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window); ++ ++ return iface->blit_to_framebuffer (screen_cast_window, bounds, framebuffer); ++} ++ + gboolean + meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window) + { +diff --git a/src/backends/meta-screen-cast-window.h b/src/backends/meta-screen-cast-window.h +index 69e5a34dc..45b681292 100644 +--- a/src/backends/meta-screen-cast-window.h ++++ b/src/backends/meta-screen-cast-window.h +@@ -56,6 +56,10 @@ struct _MetaScreenCastWindowInterface + MetaRectangle *bounds, + uint8_t *data); + ++ gboolean (*blit_to_framebuffer) (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds, ++ CoglFramebuffer *framebuffer); ++ + gboolean (*has_damage) (MetaScreenCastWindow *screen_cast_window); + }; + +@@ -78,6 +82,10 @@ void meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_win + MetaRectangle *bounds, + uint8_t *data); + ++gboolean meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds, ++ CoglFramebuffer *framebuffer); ++ + gboolean meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window); + + G_END_DECLS +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 81eb04c84..a7dc25004 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2025,6 +2025,68 @@ meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, + cairo_surface_destroy (image); + } + ++static gboolean ++meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, ++ MetaRectangle *bounds, ++ CoglFramebuffer *framebuffer) ++{ ++ MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); ++ ClutterActor *actor = CLUTTER_ACTOR (window_actor); ++ MetaRectangle scaled_clip; ++ CoglColor clear_color; ++ float resource_scale; ++ float width, height; ++ float x, y; ++ ++ if (meta_window_actor_is_destroyed (window_actor)) ++ return FALSE; ++ ++ clutter_actor_get_size (actor, &width, &height); ++ ++ if (width == 0 || height == 0) ++ return FALSE; ++ ++ if (!clutter_actor_get_resource_scale (actor, &resource_scale)) ++ return FALSE; ++ ++ width = ceilf (width * resource_scale); ++ height = ceilf (height * resource_scale); ++ ++ cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); ++ clutter_actor_get_position (actor, &x, &y); ++ ++ cogl_framebuffer_push_matrix (framebuffer); ++ ++ cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); ++ cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); ++ cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); ++ cogl_framebuffer_translate (framebuffer, -x, -y, 0); ++ ++ meta_rectangle_scale_double (bounds, resource_scale, ++ META_ROUNDING_STRATEGY_GROW, ++ &scaled_clip); ++ meta_rectangle_intersect (&scaled_clip, ++ &(MetaRectangle) { ++ .width = width, ++ .height = height, ++ }, ++ &scaled_clip); ++ ++ cogl_framebuffer_push_rectangle_clip (framebuffer, ++ scaled_clip.x, scaled_clip.y, ++ scaled_clip.x + scaled_clip.width, ++ scaled_clip.y + scaled_clip.height); ++ ++ cogl_push_framebuffer (framebuffer); ++ clutter_actor_paint (actor); ++ cogl_pop_framebuffer (); ++ ++ cogl_framebuffer_pop_clip (framebuffer); ++ cogl_framebuffer_pop_matrix (framebuffer); ++ ++ return TRUE; ++} ++ + static gboolean + meta_window_actor_has_damage (MetaScreenCastWindow *screen_cast_window) + { +@@ -2038,6 +2100,7 @@ screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) + iface->transform_relative_position = meta_window_actor_transform_relative_position; + iface->transform_cursor_position = meta_window_actor_transform_cursor_position; + iface->capture_into = meta_window_actor_capture_into; ++ iface->blit_to_framebuffer = meta_window_actor_blit_to_framebuffer; + iface->has_damage = meta_window_actor_has_damage; + } + +-- +2.26.2 + + +From d005b89e4846aeaca01568865dd7d440f036c783 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 24 Feb 2020 14:14:07 -0300 +Subject: [PATCH 31/49] screen-cast-stream-src: Remove unused parameter + +The 'data' parameter is not used in maybe_record_cursor(), so remove +it. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/meta-screen-cast-stream-src.c | 5 ++--- + 1 file changed, 2 insertions(+), 3 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 6f7551ad4..9c3657c75 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -389,8 +389,7 @@ add_cursor_metadata (MetaScreenCastStreamSrc *src, + + static void + maybe_record_cursor (MetaScreenCastStreamSrc *src, +- struct spa_buffer *spa_buffer, +- uint8_t *data) ++ struct spa_buffer *spa_buffer) + { + MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); + +@@ -505,7 +504,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + spa_buffer->datas[0].chunk->size = 0; + } + +- maybe_record_cursor (src, spa_buffer, data); ++ maybe_record_cursor (src, spa_buffer); + + priv->last_frame_timestamp_us = now_us; + +-- +2.26.2 + + +From 0b8f27b4adf72ade1c8f5e958d9fcf31a9063d94 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Fri, 28 Feb 2020 15:47:23 -0300 +Subject: [PATCH 32/49] monitor-stream-src: Use cogl_framebuffer_finish() + +Even though cogl_framebuffer_flush() was supposed to be enough, +it ends up creating streams with odd visual glitches that look +very much like unfinished frames. + +Switch back to cogl_framebuffer_finish(), which is admittedly +an overkill, but it's what works for now. There is anedoctal +evidence showing it doesn't incur in worse performance. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1086 +--- + src/backends/meta-screen-cast-monitor-stream-src.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index 0c7d8ec17..af763dc09 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -422,7 +422,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + } + } + +- cogl_framebuffer_flush (framebuffer); ++ cogl_framebuffer_finish (framebuffer); + + return TRUE; + } +-- +2.26.2 + + +From 1b9704405568f1a52168ce8c5267fe9a5ab1f789 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 9 Mar 2020 17:43:54 +0100 +Subject: [PATCH 33/49] screen-cast-stream-src: Don't complain when we can't + dequeue buffer + +PipeWire will be unable to dequeue a buffer if all are already busy. +This can happen for valid reasons, e.g. the stream consumer not being +fast enough, so don't complain in the journal if it happens. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1115 +--- + src/backends/meta-screen-cast-stream-src.c | 5 +---- + 1 file changed, 1 insertion(+), 4 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 9c3657c75..170f34043 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -456,10 +456,7 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + + buffer = pw_stream_dequeue_buffer (priv->pipewire_stream); + if (!buffer) +- { +- g_warning ("Failed to dequeue at PipeWire buffer"); +- return; +- } ++ return; + + spa_buffer = buffer->buffer; + data = spa_buffer->datas[0].data; +-- +2.26.2 + + +From 99a671e71cedcb3ef3093d0afcc823bfdd8eaf79 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Thu, 5 Mar 2020 23:29:26 +0100 +Subject: [PATCH 34/49] screen-cast-stream-src: Don't leak GSource + +For every stream src, we created and attached a GSource. Upon stream +src destruction, we g_source_destroy():ed the GSource. What +g_source_destroy() does, hawever, is not really "destroy" it but only +detaches it from the main context removing the reference the context had +added for it via g_source_attach(). This caused the GSource to leak, +although in a detached state, as the reference taken on creation was +still held. + +Fix this by also removing our own reference to it when finalizing. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1106 +--- + src/backends/meta-screen-cast-stream-src.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 170f34043..4f3d821ef 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -885,7 +885,7 @@ create_pipewire_source (void) + pipewire_source->pipewire_loop = pw_loop_new (NULL); + if (!pipewire_source->pipewire_loop) + { +- g_source_destroy ((GSource *) pipewire_source); ++ g_source_unref ((GSource *) pipewire_source); + return NULL; + } + +@@ -980,6 +980,7 @@ meta_screen_cast_stream_src_finalize (GObject *object) + g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); + g_clear_pointer (&priv->pipewire_context, pw_context_destroy); + g_source_destroy (&priv->pipewire_source->base); ++ g_source_unref (&priv->pipewire_source->base); + + G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object); + } +-- +2.26.2 + + +From 6af35cad8bc09c291c29fffb08fe8ba3b76b779e Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 16 Mar 2020 19:47:30 -0300 +Subject: [PATCH 35/49] window-actor: Shuffle some lines around + +Move the CoglColor assignment right above the cogl_framebuffer_clear() call, +and let these wonderful partners together to delight us with an easier to +read code. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + src/compositor/meta-window-actor.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index a7dc25004..f90d2b0af 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2052,11 +2052,11 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + +- cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + clutter_actor_get_position (actor, &x, &y); + + cogl_framebuffer_push_matrix (framebuffer); + ++ cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); + cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); +-- +2.26.2 + + +From 4ecb19fffd37786d6b8efc3b05e7239bee49e763 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 16 Mar 2020 19:55:16 -0300 +Subject: [PATCH 36/49] window-actor: Clip before translate when blitting + +cogl_framebuffer_push_rectangle_clip() acts on the current modelview +matrix. That means the result of clipping then translating will be +different of the result of translating then clipping. + +What we want for window screencasting is the former, not the latter. +Move the translation code (and associated) to after clipping. + +Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + src/compositor/meta-window-actor.c | 10 +++++----- + 1 file changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index f90d2b0af..c9ef5846a 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2054,13 +2054,9 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + + clutter_actor_get_position (actor, &x, &y); + +- cogl_framebuffer_push_matrix (framebuffer); +- + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); +- cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); +- cogl_framebuffer_translate (framebuffer, -x, -y, 0); + + meta_rectangle_scale_double (bounds, resource_scale, + META_ROUNDING_STRATEGY_GROW, +@@ -2077,12 +2073,16 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + scaled_clip.x + scaled_clip.width, + scaled_clip.y + scaled_clip.height); + ++ cogl_framebuffer_push_matrix (framebuffer); ++ cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); ++ cogl_framebuffer_translate (framebuffer, -x, -y, 0); ++ + cogl_push_framebuffer (framebuffer); + clutter_actor_paint (actor); + cogl_pop_framebuffer (); + +- cogl_framebuffer_pop_clip (framebuffer); + cogl_framebuffer_pop_matrix (framebuffer); ++ cogl_framebuffer_pop_clip (framebuffer); + + return TRUE; + } +-- +2.26.2 + + +From 14f76043aa912b186ecbce0e5dae46f521fec10f Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Tue, 17 Mar 2020 16:55:39 -0300 +Subject: [PATCH 37/49] window-stream-src: Ensure initial frame is recorded + +MetaScreenCastWindowStreamSrc connects to the "damaged" signal of +MetaWindowActor. This signal is not exactly tied to the paint cycle +of the stage, and a damage may take quite a while to arrive when +a client doesn't want to draw anything. For that reason, the window +screencast can start empty, waiting for a damage to arrive. + +Ensure at least one frame is recorded when enabling the window stream. + +Fixes: https://gitlab.gnome.org/GNOME/mutter/issues/1097 + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + src/backends/meta-screen-cast-window-stream-src.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 3f9cab694..0e0907e82 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -389,6 +389,8 @@ meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + break; + } ++ ++ meta_screen_cast_stream_src_maybe_record_frame (src); + } + + static void +-- +2.26.2 + + +From 4c4324e21cc3739c6d0a384519f8b8a47d9d8216 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Tue, 17 Mar 2020 18:22:29 -0300 +Subject: [PATCH 38/49] window-stream-src: Implement cursor blitting + +A regression compared to the old code, we're not drawing the cursor +when on EMBEDDED mode. + +Blit the cursor to the screencast framebuffer when on EMBEDDED mode. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + .../meta-screen-cast-window-stream-src.c | 81 ++++++++++++++++++- + 1 file changed, 77 insertions(+), 4 deletions(-) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 0e0907e82..87505b793 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -178,6 +178,65 @@ maybe_draw_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, + cairo_surface_destroy (cursor_surface); + } + ++static void ++maybe_blit_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, ++ CoglFramebuffer *framebuffer, ++ MetaRectangle *stream_rect) ++{ ++ MetaBackend *backend = get_backend (window_src); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_get_default_backend ()); ++ MetaCursorRenderer *cursor_renderer = ++ meta_backend_get_cursor_renderer (backend); ++ MetaScreenCastWindow *screen_cast_window; ++ MetaCursorSprite *cursor_sprite; ++ ClutterPoint relative_cursor_position; ++ ClutterPoint cursor_position; ++ CoglTexture *cursor_texture; ++ CoglPipeline *pipeline; ++ int width, height; ++ float scale; ++ int hotspot_x, hotspot_y; ++ float x, y; ++ ++ cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); ++ if (!cursor_sprite) ++ return; ++ ++ cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); ++ if (!cursor_texture) ++ return; ++ ++ screen_cast_window = window_src->screen_cast_window; ++ cursor_position = meta_cursor_renderer_get_position (cursor_renderer); ++ if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window, ++ cursor_sprite, ++ &cursor_position, ++ &scale, ++ &relative_cursor_position)) ++ return; ++ ++ meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); ++ ++ x = (relative_cursor_position.x - hotspot_x) * scale; ++ y = (relative_cursor_position.y - hotspot_y) * scale; ++ width = cogl_texture_get_width (cursor_texture); ++ height = cogl_texture_get_height (cursor_texture); ++ ++ pipeline = cogl_pipeline_new (cogl_context); ++ cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture); ++ cogl_pipeline_set_layer_filters (pipeline, 0, ++ COGL_PIPELINE_FILTER_LINEAR, ++ COGL_PIPELINE_FILTER_LINEAR); ++ ++ cogl_framebuffer_draw_rectangle (framebuffer, ++ pipeline, ++ x, y, ++ x + width, y + height); ++ ++ cogl_object_unref (pipeline); ++} ++ + static gboolean + capture_into (MetaScreenCastWindowStreamSrc *window_src, + uint8_t *data) +@@ -420,6 +479,7 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + { + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); ++ MetaScreenCastStream *stream; + MetaRectangle stream_rect; + + stream_rect.x = 0; +@@ -427,10 +487,23 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + stream_rect.width = get_stream_width (window_src); + stream_rect.height = get_stream_height (window_src); + +- return +- meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, +- &stream_rect, +- framebuffer); ++ if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, ++ &stream_rect, ++ framebuffer)) ++ return FALSE; ++ ++ stream = meta_screen_cast_stream_src_get_stream (src); ++ switch (meta_screen_cast_stream_get_cursor_mode (stream)) ++ { ++ case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: ++ maybe_blit_cursor_sprite (window_src, framebuffer, &stream_rect); ++ break; ++ case META_SCREEN_CAST_CURSOR_MODE_METADATA: ++ case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: ++ break; ++ } ++ ++ return TRUE; + } + + static void +-- +2.26.2 + + +From f454e95162dd44feb1581b52521c637b378aa3bd Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Mon, 16 Mar 2020 19:31:25 -0300 +Subject: [PATCH 39/49] window-stream-src: Finish framebuffer after blitting + +Just like what's done for monitor screencasting. Unfortunately, there's +no mechanism to share fences with PipeWire clients yet, which forces +us to guarantee that a frame is completed after blitting. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + src/backends/meta-screen-cast-window-stream-src.c | 2 ++ + 1 file changed, 2 insertions(+) + +diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c +index 87505b793..c252b4356 100644 +--- a/src/backends/meta-screen-cast-window-stream-src.c ++++ b/src/backends/meta-screen-cast-window-stream-src.c +@@ -503,6 +503,8 @@ meta_screen_cast_window_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + break; + } + ++ cogl_framebuffer_finish (framebuffer); ++ + return TRUE; + } + +-- +2.26.2 + + +From 3658b9f61921b6568913966be5bd28ceb111c23b Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 18 Mar 2020 21:12:26 -0300 +Subject: [PATCH 40/49] clutter/actor: Add culling inhibiting API + +This will allow us to continue painting actors that are +outside the visible boundaries of the stage view. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + clutter/clutter/clutter-actor.c | 64 ++++++++++++++++++++++++++++++++- + clutter/clutter/clutter-actor.h | 5 +++ + 2 files changed, 68 insertions(+), 1 deletion(-) + +diff --git a/clutter/clutter/clutter-actor.c b/clutter/clutter/clutter-actor.c +index 803f76aae..77a61ae1a 100644 +--- a/clutter/clutter/clutter-actor.c ++++ b/clutter/clutter/clutter-actor.c +@@ -703,6 +703,7 @@ struct _ClutterActorPrivate + + guint8 opacity; + gint opacity_override; ++ unsigned int inhibit_culling_counter; + + ClutterOffscreenRedirect offscreen_redirect; + +@@ -3824,6 +3825,7 @@ clutter_actor_paint (ClutterActor *self) + { + ClutterActorPrivate *priv; + ClutterPickMode pick_mode; ++ gboolean culling_inhibited; + gboolean clip_set = FALSE; + ClutterStage *stage; + +@@ -3973,7 +3975,10 @@ clutter_actor_paint (ClutterActor *self) + * paint then the last-paint-volume would likely represent the new + * actor position not the old. + */ +- if (!in_clone_paint () && pick_mode == CLUTTER_PICK_NONE) ++ culling_inhibited = priv->inhibit_culling_counter > 0; ++ if (!culling_inhibited && ++ !in_clone_paint () && ++ pick_mode == CLUTTER_PICK_NONE) + { + gboolean success; + /* annoyingly gcc warns if uninitialized even though +@@ -16014,6 +16019,63 @@ clutter_actor_get_opacity_override (ClutterActor *self) + return self->priv->opacity_override; + } + ++/** ++ * clutter_actor_inhibit_culling: ++ * @actor: a #ClutterActor ++ * ++ * Increases the culling inhibitor counter. Inhibiting culling ++ * forces the actor to be painted even when outside the visible ++ * bounds of the stage view. ++ * ++ * This is usually necessary when an actor is being painted on ++ * another paint context. ++ * ++ * Pair with clutter_actor_uninhibit_culling() when the actor doesn't ++ * need to be painted anymore. ++ */ ++void ++clutter_actor_inhibit_culling (ClutterActor *actor) ++{ ++ ClutterActorPrivate *priv; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (actor)); ++ ++ priv = actor->priv; ++ ++ priv->inhibit_culling_counter++; ++ _clutter_actor_set_enable_paint_unmapped (actor, TRUE); ++} ++ ++/** ++ * clutter_actor_uninhibit_culling: ++ * @actor: a #ClutterActor ++ * ++ * Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling() ++ * for when inhibit culling is necessary. ++ * ++ * Calling this function without a matching call to ++ * clutter_actor_inhibit_culling() is a programming error. ++ */ ++void ++clutter_actor_uninhibit_culling (ClutterActor *actor) ++{ ++ ClutterActorPrivate *priv; ++ ++ g_return_if_fail (CLUTTER_IS_ACTOR (actor)); ++ ++ priv = actor->priv; ++ ++ if (priv->inhibit_culling_counter == 0) ++ { ++ g_critical ("Unpaired call to clutter_actor_uninhibit_culling"); ++ return; ++ } ++ ++ priv->inhibit_culling_counter--; ++ if (priv->inhibit_culling_counter == 0) ++ _clutter_actor_set_enable_paint_unmapped (actor, FALSE); ++} ++ + /* Allows you to disable applying the actors model view transform during + * a paint. Used by ClutterClone. */ + void +diff --git a/clutter/clutter/clutter-actor.h b/clutter/clutter/clutter-actor.h +index 7d2168af1..f6e0acbb2 100644 +--- a/clutter/clutter/clutter-actor.h ++++ b/clutter/clutter/clutter-actor.h +@@ -870,6 +870,11 @@ void clutter_actor_set_opacity_override + CLUTTER_EXPORT + gint clutter_actor_get_opacity_override (ClutterActor *self); + ++CLUTTER_EXPORT ++void clutter_actor_inhibit_culling (ClutterActor *actor); ++CLUTTER_EXPORT ++void clutter_actor_uninhibit_culling (ClutterActor *actor); ++ + /** + * ClutterActorCreateChildFunc: + * @item: (type GObject): the item in the model +-- +2.26.2 + + +From 471c6d372bd9e1a79cf10e46fb8cbc4c110d1da6 Mon Sep 17 00:00:00 2001 +From: Georges Basile Stavracas Neto +Date: Wed, 18 Mar 2020 21:14:58 -0300 +Subject: [PATCH 41/49] window-actor: Inhibit culling when blitting to + screencast + +This allows us to screencast any window continuously, even +without it being visible. Because it's still being painted, +clients continue to receive frame callbacks, and people +are happy again. + +https://gitlab.gnome.org/GNOME/mutter/merge_requests/1129 +--- + src/compositor/meta-window-actor.c | 21 +++++++++++++++------ + 1 file changed, 15 insertions(+), 6 deletions(-) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index c9ef5846a..08d1b0a87 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2049,6 +2049,8 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) + return FALSE; + ++ clutter_actor_inhibit_culling (actor); ++ + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + +@@ -2084,6 +2086,8 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + cogl_framebuffer_pop_matrix (framebuffer); + cogl_framebuffer_pop_clip (framebuffer); + ++ clutter_actor_uninhibit_culling (actor); ++ + return TRUE; + } + +@@ -2151,33 +2155,36 @@ meta_window_actor_get_image (MetaWindowActor *self, + CoglColor clear_color; + float x, y; + MetaRectangle scaled_clip; +- cairo_surface_t *surface; ++ cairo_surface_t *surface = NULL; + + if (!priv->surface) + return NULL; + ++ clutter_actor_inhibit_culling (actor); ++ + if (clutter_actor_get_n_children (actor) == 1) + { + MetaShapedTexture *stex; + + stex = meta_surface_actor_get_texture (priv->surface); +- return meta_shaped_texture_get_image (stex, clip); ++ surface = meta_shaped_texture_get_image (stex, clip); ++ goto out; + } + + clutter_actor_get_size (actor, &width, &height); + + if (width == 0 || height == 0) +- return NULL; ++ goto out; + + if (!clutter_actor_get_resource_scale (actor, &resource_scale)) +- return NULL; ++ goto out; + + width = ceilf (width * resource_scale); + height = ceilf (height * resource_scale); + + texture = cogl_texture_2d_new_with_size (cogl_context, width, height); + if (!texture) +- return NULL; ++ goto out; + + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), + FALSE); +@@ -2193,7 +2200,7 @@ meta_window_actor_get_image (MetaWindowActor *self, + error->message); + cogl_object_unref (framebuffer); + cogl_object_unref (texture); +- return NULL; ++ goto out; + } + + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); +@@ -2242,5 +2249,7 @@ meta_window_actor_get_image (MetaWindowActor *self, + + cairo_surface_mark_dirty (surface); + ++out: ++ clutter_actor_uninhibit_culling (actor); + return surface; + } +-- +2.26.2 + + +From f29763e93ddf7933bd5ea3d2cd5769683b9e312b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 18 May 2020 17:52:09 +0200 +Subject: [PATCH 42/49] screen-cast/monitor-src: Get views from the stage + +Otherwise we don't get the views when in the X11 session. +--- + clutter/clutter/clutter-mutter.h | 3 +++ + clutter/clutter/clutter-stage-private.h | 2 -- + src/backends/meta-screen-cast-monitor-stream-src.c | 4 ++-- + 3 files changed, 5 insertions(+), 4 deletions(-) + +diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h +index a53080457..6137605d4 100644 +--- a/clutter/clutter/clutter-mutter.h ++++ b/clutter/clutter/clutter-mutter.h +@@ -55,6 +55,9 @@ void clutter_stage_update_resource_scales (ClutterStage *stage); + CLUTTER_EXPORT + gboolean clutter_actor_has_damage (ClutterActor *actor); + ++CLUTTER_EXPORT ++GList *_clutter_stage_peek_stage_views (ClutterStage *stage); ++ + #undef __CLUTTER_H_INSIDE__ + + #endif /* __CLUTTER_MUTTER_H__ */ +diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h +index df0bf642b..42474687a 100644 +--- a/clutter/clutter/clutter-stage-private.h ++++ b/clutter/clutter/clutter-stage-private.h +@@ -133,8 +133,6 @@ void _clutter_stage_presented (ClutterStage *stag + CoglFrameEvent frame_event, + ClutterFrameInfo *frame_info); + +-GList * _clutter_stage_peek_stage_views (ClutterStage *stage); +- + G_END_DECLS + + #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ +diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c +index af763dc09..655b68261 100644 +--- a/src/backends/meta-screen-cast-monitor-stream-src.c ++++ b/src/backends/meta-screen-cast-monitor-stream-src.c +@@ -374,7 +374,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); + MetaBackend *backend = get_backend (monitor_src); +- MetaRenderer *renderer = meta_backend_get_renderer (backend); ++ ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); + MetaMonitor *monitor; + MetaLogicalMonitor *logical_monitor; + MetaRectangle logical_monitor_layout; +@@ -390,7 +390,7 @@ meta_screen_cast_monitor_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc + else + view_scale = 1.0; + +- for (l = meta_renderer_get_views (renderer); l; l = l->next) ++ for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) + { + ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); + g_autoptr (GError) error = NULL; +-- +2.26.2 + + +From d6d401f74452e7eb2f2ac0272b30603782ea95c8 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 12 May 2020 08:52:01 +0200 +Subject: [PATCH 43/49] screen-cast-stream-src: Don't throttle if max framerate + is 1/0 + +The max framerate 1/0 means variable without any particular max, so +don't throttle if that was set. + +Not doing this would end up with a floating point exception. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 +--- + src/backends/meta-screen-cast-stream-src.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index 4f3d821ef..ad0d9ed79 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -445,7 +445,8 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) + uint64_t now_us; + + now_us = g_get_monotonic_time (); +- if (priv->last_frame_timestamp_us != 0 && ++ if (priv->video_format.max_framerate.num > 0 && ++ priv->last_frame_timestamp_us != 0 && + (now_us - priv->last_frame_timestamp_us < + ((1000000 * priv->video_format.max_framerate.denom) / + priv->video_format.max_framerate.num))) +-- +2.26.2 + + +From 0e00b9b3516362009c671cfd62dd32dc9691e647 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 12 May 2020 16:14:00 +0200 +Subject: [PATCH 44/49] screen-cast-src: Notify that about the stream being + closed on idle + +We're iterating inside the PipeWire loop when detecting PipeWire errors, +and shouldn't destroy the PipeWire objects mid-iteration. Avoid this by +first disabling the stream src (effectively stopping the recording), +then notifying about it being closed in an idle callback. The +notification eventually makes the rest of the screen cast code clean up +the objects, including the src and the associated PipeWire objects, but +will do so outside the PipeWire loop iteration. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 +--- + src/backends/meta-screen-cast-stream-src.c | 20 ++++++++++++++++---- + 1 file changed, 16 insertions(+), 4 deletions(-) + +diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c +index ad0d9ed79..0500bfec5 100644 +--- a/src/backends/meta-screen-cast-stream-src.c ++++ b/src/backends/meta-screen-cast-stream-src.c +@@ -540,10 +540,16 @@ meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) + priv->is_enabled = FALSE; + } + +-static void +-meta_screen_cast_stream_src_notify_closed (MetaScreenCastStreamSrc *src) ++static gboolean ++notify_stream_src_closed_idle (gpointer user_data) + { ++ MetaScreenCastStreamSrc *src = user_data; ++ + g_signal_emit (src, signals[CLOSED], 0); ++ ++ g_object_unref (src); ++ ++ return G_SOURCE_REMOVE; + } + + static void +@@ -560,7 +566,9 @@ on_stream_state_changed (void *data, + { + case PW_STREAM_STATE_ERROR: + g_warning ("pipewire stream error: %s", error_message); +- meta_screen_cast_stream_src_notify_closed (src); ++ if (meta_screen_cast_stream_src_is_enabled (src)) ++ meta_screen_cast_stream_src_disable (src); ++ g_idle_add (notify_stream_src_closed_idle, g_object_ref (src)); + break; + case PW_STREAM_STATE_PAUSED: + if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) +@@ -832,7 +840,11 @@ on_core_error (void *data, + g_warning ("pipewire remote error: id:%u %s", id, message); + + if (id == PW_ID_CORE && res == -EPIPE) +- meta_screen_cast_stream_src_notify_closed (src); ++ { ++ if (meta_screen_cast_stream_src_is_enabled (src)) ++ meta_screen_cast_stream_src_disable (src); ++ g_idle_add (notify_stream_src_closed_idle, g_object_ref (src)); ++ } + } + + static gboolean +-- +2.26.2 + + +From 788a6f4adea0f976598014744149fb78feac145d Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Mon, 18 May 2020 19:10:49 +0200 +Subject: [PATCH 45/49] compositor: Only check for stereo when using GLX + +If EGL Xlib is used, we'll get bogus return value and crash. +--- + src/compositor/compositor.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/src/compositor/compositor.c b/src/compositor/compositor.c +index 6c08c8fe4..a6ae55abb 100644 +--- a/src/compositor/compositor.c ++++ b/src/compositor/compositor.c +@@ -534,9 +534,17 @@ typedef struct { + static gboolean + display_has_stereo_tree_ext (MetaX11Display *x11_display) + { ++ MetaBackend *backend = meta_get_backend (); ++ ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ++ CoglContext *cogl_context = ++ clutter_backend_get_cogl_context (clutter_backend); ++ CoglRenderer *cogl_renderer = cogl_context_get_renderer (cogl_context); + Display *xdisplay = x11_display->xdisplay; + const char *extensions_string; + ++ if (cogl_renderer_get_winsys_id (cogl_renderer) != COGL_WINSYS_ID_GLX) ++ return FALSE; ++ + static const char * (*query_extensions_string) (Display *display, + int screen); + +-- +2.26.2 + + +From 1aa9d081b69dc8ce7948a2189bd6ca0dd4c9611b Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Fri, 3 Apr 2020 17:12:58 +0200 +Subject: [PATCH 46/49] window-actor: Set viewport when blitting to screencast + fb + +This fixes an issue where a non-maximized screen casted window would be +stretched to fill the whole screen cast stream, instead of just the crop +that corresponds to the current window size. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1174 +--- + src/compositor/meta-window-actor.c | 1 + + 1 file changed, 1 insertion(+) + +diff --git a/src/compositor/meta-window-actor.c b/src/compositor/meta-window-actor.c +index 08d1b0a87..1167acfa5 100644 +--- a/src/compositor/meta-window-actor.c ++++ b/src/compositor/meta-window-actor.c +@@ -2059,6 +2059,7 @@ meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, + cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); ++ cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); + + meta_rectangle_scale_double (bounds, resource_scale, + META_ROUNDING_STRATEGY_GROW, +-- +2.26.2 + + +From 289cad91ac820b7280485603865d3060b5f84cf8 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 16 May 2020 10:44:04 +0200 +Subject: [PATCH 47/49] backends: Ensure remote desktop dbus interface state + +Ensure that it does receive Start and Stop orderly, and error out +otherwise. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258 +--- + src/backends/meta-remote-desktop-session.c | 22 +++++++++++++++++++++- + 1 file changed, 21 insertions(+), 1 deletion(-) + +diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c +index f069cba2f..8d53dc41c 100644 +--- a/src/backends/meta-remote-desktop-session.c ++++ b/src/backends/meta-remote-desktop-session.c +@@ -56,6 +56,7 @@ struct _MetaRemoteDesktopSession + + MetaScreenCastSession *screen_cast_session; + gulong screen_cast_session_closed_handler_id; ++ guint started : 1; + + ClutterVirtualInputDevice *virtual_pointer; + ClutterVirtualInputDevice *virtual_keyboard; +@@ -120,7 +121,7 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, + ClutterDeviceManager *device_manager = + clutter_device_manager_get_default (); + +- g_assert (!session->virtual_pointer && !session->virtual_keyboard); ++ g_assert (!session->started); + + if (session->screen_cast_session) + { +@@ -140,6 +141,7 @@ meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, + CLUTTER_TOUCHSCREEN_DEVICE); + + init_remote_access_handle (session); ++ session->started = TRUE; + + return TRUE; + } +@@ -150,6 +152,8 @@ meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) + MetaDBusRemoteDesktopSession *skeleton = + META_DBUS_REMOTE_DESKTOP_SESSION (session); + ++ session->started = FALSE; ++ + if (session->screen_cast_session) + { + g_signal_handler_disconnect (session->screen_cast_session, +@@ -261,6 +265,14 @@ handle_start (MetaDBusRemoteDesktopSession *skeleton, + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + GError *error = NULL; + ++ if (session->started) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "Already started"); ++ return TRUE; ++ } ++ + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +@@ -293,6 +305,14 @@ handle_stop (MetaDBusRemoteDesktopSession *skeleton, + { + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + ++ if (!session->started) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "Session not started"); ++ return TRUE; ++ } ++ + if (!check_permission (session, invocation)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +-- +2.26.2 + + +From ccbfc529a11e52a27e8d6751753e82e55319e3c8 Mon Sep 17 00:00:00 2001 +From: Carlos Garnacho +Date: Sat, 16 May 2020 10:46:57 +0200 +Subject: [PATCH 48/49] backends: Make uniform checks on remote desktop input + dbus methods + +They all checked that the remote session service talked with the +correct peer, and some of them did check that there is an associated +screencast session. + +Add a new check for the session being started (as it's state is +decoupled with screencast session availability) and move all checks +to a function that is called from all input-oriented DBus methods. + +Fixes: https://gitlab.gnome.org/GNOME/mutter/-/issues/1254 + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258 +--- + src/backends/meta-remote-desktop-session.c | 145 ++++++++------------- + 1 file changed, 51 insertions(+), 94 deletions(-) + +diff --git a/src/backends/meta-remote-desktop-session.c b/src/backends/meta-remote-desktop-session.c +index 8d53dc41c..59057fc70 100644 +--- a/src/backends/meta-remote-desktop-session.c ++++ b/src/backends/meta-remote-desktop-session.c +@@ -258,6 +258,37 @@ check_permission (MetaRemoteDesktopSession *session, + g_dbus_method_invocation_get_sender (invocation)) == 0; + } + ++static gboolean ++meta_remote_desktop_session_check_can_notify (MetaRemoteDesktopSession *session, ++ GDBusMethodInvocation *invocation) ++{ ++ if (!session->started) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "Session not started"); ++ return FALSE; ++ } ++ ++ if (!check_permission (session, invocation)) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_ACCESS_DENIED, ++ "Permission denied"); ++ return FALSE; ++ } ++ ++ if (!session->screen_cast_session) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_FAILED, ++ "No screen cast active"); ++ return FALSE; ++ } ++ ++ return TRUE; ++} ++ + static gboolean + handle_start (MetaDBusRemoteDesktopSession *skeleton, + GDBusMethodInvocation *invocation) +@@ -337,13 +368,8 @@ handle_notify_keyboard_keycode (MetaDBusRemoteDesktopSession *skeleton, + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + ClutterKeyState state; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + if (pressed) + state = CLUTTER_KEY_STATE_PRESSED; +@@ -369,13 +395,8 @@ handle_notify_keyboard_keysym (MetaDBusRemoteDesktopSession *skeleton, + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + ClutterKeyState state; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + if (pressed) + state = CLUTTER_KEY_STATE_PRESSED; +@@ -423,13 +444,8 @@ handle_notify_pointer_button (MetaDBusRemoteDesktopSession *skeleton, + uint32_t button; + ClutterButtonState state; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + button = translate_to_clutter_button (button_code); + +@@ -459,13 +475,8 @@ handle_notify_pointer_axis (MetaDBusRemoteDesktopSession *skeleton, + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + if (flags & META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_FINISH) + { +@@ -512,13 +523,8 @@ handle_notify_pointer_axis_discrete (MetaDBusRemoteDesktopSession *skeleton, + ClutterScrollDirection direction; + int step_count; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + if (axis > 1) + { +@@ -563,13 +569,8 @@ handle_notify_pointer_motion_relative (MetaDBusRemoteDesktopSession *skeleton, + { + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + clutter_virtual_input_device_notify_relative_motion (session->virtual_pointer, + CLUTTER_CURRENT_TIME, +@@ -592,21 +593,8 @@ handle_notify_pointer_motion_absolute (MetaDBusRemoteDesktopSession *skeleton, + MetaScreenCastStream *stream; + double abs_x, abs_y; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } +- +- if (!session->screen_cast_session) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_FAILED, +- "No screen cast active"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + stream = meta_screen_cast_session_get_stream (session->screen_cast_session, + stream_path); +@@ -642,21 +630,8 @@ handle_notify_touch_down (MetaDBusRemoteDesktopSession *skeleton, + MetaScreenCastStream *stream; + double abs_x, abs_y; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } +- +- if (!session->screen_cast_session) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_FAILED, +- "No screen cast active"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + stream = meta_screen_cast_session_get_stream (session->screen_cast_session, + stream_path); +@@ -693,21 +668,8 @@ handle_notify_touch_motion (MetaDBusRemoteDesktopSession *skeleton, + MetaScreenCastStream *stream; + double abs_x, abs_y; + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } +- +- if (!session->screen_cast_session) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_FAILED, +- "No screen cast active"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + stream = meta_screen_cast_session_get_stream (session->screen_cast_session, + stream_path); +@@ -739,13 +701,8 @@ handle_notify_touch_up (MetaDBusRemoteDesktopSession *skeleton, + { + MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); + +- if (!check_permission (session, invocation)) +- { +- g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, +- G_DBUS_ERROR_ACCESS_DENIED, +- "Permission denied"); +- return TRUE; +- } ++ if (!meta_remote_desktop_session_check_can_notify (session, invocation)) ++ return TRUE; + + clutter_virtual_input_device_notify_touch_up (session->virtual_touchscreen, + CLUTTER_CURRENT_TIME, +-- +2.26.2 + + +From 519c86b833f57286e51f2a1514003e9e3461bd7e Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jonas=20=C3=85dahl?= +Date: Tue, 21 Apr 2020 15:44:32 +0200 +Subject: [PATCH 49/49] remote-access-controller: Allow inhibiting remote + access + +Inhibiting remote access means any current remote access session is +terminated, and no new ones can be created, until remote access is +uninhibited. The inhibitation is ref counted, meaning there can be more +than one inhibitor. + +https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1212 +(cherry picked from commit 4300f1f91d726051893146d7b294d8852782f137) +--- + src/backends/meta-backend-types.h | 1 + + src/backends/meta-backend.c | 4 +- + .../meta-remote-access-controller-private.h | 4 ++ + src/backends/meta-remote-access-controller.c | 49 +++++++++++++++++++ + src/backends/meta-remote-desktop.c | 39 +++++++++++++++ + src/backends/meta-remote-desktop.h | 4 ++ + src/backends/meta-screen-cast.c | 34 +++++++++++++ + src/backends/meta-screen-cast.h | 4 ++ + src/meta/meta-remote-access-controller.h | 6 +++ + 9 files changed, 143 insertions(+), 2 deletions(-) + +diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h +index eb982d73e..98cac8b9e 100644 +--- a/src/backends/meta-backend-types.h ++++ b/src/backends/meta-backend-types.h +@@ -49,6 +49,7 @@ typedef struct _MetaTileInfo MetaTileInfo; + typedef struct _MetaRenderer MetaRenderer; + typedef struct _MetaRendererView MetaRendererView; + ++typedef struct _MetaRemoteDesktop MetaRemoteDesktop; + typedef struct _MetaScreenCast MetaScreenCast; + typedef struct _MetaScreenCastSession MetaScreenCastSession; + typedef struct _MetaScreenCastStream MetaScreenCastStream; +diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c +index 72cfbdaf3..750a9248a 100644 +--- a/src/backends/meta-backend.c ++++ b/src/backends/meta-backend.c +@@ -501,12 +501,12 @@ meta_backend_real_post_init (MetaBackend *backend) + priv->input_settings = meta_backend_create_input_settings (backend); + + #ifdef HAVE_REMOTE_DESKTOP +- priv->remote_access_controller = +- g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL); + priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); + priv->screen_cast = meta_screen_cast_new (backend, + priv->dbus_session_watcher); + priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); ++ priv->remote_access_controller = ++ meta_remote_access_controller_new (priv->remote_desktop, priv->screen_cast); + #endif /* HAVE_REMOTE_DESKTOP */ + + if (!meta_monitor_manager_is_headless (priv->monitor_manager)) +diff --git a/src/backends/meta-remote-access-controller-private.h b/src/backends/meta-remote-access-controller-private.h +index fce2082bf..444b71a77 100644 +--- a/src/backends/meta-remote-access-controller-private.h ++++ b/src/backends/meta-remote-access-controller-private.h +@@ -21,8 +21,12 @@ + #ifndef META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H + #define META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H + ++#include "backends/meta-backend-types.h" + #include "meta/meta-remote-access-controller.h" + ++MetaRemoteAccessController * meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, ++ MetaScreenCast *screen_cast); ++ + void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller, + MetaRemoteAccessHandle *handle); + +diff --git a/src/backends/meta-remote-access-controller.c b/src/backends/meta-remote-access-controller.c +index 0e0ebe2bd..e5ae0b5bd 100644 +--- a/src/backends/meta-remote-access-controller.c ++++ b/src/backends/meta-remote-access-controller.c +@@ -22,6 +22,9 @@ + + #include "backends/meta-remote-access-controller-private.h" + ++#include "backends/meta-remote-desktop.h" ++#include "backends/meta-screen-cast.h" ++ + enum + { + HANDLE_STOPPED, +@@ -52,6 +55,9 @@ G_DEFINE_TYPE_WITH_PRIVATE (MetaRemoteAccessHandle, + struct _MetaRemoteAccessController + { + GObject parent; ++ ++ MetaRemoteDesktop *remote_desktop; ++ MetaScreenCast *screen_cast; + }; + + G_DEFINE_TYPE (MetaRemoteAccessController, +@@ -94,6 +100,49 @@ meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *con + handle); + } + ++/** ++ * meta_remote_access_controller_inhibit_remote_access: ++ * @controller: a #MetaRemoteAccessController ++ * ++ * Inhibits remote access sessions from being created and running. Any active ++ * remote access session will be terminated. ++ */ ++void ++meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller) ++{ ++ meta_remote_desktop_inhibit (controller->remote_desktop); ++ meta_screen_cast_inhibit (controller->screen_cast); ++} ++ ++/** ++ * meta_remote_access_controller_uninhibit_remote_access: ++ * @controller: a #MetaRemoteAccessController ++ * ++ * Uninhibits remote access sessions from being created and running. If this was ++ * the last inhibitation that was inhibited, new remote access sessions can now ++ * be created. ++ */ ++void ++meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller) ++{ ++ meta_screen_cast_uninhibit (controller->screen_cast); ++ meta_remote_desktop_uninhibit (controller->remote_desktop); ++} ++ ++MetaRemoteAccessController * ++meta_remote_access_controller_new (MetaRemoteDesktop *remote_desktop, ++ MetaScreenCast *screen_cast) ++{ ++ MetaRemoteAccessController *remote_access_controller; ++ ++ remote_access_controller = g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, ++ NULL); ++ remote_access_controller->remote_desktop = remote_desktop; ++ remote_access_controller->screen_cast = screen_cast; ++ ++ return remote_access_controller; ++} ++ + static void + meta_remote_access_handle_init (MetaRemoteAccessHandle *handle) + { +diff --git a/src/backends/meta-remote-desktop.c b/src/backends/meta-remote-desktop.c +index d741dccd8..6d87d755b 100644 +--- a/src/backends/meta-remote-desktop.c ++++ b/src/backends/meta-remote-desktop.c +@@ -56,6 +56,8 @@ struct _MetaRemoteDesktop + + int dbus_name_id; + ++ int inhibit_count; ++ + GHashTable *sessions; + + MetaDbusSessionWatcher *session_watcher; +@@ -70,6 +72,34 @@ G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktop, + G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP, + meta_remote_desktop_init_iface)); + ++void ++meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop) ++{ ++ remote_desktop->inhibit_count++; ++ if (remote_desktop->inhibit_count == 1) ++ { ++ GHashTableIter iter; ++ gpointer key, value; ++ ++ g_hash_table_iter_init (&iter, remote_desktop->sessions); ++ while (g_hash_table_iter_next (&iter, &key, &value)) ++ { ++ MetaRemoteDesktopSession *session = value; ++ ++ g_hash_table_iter_steal (&iter); ++ meta_remote_desktop_session_close (session); ++ } ++ } ++} ++ ++void ++meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop) ++{ ++ g_return_if_fail (remote_desktop->inhibit_count > 0); ++ ++ remote_desktop->inhibit_count--; ++} ++ + GDBusConnection * + meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop) + { +@@ -108,6 +138,15 @@ handle_create_session (MetaDBusRemoteDesktop *skeleton, + char *session_path; + const char *client_dbus_name; + ++ if (remote_desktop->inhibit_count > 0) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_ACCESS_DENIED, ++ "Session creation inhibited"); ++ ++ return TRUE; ++ } ++ + peer_name = g_dbus_method_invocation_get_sender (invocation); + session = meta_remote_desktop_session_new (remote_desktop, + peer_name, +diff --git a/src/backends/meta-remote-desktop.h b/src/backends/meta-remote-desktop.h +index 3eebc13d5..210a84a04 100644 +--- a/src/backends/meta-remote-desktop.h ++++ b/src/backends/meta-remote-desktop.h +@@ -36,6 +36,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteDesktop, meta_remote_desktop, + META, REMOTE_DESKTOP, + MetaDBusRemoteDesktopSkeleton) + ++void meta_remote_desktop_inhibit (MetaRemoteDesktop *remote_desktop); ++ ++void meta_remote_desktop_uninhibit (MetaRemoteDesktop *remote_desktop); ++ + MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, + const char *session_id); + +diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c +index 063fffd8e..46bc26838 100644 +--- a/src/backends/meta-screen-cast.c ++++ b/src/backends/meta-screen-cast.c +@@ -40,6 +40,8 @@ struct _MetaScreenCast + + int dbus_name_id; + ++ int inhibit_count; ++ + GList *sessions; + + MetaDbusSessionWatcher *session_watcher; +@@ -54,6 +56,29 @@ G_DEFINE_TYPE_WITH_CODE (MetaScreenCast, meta_screen_cast, + G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST, + meta_screen_cast_init_iface)) + ++void ++meta_screen_cast_inhibit (MetaScreenCast *screen_cast) ++{ ++ screen_cast->inhibit_count++; ++ if (screen_cast->inhibit_count == 1) ++ { ++ while (screen_cast->sessions) ++ { ++ MetaScreenCastSession *session = screen_cast->sessions->data; ++ ++ meta_screen_cast_session_close (session); ++ } ++ } ++} ++ ++void ++meta_screen_cast_uninhibit (MetaScreenCast *screen_cast) ++{ ++ g_return_if_fail (screen_cast->inhibit_count > 0); ++ ++ screen_cast->inhibit_count--; ++} ++ + GDBusConnection * + meta_screen_cast_get_connection (MetaScreenCast *screen_cast) + { +@@ -118,6 +143,15 @@ handle_create_session (MetaDBusScreenCast *skeleton, + char *remote_desktop_session_id = NULL; + MetaScreenCastSessionType session_type; + ++ if (screen_cast->inhibit_count > 0) ++ { ++ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, ++ G_DBUS_ERROR_ACCESS_DENIED, ++ "Session creation inhibited"); ++ ++ return TRUE; ++ } ++ + g_variant_lookup (properties, "remote-desktop-session-id", "s", + &remote_desktop_session_id); + +diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h +index 994c40c53..a3b650cd8 100644 +--- a/src/backends/meta-screen-cast.h ++++ b/src/backends/meta-screen-cast.h +@@ -42,6 +42,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, + META, SCREEN_CAST, + MetaDBusScreenCastSkeleton) + ++void meta_screen_cast_inhibit (MetaScreenCast *screen_cast); ++ ++void meta_screen_cast_uninhibit (MetaScreenCast *screen_cast); ++ + GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); + + MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); +diff --git a/src/meta/meta-remote-access-controller.h b/src/meta/meta-remote-access-controller.h +index e7c707bbc..56f0dcbe2 100644 +--- a/src/meta/meta-remote-access-controller.h ++++ b/src/meta/meta-remote-access-controller.h +@@ -51,4 +51,10 @@ G_DECLARE_FINAL_TYPE (MetaRemoteAccessController, + META, REMOTE_ACCESS_CONTROLLER, + GObject) + ++META_EXPORT ++void meta_remote_access_controller_inhibit_remote_access (MetaRemoteAccessController *controller); ++ ++META_EXPORT ++void meta_remote_access_controller_uninhibit_remote_access (MetaRemoteAccessController *controller); ++ + #endif /* META_REMOTE_ACCESS_CONTROLLER_H */ +-- +2.26.2 + diff --git a/SPECS/mutter.spec b/SPECS/mutter.spec index bf9a614..115b5c7 100644 --- a/SPECS/mutter.spec +++ b/SPECS/mutter.spec @@ -3,12 +3,12 @@ %global gsettings_desktop_schemas_version 3.21.4 %global json_glib_version 0.12.0 %global libinput_version 1.4 -%global pipewire_version 0.2.2 +%global pipewire_version 0.3.0 %global mutter_api_version 4 Name: mutter Version: 3.32.2 -Release: 37%{?dist} +Release: 45%{?dist} Summary: Window and compositing manager based on Clutter License: GPLv2+ @@ -119,6 +119,42 @@ Patch292: 0003-clutter-fix-hole-in-ClutterPaintNode.patch # Fix corrupted background after suspend (#1828162) Patch300: 0001-background-Reload-when-GPU-memory-is-invalidated.patch +# Backport screen cast and remote desktop improvements (#1837381) +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/623 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/752 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/976 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1022 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1062 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/687 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1086 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1115 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1106 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1129 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1251 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1174 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1258 +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1212 +Patch400: screen-cast-remote-desktop-improvements.patch +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1283 +Patch401: 0001-screen-cast-src-Destroy-hash-dmabuf-table-after-stre.patch +Patch402: 0002-renderer-native-Don-t-leak-DMA-buffer-CoglFramebuffe.patch +Patch403: 0001-renderer-Add-API-to-check-whether-renderer-is-hardwa.patch +# https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1318 (#1847062) +Patch404: 0001-backend-Add-getter-for-MetaScreenCast.patch +Patch405: 0002-renderer-native-Add-API-to-get-primary-GPU.patch +Patch406: 0003-screen-cast-Move-DMA-buffer-allocation-to-MetaScreen.patch +Patch407: 0004-screen-cast-Disable-DMA-buffer-based-screen-casting-.patch + +# Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages (#1847203) +Patch500: 0001-stage-x11-Check-that-message-is-WM_PROTOCOLS-before-.patch + +# Don't show widow actor until explictly shown (#1719937) +Patch501: 0001-window-actor-Don-t-show-actor-until-meta_window_acto.patch + +# Handle GPU unplug gracefully (#1846191) +Patch502: 0001-monitor-manager-kms-Trigger-hotplug-processing-on-gp.patch +Patch503: 0002-gpu-kms-Reset-CRTC-mode-and-output-list-if-no-resour.patch + BuildRequires: chrpath BuildRequires: pango-devel BuildRequires: startup-notification-devel @@ -260,6 +296,38 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop %{_datadir}/mutter-%{mutter_api_version}/tests %changelog +* Thu Jul 02 2020 Jonas Ådahl - 3.32.2-45 +- Handle GPU unplug gracefully + Resolves: #1846191 + +* Thu Jun 25 2020 Jonas Ådahl - 3.32.2-44 +- Don't show widow actor until explictly shown + Resolves: #1719937 + +* Thu Jun 25 2020 Jonas Ådahl - 3.32.2-43 +- Only treat WM_PROTOCOLS messages as WM_PROTOCOL messages + Resolves: #1847203 + +* Tue Jun 16 2020 Jonas Ådahl - 3.32.2-42 +- Don't pass DMA buffers if they can't be mmap():ed + Related: #1847062 + +* Wed Jun 10 2020 Florian Müllner - 3.32.2-41 +- Backport is_rendering_hardware_acclerated() API + Related: #1837381 + +* Wed Jun 03 2020 Jonas Ådahl - 3.32.2-40 +- Fix DMA buffer memory leak + Related: #1837381 + +* Mon May 25 2020 Jonas Ådahl - 3.32.2-39 +- Fix incorrect pipewire dependency version + Related: #1837381 + +* Mon May 25 2020 Jonas Ådahl - 3.32.2-38 +- Backport screen cast and remote desktop improvements + Resolves: #1837381 + * Tue May 19 2020 Jonas Ådahl - 3.32.2-37 - Fix corrupted background after suspend Resolves: #1828162 @@ -328,7 +396,7 @@ desktop-file-validate %{buildroot}/%{_datadir}/applications/%{name}.desktop - Fix mode switch pad buttons without LEDs Resolves: #1666070 -* Mon Dec 01 2019 Tomas Pelka - 3.32.2-20 +* Mon Dec 02 2019 Tomas Pelka - 3.32.2-20 - Need rebuild in correct build target Resolves: #1730891