screen-cast: Add 'cursor-mode' to allow decoupled cursor updates

The 'cursor-mode', which currently is limited to RecordMonitor(), allows
the user to either do screen casts where the cursor is hidden, embedded
in the framebuffer, or sent as PipeWire stream metadata.

The latter allows the user to get cursor updates sent, including the
cursor sprite, without requiring a stage paint each frame. Currently
this is done by using the cursor sprite texture, and either reading
directly from, or drawing to an offscreen framebuffer which is read from
instead, in case the texture is scaled.

https://gitlab.gnome.org/GNOME/mutter/merge_requests/357
This commit is contained in:
Jonas Ådahl 2018-12-14 17:47:57 +01:00
parent 79d99cbe3f
commit 4e402b3972
13 changed files with 625 additions and 57 deletions

View File

@ -13,5 +13,8 @@ RUN dnf -y update && dnf -y upgrade && \
# Unpackaged versions # 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 && \ 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 install -y intltool redhat-rpm-config make && \
dnf clean all dnf clean all

View File

@ -43,7 +43,7 @@ libinput_req = '>= 1.4'
gbm_req = '>= 10.3' gbm_req = '>= 10.3'
# screen cast version requirements # screen cast version requirements
libpipewire_req = '>= 0.2.2' libpipewire_req = '>= 0.2.5'
gnome = import('gnome') gnome = import('gnome')
pkg = import('pkgconfig') pkg = import('pkgconfig')

View File

@ -24,23 +24,36 @@
#include "backends/meta-screen-cast-monitor-stream-src.h" #include "backends/meta-screen-cast-monitor-stream-src.h"
#include <spa/buffer/meta.h>
#include "backends/meta-backend-private.h" #include "backends/meta-backend-private.h"
#include "backends/meta-cursor-tracker-private.h"
#include "backends/meta-logical-monitor.h" #include "backends/meta-logical-monitor.h"
#include "backends/meta-monitor.h" #include "backends/meta-monitor.h"
#include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-screen-cast-monitor-stream.h"
#include "backends/meta-screen-cast-session.h"
#include "clutter/clutter.h" #include "clutter/clutter.h"
#include "clutter/clutter-mutter.h" #include "clutter/clutter-mutter.h"
#include "core/boxes-private.h"
struct _MetaScreenCastMonitorStreamSrc struct _MetaScreenCastMonitorStreamSrc
{ {
MetaScreenCastStreamSrc parent; MetaScreenCastStreamSrc parent;
gulong actors_painted_handler_id; gulong actors_painted_handler_id;
gulong paint_handler_id;
gulong cursor_moved_handler_id;
gulong cursor_changed_handler_id;
}; };
G_DEFINE_TYPE (MetaScreenCastMonitorStreamSrc, static void
meta_screen_cast_monitor_stream_src, hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
META_TYPE_SCREEN_CAST_STREAM_SRC)
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 * static ClutterStage *
get_stage (MetaScreenCastMonitorStreamSrc *monitor_src) get_stage (MetaScreenCastMonitorStreamSrc *monitor_src)
@ -102,18 +115,163 @@ stage_painted (ClutterActor *actor,
meta_screen_cast_stream_src_maybe_record_frame (src); 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)
{
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 static void
meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
{ {
MetaScreenCastMonitorStreamSrc *monitor_src = MetaScreenCastMonitorStreamSrc *monitor_src =
META_SCREEN_CAST_MONITOR_STREAM_SRC (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; ClutterStage *stage;
MetaScreenCastStream *stream;
stream = meta_screen_cast_stream_src_get_stream (src);
stage = get_stage (monitor_src); stage = get_stage (monitor_src);
monitor_src->actors_painted_handler_id =
g_signal_connect_after (stage, "actors-painted", switch (meta_screen_cast_stream_get_cursor_mode (stream))
G_CALLBACK (stage_painted), {
monitor_src); 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)); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
} }
@ -122,14 +280,43 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
{ {
MetaScreenCastMonitorStreamSrc *monitor_src = MetaScreenCastMonitorStreamSrc *monitor_src =
META_SCREEN_CAST_MONITOR_STREAM_SRC (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; ClutterStage *stage;
stage = get_stage (monitor_src); stage = get_stage (monitor_src);
g_signal_handler_disconnect (stage, monitor_src->actors_painted_handler_id);
monitor_src->actors_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, meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
uint8_t *data) uint8_t *data)
{ {
@ -140,9 +327,216 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
MetaLogicalMonitor *logical_monitor; MetaLogicalMonitor *logical_monitor;
stage = get_stage (monitor_src); stage = get_stage (monitor_src);
if (!clutter_stage_is_redraw_queued (stage))
return FALSE;
monitor = get_monitor (monitor_src); monitor = get_monitor (monitor_src);
logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor = meta_monitor_get_logical_monitor (monitor);
clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data); 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);
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 * MetaScreenCastMonitorStreamSrc *
@ -169,4 +563,6 @@ meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcCl
src_class->enable = meta_screen_cast_monitor_stream_src_enable; src_class->enable = meta_screen_cast_monitor_stream_src_enable;
src_class->disable = meta_screen_cast_monitor_stream_src_disable; src_class->disable = meta_screen_cast_monitor_stream_src_disable;
src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame; 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;
} }

View File

@ -105,11 +105,12 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
} }
MetaScreenCastMonitorStream * MetaScreenCastMonitorStream *
meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection, GDBusConnection *connection,
MetaMonitor *monitor, MetaMonitor *monitor,
ClutterStage *stage, ClutterStage *stage,
GError **error) MetaScreenCastCursorMode cursor_mode,
GError **error)
{ {
MetaGpu *gpu = meta_monitor_get_gpu (monitor); MetaGpu *gpu = meta_monitor_get_gpu (monitor);
MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu); MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
@ -126,6 +127,7 @@ meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session,
error, error,
"session", session, "session", session,
"connection", connection, "connection", connection,
"cursor-mode", cursor_mode,
"monitor", monitor, "monitor", monitor,
NULL); NULL);
if (!monitor_stream) if (!monitor_stream)

View File

@ -35,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
META, SCREEN_CAST_MONITOR_STREAM, META, SCREEN_CAST_MONITOR_STREAM,
MetaScreenCastStream) MetaScreenCastStream)
MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session,
GDBusConnection *connection, GDBusConnection *connection,
MetaMonitor *monitor, MetaMonitor *monitor,
ClutterStage *stage, ClutterStage *stage,
GError **error); MetaScreenCastCursorMode cursor_mode,
GError **error);
ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream); ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);

View File

@ -262,6 +262,20 @@ on_stream_closed (MetaScreenCastStream *stream,
meta_screen_cast_session_close (session); 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 static gboolean
handle_record_monitor (MetaDBusScreenCastSession *skeleton, handle_record_monitor (MetaDBusScreenCastSession *skeleton,
GDBusMethodInvocation *invocation, GDBusMethodInvocation *invocation,
@ -275,6 +289,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
MetaMonitorManager *monitor_manager = MetaMonitorManager *monitor_manager =
meta_backend_get_monitor_manager (backend); meta_backend_get_monitor_manager (backend);
MetaMonitor *monitor; MetaMonitor *monitor;
MetaScreenCastCursorMode cursor_mode;
ClutterStage *stage; ClutterStage *stage;
GError *error = NULL; GError *error = NULL;
MetaScreenCastMonitorStream *monitor_stream; MetaScreenCastMonitorStream *monitor_stream;
@ -306,12 +321,28 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
return TRUE; 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)); stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
monitor_stream = meta_screen_cast_monitor_stream_new (session, monitor_stream = meta_screen_cast_monitor_stream_new (session,
connection, connection,
monitor, monitor,
stage, stage,
cursor_mode,
&error); &error);
if (!monitor_stream) if (!monitor_stream)
{ {

View File

@ -40,6 +40,10 @@
#define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \ #define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
(TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, 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 enum
{ {
PROP_0, PROP_0,
@ -57,14 +61,6 @@ enum
static guint signals[N_SIGNALS]; 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 typedef struct _MetaPipeWireSource
{ {
GSource base; GSource base;
@ -133,14 +129,68 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
return FALSE; return FALSE;
} }
static void static gboolean
meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src, meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
uint8_t *data) uint8_t *data)
{ {
MetaScreenCastStreamSrcClass *klass = MetaScreenCastStreamSrcClass *klass =
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); 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 void
@ -151,7 +201,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
MetaRectangle crop_rect; MetaRectangle crop_rect;
struct pw_buffer *buffer; struct pw_buffer *buffer;
struct spa_buffer *spa_buffer; struct spa_buffer *spa_buffer;
struct spa_meta_video_crop *spa_meta_video_crop;
uint8_t *map = NULL; uint8_t *map = NULL;
uint8_t *data; uint8_t *data;
uint64_t now_us; uint64_t now_us;
@ -199,35 +248,45 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
return; return;
} }
meta_screen_cast_stream_src_record_frame (src, data); if (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_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; if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
spa_meta_video_crop->y = crop_rect.y; {
spa_meta_video_crop->width = crop_rect.width; spa_meta_video_crop->x = crop_rect.x;
spa_meta_video_crop->height = crop_rect.height; spa_meta_video_crop->y = crop_rect.y;
} spa_meta_video_crop->width = crop_rect.width;
else spa_meta_video_crop->height = crop_rect.height;
{ }
spa_meta_video_crop->x = 0; else
spa_meta_video_crop->y = 0; {
spa_meta_video_crop->width = priv->stream_width; spa_meta_video_crop->x = 0;
spa_meta_video_crop->height = priv->stream_height; 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; priv->last_frame_timestamp_us = now_us;
if (map) if (map)
munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset); 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); pw_stream_queue_buffer (priv->pipewire_stream, buffer);
} }
@ -314,7 +373,7 @@ on_stream_format_changed (void *data,
uint8_t params_buffer[1024]; uint8_t params_buffer[1024];
int32_t width, height, stride, size; int32_t width, height, stride, size;
struct spa_pod_builder pod_builder; struct spa_pod_builder pod_builder;
const struct spa_pod *params[2]; const struct spa_pod *params[3];
const int bpp = 4; const int bpp = 4;
if (!format) 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.type, "I", pipewire_type->meta.VideoCrop,
":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop)); ":", 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, pw_stream_finish_format (priv->pipewire_stream, 0,
params, G_N_ELEMENTS (params)); 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_media_subtype_map (map, &type->media_subtype);
spa_type_format_video_map (map, &type->format_video); spa_type_format_video_map (map, &type->format_video);
spa_type_video_format_map (map, &type->video_format); spa_type_video_format_map (map, &type->video_format);
type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor);
} }
static MetaPipeWireSource * static MetaPipeWireSource *

View File

@ -24,10 +24,26 @@
#define META_SCREEN_CAST_STREAM_SRC_H #define META_SCREEN_CAST_STREAM_SRC_H
#include <glib-object.h> #include <glib-object.h>
#include <spa/param/video/format-utils.h>
#include <spa/buffer/meta.h>
#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 "clutter/clutter.h"
#include "cogl/cogl.h"
#include "meta/boxes.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; typedef struct _MetaScreenCastStream MetaScreenCastStream;
#define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
@ -46,14 +62,18 @@ struct _MetaScreenCastStreamSrcClass
float *frame_rate); float *frame_rate);
void (* enable) (MetaScreenCastStreamSrc *src); void (* enable) (MetaScreenCastStreamSrc *src);
void (* disable) (MetaScreenCastStreamSrc *src); void (* disable) (MetaScreenCastStreamSrc *src);
void (* record_frame) (MetaScreenCastStreamSrc *src, gboolean (* record_frame) (MetaScreenCastStreamSrc *src,
uint8_t *data); uint8_t *data);
gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
MetaRectangle *crop_rect); 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); void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (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 */ #endif /* META_SCREEN_CAST_STREAM_SRC_H */

View File

@ -34,6 +34,7 @@ enum
PROP_SESSION, PROP_SESSION,
PROP_CONNECTION, PROP_CONNECTION,
PROP_CURSOR_MODE,
}; };
enum enum
@ -52,6 +53,8 @@ typedef struct _MetaScreenCastStreamPrivate
GDBusConnection *connection; GDBusConnection *connection;
char *object_path; char *object_path;
MetaScreenCastCursorMode cursor_mode;
MetaScreenCastStreamSrc *src; MetaScreenCastStreamSrc *src;
} MetaScreenCastStreamPrivate; } MetaScreenCastStreamPrivate;
@ -164,6 +167,15 @@ meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
y); 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 static void
meta_screen_cast_stream_set_property (GObject *object, meta_screen_cast_stream_set_property (GObject *object,
guint prop_id, guint prop_id,
@ -182,6 +194,9 @@ meta_screen_cast_stream_set_property (GObject *object,
case PROP_CONNECTION: case PROP_CONNECTION:
priv->connection = g_value_get_object (value); priv->connection = g_value_get_object (value);
break; break;
case PROP_CURSOR_MODE:
priv->cursor_mode = g_value_get_uint (value);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
@ -205,6 +220,9 @@ meta_screen_cast_stream_get_property (GObject *object,
case PROP_CONNECTION: case PROP_CONNECTION:
g_value_set_object (value, priv->connection); g_value_set_object (value, priv->connection);
break; break;
case PROP_CURSOR_MODE:
g_value_set_uint (value, priv->cursor_mode);
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
} }
@ -296,6 +314,18 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
G_PARAM_CONSTRUCT_ONLY | G_PARAM_CONSTRUCT_ONLY |
G_PARAM_STATIC_STRINGS)); 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", signals[CLOSED] = g_signal_new ("closed",
G_TYPE_FROM_CLASS (klass), G_TYPE_FROM_CLASS (klass),
G_SIGNAL_RUN_LAST, G_SIGNAL_RUN_LAST,

View File

@ -65,4 +65,6 @@ void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
double *x, double *x,
double *y); double *y);
MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream);
#endif /* META_SCREEN_CAST_STREAM_H */ #endif /* META_SCREEN_CAST_STREAM_H */

View File

@ -207,7 +207,7 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
meta_screen_cast_window_stream_src_stop (window_src); meta_screen_cast_window_stream_src_stop (window_src);
} }
static void static gboolean
meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
uint8_t *data) uint8_t *data)
{ {
@ -215,6 +215,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
META_SCREEN_CAST_WINDOW_STREAM_SRC (src); META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
capture_into (window_src, data); capture_into (window_src, data);
return TRUE;
} }
MetaScreenCastWindowStreamSrc * MetaScreenCastWindowStreamSrc *

View File

@ -30,6 +30,13 @@
#include "meta-dbus-screen-cast.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 ()) #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ())
G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
META, SCREEN_CAST, META, SCREEN_CAST,

View File

@ -71,7 +71,15 @@
Record a single monitor. 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
--> -->
<method name="RecordMonitor"> <method name="RecordMonitor">
<arg name="connector" type="s" direction="in" /> <arg name="connector" type="s" direction="in" />
@ -84,7 +92,7 @@
@properties: Properties used determining what window to select @properties: Properties used determining what window to select
@stream_path: Path to the new stream object @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: Available @properties include: