diff --git a/.gitlab-ci/Dockerfile b/.gitlab-ci/Dockerfile index 29db895f9fcf9690c58c57298e41c85e8419fc1b..43cafa1aea2753d33c73c12f89e0edcb20a7e3ca 100644 --- a/.gitlab-ci/Dockerfile +++ b/.gitlab-ci/Dockerfile @@ -13,5 +13,8 @@ RUN dnf -y update && dnf -y upgrade && \ # Unpackaged versions dnf install -y https://copr-be.cloud.fedoraproject.org/results/jadahl/mutter-ci/fedora-29-x86_64/00836095-gsettings-desktop-schemas/gsettings-desktop-schemas-3.30.1-1.20181206git918efdd69be53.fc29.x86_64.rpm https://copr-be.cloud.fedoraproject.org/results/jadahl/mutter-ci/fedora-29-x86_64/00836095-gsettings-desktop-schemas/gsettings-desktop-schemas-devel-3.30.1-1.20181206git918efdd69be53.fc29.x86_64.rpm && \ + # Packages not yet in stable + dnf install -y https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-0.2.5-1.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-devel-0.2.5-1.fc29.x86_64.rpm https://kojipkgs.fedoraproject.org//packages/pipewire/0.2.5/1.fc29/x86_64/pipewire-libs-0.2.5-1.fc29.x86_64.rpm && \ + dnf install -y intltool redhat-rpm-config make && \ dnf clean all diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index d1107149e26aef55b2e7b733f173015321d251bc..8dc7ebc4ecc73be2015f4a31d4cfa7a1e4bfb492 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -3731,6 +3731,17 @@ clutter_stage_ensure_redraw (ClutterStage *stage) _clutter_master_clock_start_running (master_clock); } +/** + * clutter_stage_is_redraw_queued: (skip) + */ +gboolean +clutter_stage_is_redraw_queued (ClutterStage *stage) +{ + ClutterStagePrivate *priv = stage->priv; + + return priv->redraw_pending; +} + /** * clutter_stage_queue_redraw: * @stage: the #ClutterStage diff --git a/clutter/clutter/clutter-stage.h b/clutter/clutter/clutter-stage.h index 643f8d7b3711ccd49b5203690feef4e811f8d98b..ab1ab92fa4e12490d4afe8b81701ea2d552f6774 100644 --- a/clutter/clutter/clutter-stage.h +++ b/clutter/clutter/clutter-stage.h @@ -250,6 +250,9 @@ void clutter_stage_ensure_viewport (ClutterStage CLUTTER_EXPORT void clutter_stage_ensure_redraw (ClutterStage *stage); +CLUTTER_EXPORT +gboolean clutter_stage_is_redraw_queued (ClutterStage *stage); + #ifdef CLUTTER_ENABLE_EXPERIMENTAL_API CLUTTER_EXPORT void clutter_stage_set_sync_delay (ClutterStage *stage, diff --git a/meson.build b/meson.build index 8d8f4da7babe41c1d4c32ad1ec5bcf5818979f48..be1b829a3b458f8e169d57ced19fc0329deceba3 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.2' +libpipewire_req = '>= 0.2.5' gnome = import('gnome') pkg = import('pkgconfig') diff --git a/src/backends/meta-backend-types.h b/src/backends/meta-backend-types.h index 66b6ae23849edd810b1efa275e6980336994c362..eb982d73eb23d5c729572849badd8dd43c736ef4 100644 --- a/src/backends/meta-backend-types.h +++ b/src/backends/meta-backend-types.h @@ -49,4 +49,8 @@ typedef struct _MetaTileInfo MetaTileInfo; typedef struct _MetaRenderer MetaRenderer; typedef struct _MetaRendererView MetaRendererView; +typedef struct _MetaScreenCast MetaScreenCast; +typedef struct _MetaScreenCastSession MetaScreenCastSession; +typedef struct _MetaScreenCastStream MetaScreenCastStream; + #endif /* META_BACKEND_TYPE_H */ diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 8987da1e3c7834f8d8afb08c0cb5a804e9ae0355..c527114bee2ea18d7df72daec4660a67996c2e8c 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -504,7 +504,8 @@ meta_backend_real_post_init (MetaBackend *backend) 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 (priv->dbus_session_watcher); + priv->screen_cast = meta_screen_cast_new (backend, + priv->dbus_session_watcher); priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); #endif /* HAVE_REMOTE_DESKTOP */ diff --git a/src/backends/meta-cursor-renderer.c b/src/backends/meta-cursor-renderer.c index 1f76206f84f8f5321f69cd94093e7ba9a0c835b9..34a7f91d85572730c2858e54a9f12392f15ead1b 100644 --- a/src/backends/meta-cursor-renderer.c +++ b/src/backends/meta-cursor-renderer.c @@ -34,6 +34,9 @@ #include "meta/meta-backend.h" #include "meta/util.h" +G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor, + G_TYPE_OBJECT) + struct _MetaCursorRendererPrivate { float current_x; @@ -43,6 +46,8 @@ struct _MetaCursorRendererPrivate MetaOverlay *stage_overlay; gboolean handled_by_backend; guint post_paint_func_id; + + GList *hw_cursor_inhibitors; }; typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate; @@ -54,6 +59,21 @@ static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT); +static gboolean +meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor, + MetaCursorSprite *cursor_sprite) +{ + MetaHwCursorInhibitorInterface *iface = + META_HW_CURSOR_INHIBITOR_GET_IFACE (inhibitor); + + return iface->is_cursor_sprite_inhibited (inhibitor, cursor_sprite); +} + +static void +meta_hw_cursor_inhibitor_default_init (MetaHwCursorInhibitorInterface *iface) +{ +} + void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) @@ -282,3 +302,45 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer) return priv->displayed_cursor; } + +void +meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer *renderer, + MetaHwCursorInhibitor *inhibitor) +{ + MetaCursorRendererPrivate *priv = + meta_cursor_renderer_get_instance_private (renderer); + + priv->hw_cursor_inhibitors = g_list_prepend (priv->hw_cursor_inhibitors, + inhibitor); +} + +void +meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer *renderer, + MetaHwCursorInhibitor *inhibitor) +{ + MetaCursorRendererPrivate *priv = + meta_cursor_renderer_get_instance_private (renderer); + + priv->hw_cursor_inhibitors = g_list_remove (priv->hw_cursor_inhibitors, + inhibitor); +} + +gboolean +meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer, + MetaCursorSprite *cursor_sprite) +{ + MetaCursorRendererPrivate *priv = + meta_cursor_renderer_get_instance_private (renderer); + GList *l; + + for (l = priv->hw_cursor_inhibitors; l; l = l->next) + { + MetaHwCursorInhibitor *inhibitor = l->data; + + if (meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (inhibitor, + cursor_sprite)) + return TRUE; + } + + return FALSE; +} diff --git a/src/backends/meta-cursor-renderer.h b/src/backends/meta-cursor-renderer.h index e1d5f8d42277afe6c594aecac74a9bf17ac10745..60f1dbe1585272005e459cc97aa2423437625bea 100644 --- a/src/backends/meta-cursor-renderer.h +++ b/src/backends/meta-cursor-renderer.h @@ -27,8 +27,21 @@ #include +#include "backends/meta-backend-types.h" #include "backends/meta-cursor.h" +#define META_TYPE_HW_CURSOR_INHIBITOR (meta_hw_cursor_inhibitor_get_type ()) +G_DECLARE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor, + META, HW_CURSOR_INHIBITOR, GObject) + +struct _MetaHwCursorInhibitorInterface +{ + GTypeInterface parent_iface; + + gboolean (* is_cursor_sprite_inhibited) (MetaHwCursorInhibitor *inhibitor, + MetaCursorSprite *cursor_sprite); +}; + #define META_TYPE_CURSOR_RENDERER (meta_cursor_renderer_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer, META, CURSOR_RENDERER, GObject); @@ -54,6 +67,15 @@ void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer); MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer); +void meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer *renderer, + MetaHwCursorInhibitor *inhibitor); + +void meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer *renderer, + MetaHwCursorInhibitor *inhibitor); + +gboolean meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer, + MetaCursorSprite *cursor_sprite); + ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); diff --git a/src/backends/meta-cursor-tracker.c b/src/backends/meta-cursor-tracker.c index 1a35019353f03388992050407332a5e88bd2584b..0d49327f45a6655bab324c088a5640bd8f5a69bb 100644 --- a/src/backends/meta-cursor-tracker.c +++ b/src/backends/meta-cursor-tracker.c @@ -48,6 +48,7 @@ G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT); enum { CURSOR_CHANGED, + CURSOR_MOVED, LAST_SIGNAL }; @@ -117,11 +118,15 @@ change_cursor_renderer (MetaCursorTracker *tracker) static void sync_cursor (MetaCursorTracker *tracker) { - if (update_displayed_cursor (tracker)) - g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); + gboolean cursor_changed = FALSE; + + cursor_changed = update_displayed_cursor (tracker); if (update_effective_cursor (tracker)) change_cursor_renderer (tracker); + + if (cursor_changed) + g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); } static void @@ -158,6 +163,15 @@ meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass) 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + + signals[CURSOR_MOVED] = g_signal_new ("cursor-moved", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 2, + G_TYPE_FLOAT, + G_TYPE_FLOAT); } /** @@ -342,6 +356,8 @@ meta_cursor_tracker_update_position (MetaCursorTracker *tracker, g_assert (meta_is_wayland_compositor ()); meta_cursor_renderer_set_position (cursor_renderer, new_x, new_y); + + g_signal_emit (tracker, signals[CURSOR_MOVED], 0, new_x, new_y); } static void diff --git a/src/backends/meta-renderer.c b/src/backends/meta-renderer.c index 85d3984edc9e64712c6341f83253ec2d3c6b8942..28637437ba6fda7809ba94f5be6f0ecd70063862 100644 --- a/src/backends/meta-renderer.c +++ b/src/backends/meta-renderer.c @@ -142,6 +142,24 @@ meta_renderer_get_views (MetaRenderer *renderer) return priv->views; } +MetaRendererView * +meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + MetaLogicalMonitor *logical_monitor) +{ + GList *l; + + for (l = meta_renderer_get_views (renderer); l; l = l->next) + { + MetaRendererView *view = l->data; + + if (meta_renderer_view_get_logical_monitor (view) == + logical_monitor) + return view; + } + + return NULL; +} + static void meta_renderer_finalize (GObject *object) { diff --git a/src/backends/meta-renderer.h b/src/backends/meta-renderer.h index ba91a2584cddca58a0c71065247a3a884ad2a575..b6df4f1770615eb25e8f9d65addfa8a6af39d4dd 100644 --- a/src/backends/meta-renderer.h +++ b/src/backends/meta-renderer.h @@ -53,4 +53,7 @@ void meta_renderer_set_legacy_view (MetaRenderer *renderer, GList * meta_renderer_get_views (MetaRenderer *renderer); +MetaRendererView * meta_renderer_get_view_from_logical_monitor (MetaRenderer *renderer, + MetaLogicalMonitor *logical_monitor); + #endif /* META_RENDERER_H */ diff --git a/src/backends/meta-screen-cast-monitor-stream-src.c b/src/backends/meta-screen-cast-monitor-stream-src.c index 1118daadd7553008220c01a2cf23795f998ceb2e..897b86ae64a572b2590360a21bb6a08c748ac48b 100644 --- a/src/backends/meta-screen-cast-monitor-stream-src.c +++ b/src/backends/meta-screen-cast-monitor-stream-src.c @@ -24,23 +24,38 @@ #include "backends/meta-screen-cast-monitor-stream-src.h" +#include + #include "backends/meta-backend-private.h" +#include "backends/meta-cursor-tracker-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-screen-cast-monitor-stream.h" +#include "backends/meta-screen-cast-session.h" #include "clutter/clutter.h" #include "clutter/clutter-mutter.h" +#include "core/boxes-private.h" struct _MetaScreenCastMonitorStreamSrc { MetaScreenCastStreamSrc parent; - gulong stage_painted_handler_id; + gboolean cursor_bitmap_invalid; + + gulong actors_painted_handler_id; + gulong paint_handler_id; + gulong cursor_moved_handler_id; + gulong cursor_changed_handler_id; }; -G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc, - meta_screen_cast_monitor_stream_src, - META_TYPE_SCREEN_CAST_STREAM_SRC) +static void +hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface); + +G_DEFINE_TYPE_WITH_CODE (MetaScreenCastMonitorStreamSrc, + meta_screen_cast_monitor_stream_src, + META_TYPE_SCREEN_CAST_STREAM_SRC, + G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR, + hw_cursor_inhibitor_iface_init)) static ClutterStage * get_stage (MetaScreenCastMonitorStreamSrc *monitor_src) @@ -102,18 +117,164 @@ stage_painted (ClutterActor *actor, meta_screen_cast_stream_src_maybe_record_frame (src); } +static MetaBackend * +get_backend (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + 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); + + return meta_screen_cast_get_backend (screen_cast); +} + +static gboolean +is_cursor_in_stream (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaBackend *backend = get_backend (monitor_src); + MetaCursorRenderer *cursor_renderer = + meta_backend_get_cursor_renderer (backend); + MetaMonitor *monitor; + MetaLogicalMonitor *logical_monitor; + MetaRectangle logical_monitor_layout; + ClutterRect logical_monitor_rect; + MetaCursorSprite *cursor_sprite; + + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); + logical_monitor_rect = + meta_rectangle_to_clutter_rect (&logical_monitor_layout); + + cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); + if (cursor_sprite) + { + ClutterRect cursor_rect; + + cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer, + cursor_sprite); + return clutter_rect_intersection (&cursor_rect, + &logical_monitor_rect, + NULL); + } + else + { + ClutterPoint cursor_position; + + cursor_position = meta_cursor_renderer_get_position (cursor_renderer); + return clutter_rect_contains_point (&logical_monitor_rect, + &cursor_position); + } +} + +static void +sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + ClutterStage *stage = get_stage (monitor_src); + + if (!is_cursor_in_stream (monitor_src)) + return; + + if (clutter_stage_is_redraw_queued (stage)) + return; + + meta_screen_cast_stream_src_maybe_record_frame (src); +} + +static void +cursor_moved (MetaCursorTracker *cursor_tracker, + float x, + float y, + MetaScreenCastMonitorStreamSrc *monitor_src) +{ + sync_cursor_state (monitor_src); +} + +static void +cursor_changed (MetaCursorTracker *cursor_tracker, + MetaScreenCastMonitorStreamSrc *monitor_src) +{ + monitor_src->cursor_bitmap_invalid = TRUE; + sync_cursor_state (monitor_src); +} + +static MetaCursorRenderer * +get_cursor_renderer (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); + 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); + MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); + + return meta_backend_get_cursor_renderer (backend); +} + +static void +inhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaCursorRenderer *cursor_renderer; + MetaHwCursorInhibitor *inhibitor; + + cursor_renderer = get_cursor_renderer (monitor_src); + inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src); + meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor); +} + +static void +uninhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src) +{ + MetaCursorRenderer *cursor_renderer; + MetaHwCursorInhibitor *inhibitor; + + cursor_renderer = get_cursor_renderer (monitor_src); + inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src); + meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor); +} + static void 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); + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterStage *stage; + MetaScreenCastStream *stream; + stream = meta_screen_cast_stream_src_get_stream (src); stage = get_stage (monitor_src); - monitor_src->stage_painted_handler_id = - g_signal_connect_after (stage, "paint", - G_CALLBACK (stage_painted), - monitor_src); + + switch (meta_screen_cast_stream_get_cursor_mode (stream)) + { + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + monitor_src->cursor_moved_handler_id = + g_signal_connect_after (cursor_tracker, "cursor-moved", + G_CALLBACK (cursor_moved), + monitor_src); + monitor_src->cursor_changed_handler_id = + g_signal_connect_after (cursor_tracker, "cursor-changed", + G_CALLBACK (cursor_changed), + 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); + 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); + break; + } + clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } @@ -122,14 +283,43 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); + MetaBackend *backend = get_backend (monitor_src); + MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterStage *stage; stage = get_stage (monitor_src); - g_signal_handler_disconnect (stage, monitor_src->stage_painted_handler_id); - monitor_src->stage_painted_handler_id = 0; + + if (monitor_src->actors_painted_handler_id) + { + g_signal_handler_disconnect (stage, + monitor_src->actors_painted_handler_id); + monitor_src->actors_painted_handler_id = 0; + } + + if (monitor_src->paint_handler_id) + { + g_signal_handler_disconnect (stage, + monitor_src->paint_handler_id); + monitor_src->paint_handler_id = 0; + uninhibit_hw_cursor (monitor_src); + } + + if (monitor_src->cursor_moved_handler_id) + { + g_signal_handler_disconnect (cursor_tracker, + monitor_src->cursor_moved_handler_id); + monitor_src->cursor_moved_handler_id = 0; + } + + if (monitor_src->cursor_changed_handler_id) + { + g_signal_handler_disconnect (cursor_tracker, + monitor_src->cursor_changed_handler_id); + monitor_src->cursor_changed_handler_id = 0; + } } -static void +static gboolean meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, uint8_t *data) { @@ -140,9 +330,226 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src, MetaLogicalMonitor *logical_monitor; stage = get_stage (monitor_src); + if (!clutter_stage_is_redraw_queued (stage)) + return FALSE; + monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data); + + return TRUE; +} + +static gboolean +draw_cursor_sprite_via_offscreen (MetaScreenCastMonitorStreamSrc *monitor_src, + CoglTexture *cursor_texture, + int bitmap_width, + int bitmap_height, + uint32_t *bitmap_data, + GError **error) +{ + MetaBackend *backend = get_backend (monitor_src); + ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); + CoglContext *cogl_context = + clutter_backend_get_cogl_context (clutter_backend); + CoglTexture2D *bitmap_texture; + CoglOffscreen *offscreen; + CoglFramebuffer *fb; + CoglPipeline *pipeline; + CoglColor clear_color; + + bitmap_texture = cogl_texture_2d_new_with_size (cogl_context, + bitmap_width, bitmap_height); + cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture), + FALSE); + if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error)) + { + cogl_object_unref (bitmap_texture); + return FALSE; + } + + offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture)); + fb = COGL_FRAMEBUFFER (offscreen); + cogl_object_unref (bitmap_texture); + if (!cogl_framebuffer_allocate (fb, error)) + { + cogl_object_unref (fb); + return FALSE; + } + + 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_color_init_from_4ub (&clear_color, 0, 0, 0, 0); + cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color); + cogl_framebuffer_draw_rectangle (fb, pipeline, + -1, 1, 1, -1); + cogl_object_unref (pipeline); + + cogl_framebuffer_read_pixels (fb, + 0, 0, + bitmap_width, bitmap_height, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + (uint8_t *) bitmap_data); + cogl_object_unref (fb); + + return TRUE; +} + +static void +meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +{ + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (src); + MetaBackend *backend = get_backend (monitor_src); + MetaCursorRenderer *cursor_renderer = + meta_backend_get_cursor_renderer (backend); + MetaRenderer *renderer = meta_backend_get_renderer (backend); + MetaSpaType *spa_type = meta_screen_cast_stream_src_get_spa_type (src); + GError *error = NULL; + MetaCursorSprite *cursor_sprite; + CoglTexture *cursor_texture; + MetaMonitor *monitor; + MetaLogicalMonitor *logical_monitor; + MetaRectangle logical_monitor_layout; + ClutterRect logical_monitor_rect; + MetaRendererView *view; + float view_scale; + ClutterPoint cursor_position; + struct spa_meta_bitmap *spa_meta_bitmap; + + cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); + if (cursor_sprite) + cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); + else + cursor_texture = NULL; + + if (!is_cursor_in_stream (monitor_src)) + { + spa_meta_cursor->id = 0; + return; + } + + monitor = get_monitor (monitor_src); + logical_monitor = meta_monitor_get_logical_monitor (monitor); + logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); + logical_monitor_rect = + meta_rectangle_to_clutter_rect (&logical_monitor_layout); + + view = meta_renderer_get_view_from_logical_monitor (renderer, + logical_monitor); + if (view) + view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view)); + else + view_scale = 1.0; + + cursor_position = meta_cursor_renderer_get_position (cursor_renderer); + cursor_position.x -= logical_monitor_rect.origin.x; + cursor_position.y -= logical_monitor_rect.origin.y; + cursor_position.x *= view_scale; + cursor_position.y *= view_scale; + + spa_meta_cursor->id = 1; + spa_meta_cursor->position.x = (int32_t) roundf (cursor_position.x); + spa_meta_cursor->position.y = (int32_t) roundf (cursor_position.y); + + if (!monitor_src->cursor_bitmap_invalid) + { + spa_meta_cursor->hotspot.x = 0; + spa_meta_cursor->hotspot.y = 0; + spa_meta_cursor->bitmap_offset = 0; + return; + } + monitor_src->cursor_bitmap_invalid = FALSE; + + spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor); + + 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->offset = sizeof (struct spa_meta_bitmap); + + if (cursor_texture) + { + float cursor_scale; + float bitmap_scale; + int hotspot_x, hotspot_y; + int texture_width, texture_height; + int bitmap_width, bitmap_height; + uint32_t *bitmap_data; + + cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite); + bitmap_scale = view_scale * cursor_scale; + + meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); + spa_meta_cursor->hotspot.x = (int32_t) roundf (hotspot_x * bitmap_scale); + spa_meta_cursor->hotspot.y = (int32_t) roundf (hotspot_y * bitmap_scale); + + texture_width = cogl_texture_get_width (cursor_texture); + texture_height = cogl_texture_get_height (cursor_texture); + bitmap_width = texture_width * bitmap_scale; + bitmap_height = texture_height * bitmap_scale; + + spa_meta_bitmap->size.width = bitmap_width; + spa_meta_bitmap->size.height = bitmap_height; + spa_meta_bitmap->stride = bitmap_width * 4; + + bitmap_data = SPA_MEMBER (spa_meta_bitmap, + spa_meta_bitmap->offset, + uint32_t); + + if (texture_width == bitmap_width && + texture_height == bitmap_height) + { + cogl_texture_get_data (cursor_texture, + COGL_PIXEL_FORMAT_RGBA_8888_PRE, + texture_width * 4, + (uint8_t *) bitmap_data); + } + else + { + if (!draw_cursor_sprite_via_offscreen (monitor_src, + cursor_texture, + bitmap_width, + bitmap_height, + bitmap_data, + &error)) + { + g_warning ("Failed to draw cursor via offscreen: %s", + error->message); + g_error_free (error); + spa_meta_cursor->id = 0; + } + } + } + else + { + spa_meta_cursor->hotspot.x = 0; + spa_meta_cursor->hotspot.y = 0; + + *spa_meta_bitmap = (struct spa_meta_bitmap) { 0 }; + } +} + +static gboolean +meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor, + MetaCursorSprite *cursor_sprite) +{ + MetaScreenCastMonitorStreamSrc *monitor_src = + META_SCREEN_CAST_MONITOR_STREAM_SRC (inhibitor); + + return is_cursor_in_stream (monitor_src); +} + +static void +hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface) +{ + iface->is_cursor_sprite_inhibited = + meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited; } MetaScreenCastMonitorStreamSrc * @@ -157,6 +564,7 @@ meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream *monitor_s static void meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src) { + monitor_src->cursor_bitmap_invalid = TRUE; } static void @@ -169,4 +577,6 @@ 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->set_cursor_metadata = + meta_screen_cast_monitor_stream_src_set_cursor_metadata; } diff --git a/src/backends/meta-screen-cast-monitor-stream.c b/src/backends/meta-screen-cast-monitor-stream.c index df43f977c9a10c0d5ad973c05e8787d37c3f9ee9..33b9f026a2a6420aba0bc5dc5518882db49104c3 100644 --- a/src/backends/meta-screen-cast-monitor-stream.c +++ b/src/backends/meta-screen-cast-monitor-stream.c @@ -105,12 +105,15 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito } MetaScreenCastMonitorStream * -meta_screen_cast_monitor_stream_new (GDBusConnection *connection, - MetaMonitorManager *monitor_manager, - MetaMonitor *monitor, - ClutterStage *stage, - GError **error) +meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, + GDBusConnection *connection, + MetaMonitor *monitor, + ClutterStage *stage, + MetaScreenCastCursorMode cursor_mode, + GError **error) { + MetaGpu *gpu = meta_monitor_get_gpu (monitor); + MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu); MetaScreenCastMonitorStream *monitor_stream; if (!meta_monitor_is_active (monitor)) @@ -122,7 +125,9 @@ meta_screen_cast_monitor_stream_new (GDBusConnection *connection, monitor_stream = g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM, NULL, error, + "session", session, "connection", connection, + "cursor-mode", cursor_mode, "monitor", monitor, NULL); if (!monitor_stream) diff --git a/src/backends/meta-screen-cast-monitor-stream.h b/src/backends/meta-screen-cast-monitor-stream.h index fbf3c77c3077afbed5ea7c520033bbce2e2903bb..f8dc04181ad82b62aeecbe1d6a8e8e2fb509b349 100644 --- a/src/backends/meta-screen-cast-monitor-stream.h +++ b/src/backends/meta-screen-cast-monitor-stream.h @@ -27,6 +27,7 @@ #include "backends/meta-monitor-manager-private.h" #include "backends/meta-screen-cast-stream.h" +#include "backends/meta-screen-cast.h" #define META_TYPE_SCREEN_CAST_MONITOR_STREAM (meta_screen_cast_monitor_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream, @@ -34,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream, META, SCREEN_CAST_MONITOR_STREAM, MetaScreenCastStream) -MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection *connection, - MetaMonitorManager *monitor_manager, - MetaMonitor *monitor, - ClutterStage *stage, - GError **error); +MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, + GDBusConnection *connection, + MetaMonitor *monitor, + ClutterStage *stage, + MetaScreenCastCursorMode cursor_mode, + GError **error); ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream); diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c index d0f5a79d919dc6fe76dd9dda116b13e868105fcc..45d403dca06010aa6b819320f0c0bf9078cba179 100644 --- a/src/backends/meta-screen-cast-session.c +++ b/src/backends/meta-screen-cast-session.c @@ -38,6 +38,8 @@ struct _MetaScreenCastSession { MetaDBusScreenCastSessionSkeleton parent; + MetaScreenCast *screen_cast; + char *peer_name; MetaScreenCastSessionType session_type; @@ -159,6 +161,12 @@ meta_screen_cast_session_get_stream (MetaScreenCastSession *session, return NULL; } +MetaScreenCast * +meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session) +{ + return session->screen_cast; +} + char * meta_screen_cast_session_get_object_path (MetaScreenCastSession *session) { @@ -254,6 +262,20 @@ on_stream_closed (MetaScreenCastStream *stream, meta_screen_cast_session_close (session); } +static gboolean +is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode) +{ + switch (cursor_mode) + { + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + return TRUE; + } + + return FALSE; +} + static gboolean handle_record_monitor (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation, @@ -267,6 +289,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton, MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitor *monitor; + MetaScreenCastCursorMode cursor_mode; ClutterStage *stage; GError *error = NULL; MetaScreenCastMonitorStream *monitor_stream; @@ -298,12 +321,28 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton, return TRUE; } + if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode)) + { + cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN; + } + else + { + if (!is_valid_cursor_mode (cursor_mode)) + { + g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, + G_DBUS_ERROR_FAILED, + "Unknown cursor mode"); + return TRUE; + } + } + stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); - monitor_stream = meta_screen_cast_monitor_stream_new (connection, - monitor_manager, + monitor_stream = meta_screen_cast_monitor_stream_new (session, + connection, monitor, stage, + cursor_mode, &error); if (!monitor_stream) { @@ -382,7 +421,8 @@ handle_record_window (MetaDBusScreenCastSession *skeleton, interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton); connection = g_dbus_interface_skeleton_get_connection (interface_skeleton); - window_stream = meta_screen_cast_window_stream_new (connection, + window_stream = meta_screen_cast_window_stream_new (session, + connection, window, &error); if (!window_stream) @@ -442,6 +482,7 @@ meta_screen_cast_session_new (MetaScreenCast *screen_cast, static unsigned int global_session_number = 0; session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL); + session->screen_cast = screen_cast; session->session_type = session_type; session->peer_name = g_strdup (peer_name); session->object_path = diff --git a/src/backends/meta-screen-cast-session.h b/src/backends/meta-screen-cast-session.h index ac0a31a165b7266a4f4bef2cee5cae8e0fe4524b..105a6509895cae520933ab2fec2aaaabc18b30bd 100644 --- a/src/backends/meta-screen-cast-session.h +++ b/src/backends/meta-screen-cast-session.h @@ -60,4 +60,6 @@ void meta_screen_cast_session_close (MetaScreenCastSession *session); MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path); +MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session); + #endif /* META_SCREEN_CAST_SESSION_H */ diff --git a/src/backends/meta-screen-cast-stream-src.c b/src/backends/meta-screen-cast-stream-src.c index 04b84eb1df57375564894b909c1d7b4bc38582c9..7e11bd067269702b808a72bd239e3277417280c0 100644 --- a/src/backends/meta-screen-cast-stream-src.c +++ b/src/backends/meta-screen-cast-stream-src.c @@ -40,6 +40,10 @@ #define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \ (TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name)) +#define CURSOR_META_SIZE(width, height) \ + (sizeof (struct spa_meta_cursor) + \ + sizeof (struct spa_meta_bitmap) + width * height * 4) + enum { PROP_0, @@ -57,14 +61,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; -} MetaSpaType; - typedef struct _MetaPipeWireSource { GSource base; @@ -133,14 +129,68 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, return FALSE; } -static void +static gboolean meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, uint8_t *data) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); - klass->record_frame (src, data); + return klass->record_frame (src, data); +} + +static void +meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor) +{ + MetaScreenCastStreamSrcClass *klass = + META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); + + if (klass->set_cursor_metadata) + klass->set_cursor_metadata (src, spa_meta_cursor); +} + +MetaSpaType * +meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src) +{ + MetaScreenCastStreamSrcPrivate *priv = + meta_screen_cast_stream_src_get_instance_private (src); + + return &priv->spa_type; +} + +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); + if (spa_meta_cursor) + meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor); +} + +static void +maybe_record_cursor (MetaScreenCastStreamSrc *src, + struct spa_buffer *spa_buffer, + uint8_t *data) +{ + MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); + + switch (meta_screen_cast_stream_get_cursor_mode (stream)) + { + case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: + case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: + return; + case META_SCREEN_CAST_CURSOR_MODE_METADATA: + add_cursor_metadata (src, spa_buffer); + return; + } + + g_assert_not_reached (); } void @@ -151,7 +201,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) MetaRectangle crop_rect; struct pw_buffer *buffer; struct spa_buffer *spa_buffer; - struct spa_meta_video_crop *spa_meta_video_crop; uint8_t *map = NULL; uint8_t *data; uint64_t now_us; @@ -199,35 +248,45 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src) return; } - meta_screen_cast_stream_src_record_frame (src, data); - - /* Update VideoCrop if needed */ - spa_meta_video_crop = spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop); - if (spa_meta_video_crop) + if (meta_screen_cast_stream_src_record_frame (src, data)) { - 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; - } - else + struct spa_meta_video_crop *spa_meta_video_crop; + + spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; + + /* Update VideoCrop if needed */ + spa_meta_video_crop = + spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop); + if (spa_meta_video_crop) { - 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; + 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; + } + 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; + } } } + else + { + spa_buffer->datas[0].chunk->size = 0; + } + + maybe_record_cursor (src, spa_buffer, data); priv->last_frame_timestamp_us = now_us; if (map) munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset); - spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; - pw_stream_queue_buffer (priv->pipewire_stream, buffer); } @@ -314,7 +373,7 @@ on_stream_format_changed (void *data, uint8_t params_buffer[1024]; int32_t width, height, stride, size; struct spa_pod_builder pod_builder; - const struct spa_pod *params[2]; + const struct spa_pod *params[3]; const int bpp = 4; if (!format) @@ -348,6 +407,12 @@ on_stream_format_changed (void *data, ":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop, ":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop)); + params[2] = spa_pod_builder_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)); + pw_stream_finish_format (priv->pipewire_stream, 0, params, G_N_ELEMENTS (params)); } @@ -517,6 +582,7 @@ init_spa_type (MetaSpaType *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 * diff --git a/src/backends/meta-screen-cast-stream-src.h b/src/backends/meta-screen-cast-stream-src.h index f3b3fd77938d3bebe3ca76bb96c1ba912f8aa41d..f2f96f2130fbc71e864a415e611affe567c97ab3 100644 --- a/src/backends/meta-screen-cast-stream-src.h +++ b/src/backends/meta-screen-cast-stream-src.h @@ -24,10 +24,26 @@ #define META_SCREEN_CAST_STREAM_SRC_H #include +#include +#include +#include "backends/meta-backend-private.h" +#include "backends/meta-cursor-renderer.h" +#include "backends/meta-cursor.h" +#include "backends/meta-renderer.h" #include "clutter/clutter.h" +#include "cogl/cogl.h" #include "meta/boxes.h" +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 _MetaScreenCastStream MetaScreenCastStream; #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) @@ -46,14 +62,18 @@ struct _MetaScreenCastStreamSrcClass float *frame_rate); void (* enable) (MetaScreenCastStreamSrc *src); void (* disable) (MetaScreenCastStreamSrc *src); - void (* record_frame) (MetaScreenCastStreamSrc *src, - uint8_t *data); + gboolean (* record_frame) (MetaScreenCastStreamSrc *src, + uint8_t *data); gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, MetaRectangle *crop_rect); + void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, + struct spa_meta_cursor *spa_meta_cursor); }; void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src); MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); +MetaSpaType * meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src); + #endif /* META_SCREEN_CAST_STREAM_SRC_H */ diff --git a/src/backends/meta-screen-cast-stream.c b/src/backends/meta-screen-cast-stream.c index 97ee4bfcf37903dbf0a581d6b4f06dba351102f2..c14f8fd85121a22ff65267e03493e8d7429f7f16 100644 --- a/src/backends/meta-screen-cast-stream.c +++ b/src/backends/meta-screen-cast-stream.c @@ -24,13 +24,17 @@ #include "backends/meta-screen-cast-stream.h" +#include "backends/meta-screen-cast-session.h" + #define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Stream" enum { PROP_0, + PROP_SESSION, PROP_CONNECTION, + PROP_CURSOR_MODE, }; enum @@ -44,9 +48,13 @@ static guint signals[N_SIGNALS]; typedef struct _MetaScreenCastStreamPrivate { + MetaScreenCastSession *session; + GDBusConnection *connection; char *object_path; + MetaScreenCastCursorMode cursor_mode; + MetaScreenCastStreamSrc *src; } MetaScreenCastStreamPrivate; @@ -97,6 +105,15 @@ on_stream_src_ready (MetaScreenCastStreamSrc *src, meta_dbus_screen_cast_stream_emit_pipewire_stream_added (skeleton, node_id); } +MetaScreenCastSession * +meta_screen_cast_stream_get_session (MetaScreenCastStream *stream) +{ + MetaScreenCastStreamPrivate *priv = + meta_screen_cast_stream_get_instance_private (stream); + + return priv->session; +} + gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream, GError **error) @@ -150,6 +167,15 @@ meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream, y); } +MetaScreenCastCursorMode +meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream) +{ + MetaScreenCastStreamPrivate *priv = + meta_screen_cast_stream_get_instance_private (stream); + + return priv->cursor_mode; +} + static void meta_screen_cast_stream_set_property (GObject *object, guint prop_id, @@ -162,9 +188,15 @@ meta_screen_cast_stream_set_property (GObject *object, switch (prop_id) { + case PROP_SESSION: + priv->session = g_value_get_object (value); + break; case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; + case PROP_CURSOR_MODE: + priv->cursor_mode = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -182,9 +214,15 @@ meta_screen_cast_stream_get_property (GObject *object, switch (prop_id) { + case PROP_SESSION: + g_value_set_object (value, priv->session); + break; case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; + case PROP_CURSOR_MODE: + g_value_set_uint (value, priv->cursor_mode); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } @@ -256,6 +294,16 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass) object_class->set_property = meta_screen_cast_stream_set_property; object_class->get_property = meta_screen_cast_stream_get_property; + g_object_class_install_property (object_class, + PROP_SESSION, + g_param_spec_object ("session", + "session", + "MetaScreenSession", + META_TYPE_SCREEN_CAST_SESSION, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object ("connection", @@ -266,6 +314,18 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass) G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (object_class, + PROP_CURSOR_MODE, + g_param_spec_uint ("cursor-mode", + "cursor-mode", + "Cursor mode", + META_SCREEN_CAST_CURSOR_MODE_HIDDEN, + META_SCREEN_CAST_CURSOR_MODE_METADATA, + META_SCREEN_CAST_CURSOR_MODE_HIDDEN, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); + signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, diff --git a/src/backends/meta-screen-cast-stream.h b/src/backends/meta-screen-cast-stream.h index fd7930c4c29b92a131a0fce734b0919263ed838b..4fc10869d7a20f54f41e09cc2aa21f9b4e2de09d 100644 --- a/src/backends/meta-screen-cast-stream.h +++ b/src/backends/meta-screen-cast-stream.h @@ -26,6 +26,8 @@ #include #include "backends/meta-screen-cast-stream-src.h" +#include "backends/meta-screen-cast.h" + #include "meta-dbus-screen-cast.h" #define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ()) @@ -48,6 +50,8 @@ struct _MetaScreenCastStreamClass double *y); }; +MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream); + gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream, GError **error); @@ -61,4 +65,6 @@ void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream, double *x, double *y); +MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream); + #endif /* META_SCREEN_CAST_STREAM_H */ diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c index c3f9cf5ca9212077815438567a22b4e6acbda359..32df9f1275c3710c287bdbe4fc46e51c45cd42fc 100644 --- a/src/backends/meta-screen-cast-window-stream-src.c +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -207,7 +207,7 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) meta_screen_cast_window_stream_src_stop (window_src); } -static void +static gboolean meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, uint8_t *data) { @@ -215,6 +215,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, META_SCREEN_CAST_WINDOW_STREAM_SRC (src); capture_into (window_src, data); + + return TRUE; } MetaScreenCastWindowStreamSrc * diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c index 1200a39eff5dfcb04101932d5a7a50a00bd8b61d..4c92271164595c1bdabdeac787a1cbded35e0952 100644 --- a/src/backends/meta-screen-cast-window-stream.c +++ b/src/backends/meta-screen-cast-window-stream.c @@ -71,9 +71,10 @@ meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_st } MetaScreenCastWindowStream * -meta_screen_cast_window_stream_new (GDBusConnection *connection, - MetaWindow *window, - GError **error) +meta_screen_cast_window_stream_new (MetaScreenCastSession *session, + GDBusConnection *connection, + MetaWindow *window, + GError **error) { MetaScreenCastWindowStream *window_stream; MetaLogicalMonitor *logical_monitor; @@ -90,6 +91,7 @@ meta_screen_cast_window_stream_new (GDBusConnection *connection, window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM, NULL, error, + "session", session, "connection", connection, "window", window, NULL); diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h index 6726ef873170195473982e3083f229680447a2ce..3799be98a2cebf99301b510c56b7302b700a7239 100644 --- a/src/backends/meta-screen-cast-window-stream.h +++ b/src/backends/meta-screen-cast-window-stream.h @@ -32,9 +32,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream, META, SCREEN_CAST_WINDOW_STREAM, MetaScreenCastStream) -MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (GDBusConnection *connection, - MetaWindow *window, - GError **error); +MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session, + GDBusConnection *connection, + MetaWindow *window, + GError **error); MetaWindow * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream); int meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream); diff --git a/src/backends/meta-screen-cast.c b/src/backends/meta-screen-cast.c index bebef5b4ee65e8c95f99d2bbeb59e85e5f4617a4..ebf6c18b395183f19aeb19ad65076c58a08747ea 100644 --- a/src/backends/meta-screen-cast.c +++ b/src/backends/meta-screen-cast.c @@ -43,6 +43,7 @@ struct _MetaScreenCast GList *sessions; MetaDbusSessionWatcher *session_watcher; + MetaBackend *backend; }; static void @@ -62,12 +63,20 @@ meta_screen_cast_get_connection (MetaScreenCast *screen_cast) return g_dbus_interface_skeleton_get_connection (interface_skeleton); } +MetaBackend * +meta_screen_cast_get_backend (MetaScreenCast *screen_cast) +{ + return screen_cast->backend; +} + static gboolean register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, const char *remote_desktop_session_id, GError **error) { - MetaBackend *backend = meta_get_backend (); + MetaScreenCast *screen_cast = + meta_screen_cast_session_get_screen_cast (session); + MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend); MetaRemoteDesktopSession *remote_desktop_session; @@ -244,11 +253,13 @@ meta_screen_cast_finalize (GObject *object) } MetaScreenCast * -meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher) +meta_screen_cast_new (MetaBackend *backend, + MetaDbusSessionWatcher *session_watcher) { MetaScreenCast *screen_cast; screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL); + screen_cast->backend = backend; screen_cast->session_watcher = session_watcher; return screen_cast; diff --git a/src/backends/meta-screen-cast.h b/src/backends/meta-screen-cast.h index a28a0aa07af4724f0a418436e801818930afa607..994c40c5358daf9613dc9691469733f865a90a28 100644 --- a/src/backends/meta-screen-cast.h +++ b/src/backends/meta-screen-cast.h @@ -25,10 +25,18 @@ #include +#include "backends/meta-backend-private.h" #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-screen-cast.h" +typedef enum _MetaScreenCastCursorMode +{ + META_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0, + META_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1, + META_SCREEN_CAST_CURSOR_MODE_METADATA = 2, +} MetaScreenCastCursorMode; + #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, META, SCREEN_CAST, @@ -36,6 +44,9 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); -MetaScreenCast * meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher); +MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); + +MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, + MetaDbusSessionWatcher *session_watcher); #endif /* META_SCREEN_CAST_H */ diff --git a/src/backends/meta-stage.c b/src/backends/meta-stage.c index 9b1dadab77ebae84ca6e53e6cab6a29b42693533..c009f1022334922b94a930911fa17206984001af 100644 --- a/src/backends/meta-stage.c +++ b/src/backends/meta-stage.c @@ -30,7 +30,17 @@ #include "meta/meta-monitor-manager.h" #include "meta/util.h" -struct _MetaOverlay { +enum +{ + ACTORS_PAINTED, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +struct _MetaOverlay +{ gboolean enabled; CoglPipeline *pipeline; @@ -141,6 +151,8 @@ meta_stage_paint (ClutterActor *actor) CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor); + g_signal_emit (stage, signals[ACTORS_PAINTED], 0); + for (l = stage->overlays; l; l = l->next) meta_overlay_paint (l->data); } @@ -178,6 +190,13 @@ meta_stage_class_init (MetaStageClass *klass) stage_class->activate = meta_stage_activate; stage_class->deactivate = meta_stage_deactivate; + + signals[ACTORS_PAINTED] = g_signal_new ("actors-painted", + G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, + NULL, NULL, NULL, + G_TYPE_NONE, 0); } static void diff --git a/src/backends/native/meta-cursor-renderer-native.c b/src/backends/native/meta-cursor-renderer-native.c index 4fd7b1736cc8bad35fb41ef167efcdd62699376e..d325483449d6fc7b8c71751045482f6ff3be5dce 100644 --- a/src/backends/native/meta-cursor-renderer-native.c +++ b/src/backends/native/meta-cursor-renderer-native.c @@ -586,6 +586,10 @@ should_have_hw_cursor (MetaCursorRenderer *renderer, if (!cursor_sprite) return FALSE; + if (meta_cursor_renderer_is_hw_cursors_inhibited (renderer, + cursor_sprite)) + return FALSE; + for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = l->data; diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml index 3cd02b6cba28eb4a5b16b40fe7f3c604164f7cc4..953809727e0a3d9bc9672cd238cf374c5df691cc 100644 --- a/src/org.gnome.Mutter.ScreenCast.xml +++ b/src/org.gnome.Mutter.ScreenCast.xml @@ -71,7 +71,15 @@ Record a single monitor. - Available @properties include: (none) + Available @properties include: + + * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below) + + Available cursor mode values: + + 0: hidden - cursor is not included in the stream + 1: embedded - cursor is included in the framebuffer + 2: metadata - cursor is included as metadata in the PipeWire stream --> @@ -84,7 +92,7 @@ @properties: Properties used determining what window to select @stream_path: Path to the new stream object - Record a single window. + Record a single window. The cursor will not be included. Available @properties include: