diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c index 9352f49e8..763ec96ee 100644 --- a/clutter/clutter/clutter-stage.c +++ b/clutter/clutter/clutter-stage.c @@ -3722,6 +3722,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 e8fbda84c..ddf08fde4 100644 --- a/clutter/clutter/clutter-stage.h +++ b/clutter/clutter/clutter-stage.h @@ -250,6 +250,9 @@ void clutter_stage_ensure_viewport (ClutterStage CLUTTER_AVAILABLE_IN_ALL void clutter_stage_ensure_redraw (ClutterStage *stage); +CLUTTER_AVAILABLE_IN_ALL +gboolean clutter_stage_is_redraw_queued (ClutterStage *stage); + #ifdef CLUTTER_ENABLE_EXPERIMENTAL_API CLUTTER_AVAILABLE_IN_1_14 void clutter_stage_set_sync_delay (ClutterStage *stage, diff --git a/configure.ac b/configure.ac index 2aa9c4352..449f3d693 100644 --- a/configure.ac +++ b/configure.ac @@ -245,7 +245,7 @@ AC_ARG_ENABLE(remote-desktop, enable_remote_desktop=no ) AS_IF([test "$enable_remote_desktop" = "yes"], [ - MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.2" + MUTTER_PC_MODULES="$MUTTER_PC_MODULES libpipewire-0.2 >= 0.2.5" AC_DEFINE([HAVE_REMOTE_DESKTOP],[1], [Defined if screen cast and remote desktop support is enabled]) ]) AM_CONDITIONAL([HAVE_REMOTE_DESKTOP],[test "$enable_remote_desktop" = "yes"]) diff --git a/src/backends/meta-backend.c b/src/backends/meta-backend.c index 888e51cd9..28f1cd92f 100644 --- a/src/backends/meta-backend.c +++ b/src/backends/meta-backend.c @@ -456,7 +456,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 eb79737f1..0a456bee6 100644 --- a/src/backends/meta-cursor-renderer.c +++ b/src/backends/meta-cursor-renderer.c @@ -35,6 +35,9 @@ #include "meta-stage-private.h" +G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor, + G_TYPE_OBJECT) + struct _MetaCursorRendererPrivate { float current_x; @@ -44,6 +47,8 @@ struct _MetaCursorRendererPrivate MetaOverlay *stage_overlay; gboolean handled_by_backend; guint post_paint_func_id; + + GList *hw_cursor_inhibitors; }; typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate; @@ -55,6 +60,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) @@ -283,3 +303,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 830d16ef6..092f17f1e 100644 --- a/src/backends/meta-cursor-renderer.h +++ b/src/backends/meta-cursor-renderer.h @@ -30,6 +30,18 @@ #include #include "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); @@ -55,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 6244f11ee..97e7f8cb4 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); } /** @@ -334,6 +348,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 ceac7df57..ea93dc99e 100644 --- a/src/backends/meta-renderer.c +++ b/src/backends/meta-renderer.c @@ -94,6 +94,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 bf51b51ab..1c617214b 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 382d7d4a2..1d6aea242 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-screen-cast-monitor-stream.h" +#include "backends/meta-screen-cast-session.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.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 df43f977c..33b9f026a 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 fbf3c77c3..f8dc04181 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 d0f5a79d9..45d403dca 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 ac0a31a16..105a65098 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 673a4640b..9f97bf36d 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)) + 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 = 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; + 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 f3b3fd779..f2f96f213 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 97ee4bfcf..c14f8fd85 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 fd7930c4c..28ca6f683 100644 --- a/src/backends/meta-screen-cast-stream.h +++ b/src/backends/meta-screen-cast-stream.h @@ -26,8 +26,12 @@ #include #include "backends/meta-screen-cast-stream-src.h" +#include "backends/meta-screen-cast.h" + #include "meta-dbus-screen-cast.h" +typedef struct _MetaScreenCastSession MetaScreenCastSession; + #define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream, META, SCREEN_CAST_STREAM, @@ -48,6 +52,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 +67,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 c3f9cf5ca..32df9f127 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 1200a39ef..4c9227116 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 6726ef873..3799be98a 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 4a67683e7..ceea9cbe1 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 a4fb8dd45..cadb6a2fe 100644 --- a/src/backends/meta-screen-cast.h +++ b/src/backends/meta-screen-cast.h @@ -25,9 +25,17 @@ #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, @@ -35,6 +43,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 73ca70118..4ed56c04b 100644 --- a/src/backends/meta-stage.c +++ b/src/backends/meta-stage.c @@ -30,7 +30,17 @@ #include "backends/meta-backend-private.h" #include "clutter/clutter-mutter.h" -struct _MetaOverlay { +enum +{ + ACTORS_PAINTED, + + N_SIGNALS +}; + +static guint signals[N_SIGNALS]; + +struct _MetaOverlay +{ gboolean enabled; CoglPipeline *pipeline; @@ -140,6 +150,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 = priv->overlays; l; l = l->next) meta_overlay_paint (l->data); } @@ -179,6 +191,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 29800953b..3ff4e81fb 100644 --- a/src/backends/native/meta-cursor-renderer-native.c +++ b/src/backends/native/meta-cursor-renderer-native.c @@ -587,6 +587,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 3cd02b6cb..953809727 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: