[mutter] screen-cast: Add RecordArea for screen cast arbitrary area
- From: Jonas Ådahl <jadahl src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [mutter] screen-cast: Add RecordArea for screen cast arbitrary area
- Date: Thu, 23 Apr 2020 18:44:49 +0000 (UTC)
commit c4535fdf85587e5cdf3613fad764d878b2e57fb4
Author: Jonas Ådahl <jadahl gmail com>
Date: Wed Mar 4 21:42:52 2020 +0100
screen-cast: Add RecordArea for screen cast arbitrary area
It takes coordinates in stage coordinate space, and will result in
a screen cast stream consisting of that area, but scaled up by the scale
factor of the view that overlaps with the area and has the highest scale
factor.
https://gitlab.gnome.org/GNOME/mutter/-/merge_requests/1207
clutter/clutter/clutter-mutter.h | 3 +
clutter/clutter/clutter-stage-private.h | 2 -
clutter/clutter/clutter-stage.c | 9 +-
src/backends/meta-screen-cast-area-stream-src.c | 570 ++++++++++++++++++++++++
src/backends/meta-screen-cast-area-stream-src.h | 37 ++
src/backends/meta-screen-cast-area-stream.c | 177 ++++++++
src/backends/meta-screen-cast-area-stream.h | 48 ++
src/backends/meta-screen-cast-session.c | 86 ++++
src/meson.build | 4 +
src/org.gnome.Mutter.ScreenCast.xml | 33 ++
10 files changed, 964 insertions(+), 5 deletions(-)
---
diff --git a/clutter/clutter/clutter-mutter.h b/clutter/clutter/clutter-mutter.h
index 7506e74da..d5dbf89e6 100644
--- a/clutter/clutter/clutter-mutter.h
+++ b/clutter/clutter/clutter-mutter.h
@@ -36,6 +36,9 @@
#include "cogl/clutter-stage-cogl.h"
#include "clutter/x11/clutter-backend-x11.h"
+CLUTTER_EXPORT
+GList * clutter_stage_peek_stage_views (ClutterStage *stage);
+
CLUTTER_EXPORT
void clutter_set_custom_backend_func (ClutterBackend *(* func) (void));
diff --git a/clutter/clutter/clutter-stage-private.h b/clutter/clutter/clutter-stage-private.h
index c8c1ef34a..db2ee4baf 100644
--- a/clutter/clutter/clutter-stage-private.h
+++ b/clutter/clutter/clutter-stage-private.h
@@ -139,8 +139,6 @@ void _clutter_stage_presented (ClutterStage *stag
CoglFrameEvent frame_event,
ClutterFrameInfo *frame_info);
-GList * _clutter_stage_peek_stage_views (ClutterStage *stage);
-
void clutter_stage_queue_actor_relayout (ClutterStage *stage,
ClutterActor *actor);
diff --git a/clutter/clutter/clutter-stage.c b/clutter/clutter/clutter-stage.c
index c97d9bd9a..d656ec2e9 100644
--- a/clutter/clutter/clutter-stage.c
+++ b/clutter/clutter/clutter-stage.c
@@ -552,7 +552,7 @@ clutter_stage_add_redraw_clip (ClutterStage *stage,
{
GList *l;
- for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
+ for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
@@ -1573,7 +1573,7 @@ is_full_stage_redraw_queued (ClutterStage *stage)
{
GList *l;
- for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next)
+ for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
{
ClutterStageView *view = l->data;
@@ -4402,8 +4402,11 @@ clutter_stage_thaw_updates (ClutterStage *stage)
}
}
+/**
+ * clutter_stage_peek_stage_views: (skip)
+ */
GList *
-_clutter_stage_peek_stage_views (ClutterStage *stage)
+clutter_stage_peek_stage_views (ClutterStage *stage)
{
ClutterStagePrivate *priv = stage->priv;
diff --git a/src/backends/meta-screen-cast-area-stream-src.c b/src/backends/meta-screen-cast-area-stream-src.c
new file mode 100644
index 000000000..1fe94d448
--- /dev/null
+++ b/src/backends/meta-screen-cast-area-stream-src.c
@@ -0,0 +1,570 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "backends/meta-screen-cast-area-stream-src.h"
+
+#include <spa/buffer/meta.h>
+
+#include "backends/meta-backend-private.h"
+#include "backends/meta-cursor-tracker-private.h"
+#include "backends/meta-screen-cast-area-stream.h"
+#include "backends/meta-screen-cast-session.h"
+#include "backends/meta-stage-private.h"
+#include "clutter/clutter.h"
+#include "clutter/clutter-mutter.h"
+#include "core/boxes-private.h"
+
+struct _MetaScreenCastAreaStreamSrc
+{
+ MetaScreenCastStreamSrc parent;
+
+ gboolean cursor_bitmap_invalid;
+ gboolean hw_cursor_inhibited;
+
+ GList *watches;
+
+ gulong cursor_moved_handler_id;
+ gulong cursor_changed_handler_id;
+
+ guint maybe_record_idle_id;
+};
+
+static void
+hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (MetaScreenCastAreaStreamSrc,
+ meta_screen_cast_area_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 (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaScreenCastStreamSrc *src;
+ MetaScreenCastStream *stream;
+ MetaScreenCastAreaStream *area_stream;
+
+ src = META_SCREEN_CAST_STREAM_SRC (area_src);
+ stream = meta_screen_cast_stream_src_get_stream (src);
+ area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+
+ return meta_screen_cast_area_stream_get_stage (area_stream);
+}
+
+static MetaBackend *
+get_backend (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_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 MetaCursorRenderer *
+get_cursor_renderer (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_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
+meta_screen_cast_area_stream_src_get_specs (MetaScreenCastStreamSrc *src,
+ int *width,
+ int *height,
+ float *frame_rate)
+{
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaRectangle *area;
+ float scale;
+
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ scale = meta_screen_cast_area_stream_get_scale (area_stream);
+
+ *width = (int) roundf (area->width * scale);
+ *height = (int) roundf (area->height * scale);
+ *frame_rate = 60.0;
+}
+
+static gboolean
+is_cursor_in_stream (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaBackend *backend = get_backend (area_src);
+ MetaCursorRenderer *cursor_renderer =
+ meta_backend_get_cursor_renderer (backend);
+ MetaRectangle *area;
+ graphene_rect_t area_rect;
+ MetaCursorSprite *cursor_sprite;
+
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ area_rect = meta_rectangle_to_graphene_rect (area);
+
+ cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
+ if (cursor_sprite)
+ {
+ graphene_rect_t cursor_rect;
+
+ cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer,
+ cursor_sprite);
+ return graphene_rect_intersection (&cursor_rect, &area_rect, NULL);
+ }
+ else
+ {
+ graphene_point_t cursor_position;
+
+ cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
+ return graphene_rect_contains_point (&area_rect, &cursor_position);
+ }
+}
+
+static void
+sync_cursor_state (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
+ ClutterStage *stage = get_stage (area_src);
+
+ if (!is_cursor_in_stream (area_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,
+ MetaScreenCastAreaStreamSrc *area_src)
+{
+ sync_cursor_state (area_src);
+}
+
+static void
+cursor_changed (MetaCursorTracker *cursor_tracker,
+ MetaScreenCastAreaStreamSrc *area_src)
+{
+ area_src->cursor_bitmap_invalid = TRUE;
+ sync_cursor_state (area_src);
+}
+
+static void
+inhibit_hw_cursor (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaCursorRenderer *cursor_renderer;
+ MetaHwCursorInhibitor *inhibitor;
+
+ g_return_if_fail (!area_src->hw_cursor_inhibited);
+
+ cursor_renderer = get_cursor_renderer (area_src);
+ inhibitor = META_HW_CURSOR_INHIBITOR (area_src);
+ meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor);
+
+ area_src->hw_cursor_inhibited = TRUE;
+}
+
+static void
+uninhibit_hw_cursor (MetaScreenCastAreaStreamSrc *area_src)
+{
+ MetaCursorRenderer *cursor_renderer;
+ MetaHwCursorInhibitor *inhibitor;
+
+ g_return_if_fail (area_src->hw_cursor_inhibited);
+
+ cursor_renderer = get_cursor_renderer (area_src);
+ inhibitor = META_HW_CURSOR_INHIBITOR (area_src);
+ meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor);
+
+ area_src->hw_cursor_inhibited = FALSE;
+}
+
+static gboolean
+maybe_record_frame_on_idle (gpointer user_data)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (user_data);
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
+
+ area_src->maybe_record_idle_id = 0;
+
+ meta_screen_cast_stream_src_maybe_record_frame (src);
+
+ return G_SOURCE_REMOVE;
+}
+
+static void
+stage_painted (MetaStage *stage,
+ ClutterStageView *view,
+ ClutterPaintContext *paint_context,
+ gpointer user_data)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (user_data);
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ const cairo_region_t *redraw_clip;
+ MetaRectangle *area;
+
+ if (area_src->maybe_record_idle_id)
+ return;
+
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ redraw_clip = clutter_paint_context_get_redraw_clip (paint_context);
+
+ if (redraw_clip)
+ {
+ switch (cairo_region_contains_rectangle (redraw_clip, area))
+ {
+ case CAIRO_REGION_OVERLAP_IN:
+ case CAIRO_REGION_OVERLAP_PART:
+ break;
+ case CAIRO_REGION_OVERLAP_OUT:
+ return;
+ }
+ }
+
+ area_src->maybe_record_idle_id = g_idle_add (maybe_record_frame_on_idle, src);
+}
+
+static void
+add_view_painted_watches (MetaScreenCastAreaStreamSrc *area_src,
+ MetaStageWatchPhase watch_phase)
+{
+ MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (area_src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaBackend *backend = get_backend (area_src);
+ MetaRenderer *renderer = meta_backend_get_renderer (backend);
+ ClutterStage *stage;
+ MetaStage *meta_stage;
+ MetaRectangle *area;
+ GList *l;
+
+ stage = get_stage (area_src);
+ meta_stage = META_STAGE (stage);
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+
+ for (l = meta_renderer_get_views (renderer); l; l = l->next)
+ {
+ MetaRendererView *view = l->data;
+ MetaRectangle view_layout;
+
+ clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout);
+ if (meta_rectangle_overlap (area, &view_layout))
+ {
+ MetaStageWatch *watch;
+
+ watch = meta_stage_watch_view (meta_stage,
+ CLUTTER_STAGE_VIEW (view),
+ watch_phase,
+ stage_painted,
+ area_src);
+
+ area_src->watches = g_list_prepend (area_src->watches, watch);
+ }
+ }
+}
+
+static void
+meta_screen_cast_area_stream_src_enable (MetaScreenCastStreamSrc *src)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (src);
+ MetaBackend *backend = get_backend (area_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 (area_src);
+
+ switch (meta_screen_cast_stream_get_cursor_mode (stream))
+ {
+ case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+ area_src->cursor_moved_handler_id =
+ g_signal_connect_after (cursor_tracker, "cursor-moved",
+ G_CALLBACK (cursor_moved),
+ area_src);
+ area_src->cursor_changed_handler_id =
+ g_signal_connect_after (cursor_tracker, "cursor-changed",
+ G_CALLBACK (cursor_changed),
+ area_src);
+ G_GNUC_FALLTHROUGH;
+ case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+ add_view_painted_watches (area_src,
+ META_STAGE_WATCH_AFTER_ACTOR_PAINT);
+ break;
+ case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+ inhibit_hw_cursor (area_src);
+ add_view_painted_watches (area_src,
+ META_STAGE_WATCH_AFTER_ACTOR_PAINT);
+ break;
+ }
+
+ clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
+}
+
+static void
+meta_screen_cast_area_stream_src_disable (MetaScreenCastStreamSrc *src)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (src);
+ MetaBackend *backend = get_backend (area_src);
+ MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
+ ClutterStage *stage;
+ MetaStage *meta_stage;
+ GList *l;
+
+ stage = get_stage (area_src);
+ meta_stage = META_STAGE (stage);
+
+ for (l = area_src->watches; l; l = l->next)
+ {
+ MetaStageWatch *watch = l->data;
+
+ meta_stage_remove_watch (meta_stage, watch);
+ }
+ g_clear_pointer (&area_src->watches, g_list_free);
+
+ if (area_src->hw_cursor_inhibited)
+ uninhibit_hw_cursor (area_src);
+
+ g_clear_signal_handler (&area_src->cursor_moved_handler_id,
+ cursor_tracker);
+ g_clear_signal_handler (&area_src->cursor_changed_handler_id,
+ cursor_tracker);
+
+ g_clear_handle_id (&area_src->maybe_record_idle_id, g_source_remove);
+}
+
+static gboolean
+meta_screen_cast_area_stream_src_record_frame (MetaScreenCastStreamSrc *src,
+ uint8_t *data)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ ClutterStage *stage;
+ MetaRectangle *area;
+ float scale;
+ int stride;
+ ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
+ g_autoptr (GError) error = NULL;
+
+ stage = get_stage (area_src);
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ scale = meta_screen_cast_area_stream_get_scale (area_stream);
+ stride = meta_screen_cast_stream_src_get_stride (src);
+
+ switch (meta_screen_cast_stream_get_cursor_mode (stream))
+ {
+ case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+ case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+ paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
+ break;
+ case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+ break;
+ }
+
+ if (!clutter_stage_paint_to_buffer (stage, area, scale,
+ data,
+ stride,
+ CLUTTER_CAIRO_FORMAT_ARGB32,
+ paint_flags,
+ &error))
+ {
+ g_warning ("Failed to record area: %s", error->message);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+meta_screen_cast_area_stream_src_blit_to_framebuffer (MetaScreenCastStreamSrc *src,
+ CoglFramebuffer *framebuffer)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaBackend *backend = get_backend (area_src);
+ ClutterStage *stage;
+ MetaRectangle *area;
+ float scale;
+ ClutterPaintFlag paint_flags = CLUTTER_PAINT_FLAG_NONE;
+
+ stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ scale = meta_screen_cast_area_stream_get_scale (area_stream);
+
+ switch (meta_screen_cast_stream_get_cursor_mode (stream))
+ {
+ case META_SCREEN_CAST_CURSOR_MODE_METADATA:
+ case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
+ paint_flags |= CLUTTER_PAINT_FLAG_NO_CURSORS;
+ break;
+ case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
+ break;
+ }
+ clutter_stage_paint_to_framebuffer (stage, framebuffer,
+ area, scale,
+ paint_flags);
+
+ cogl_framebuffer_finish (framebuffer);
+
+ return TRUE;
+}
+
+static void
+meta_screen_cast_area_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
+ struct spa_meta_cursor *spa_meta_cursor)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (src);
+ MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
+ MetaScreenCastAreaStream *area_stream = META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaBackend *backend = get_backend (area_src);
+ MetaCursorRenderer *cursor_renderer =
+ meta_backend_get_cursor_renderer (backend);
+ MetaCursorSprite *cursor_sprite;
+ MetaRectangle *area;
+ float scale;
+ graphene_point_t cursor_position;
+ int x, y;
+
+ cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
+
+ if (!is_cursor_in_stream (area_src))
+ {
+ meta_screen_cast_stream_src_unset_cursor_metadata (src,
+ spa_meta_cursor);
+ return;
+ }
+
+ area = meta_screen_cast_area_stream_get_area (area_stream);
+ scale = meta_screen_cast_area_stream_get_scale (area_stream);
+
+ cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
+ cursor_position.x -= area->x;
+ cursor_position.y -= area->y;
+ cursor_position.x *= scale;
+ cursor_position.y *= scale;
+
+ x = (int) roundf (cursor_position.x);
+ y = (int) roundf (cursor_position.y);
+
+ if (area_src->cursor_bitmap_invalid)
+ {
+ if (cursor_sprite)
+ {
+ float cursor_scale;
+ float metadata_scale;
+
+ cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite);
+ metadata_scale = scale * cursor_scale;
+ meta_screen_cast_stream_src_set_cursor_sprite_metadata (src,
+ spa_meta_cursor,
+ cursor_sprite,
+ x, y,
+ metadata_scale);
+ }
+ else
+ {
+ meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src,
+ spa_meta_cursor,
+ x, y);
+ }
+
+ area_src->cursor_bitmap_invalid = FALSE;
+ }
+ else
+ {
+ meta_screen_cast_stream_src_set_cursor_position_metadata (src,
+ spa_meta_cursor,
+ x, y);
+ }
+}
+
+static gboolean
+meta_screen_cast_area_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor,
+ MetaCursorSprite *cursor_sprite)
+{
+ MetaScreenCastAreaStreamSrc *area_src =
+ META_SCREEN_CAST_AREA_STREAM_SRC (inhibitor);
+
+ return is_cursor_in_stream (area_src);
+}
+
+static void
+hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface)
+{
+ iface->is_cursor_sprite_inhibited =
+ meta_screen_cast_area_stream_src_is_cursor_sprite_inhibited;
+}
+
+MetaScreenCastAreaStreamSrc *
+meta_screen_cast_area_stream_src_new (MetaScreenCastAreaStream *area_stream,
+ GError **error)
+{
+ return g_initable_new (META_TYPE_SCREEN_CAST_AREA_STREAM_SRC, NULL, error,
+ "stream", area_stream,
+ NULL);
+}
+
+static void
+meta_screen_cast_area_stream_src_init (MetaScreenCastAreaStreamSrc *area_src)
+{
+ area_src->cursor_bitmap_invalid = TRUE;
+}
+
+static void
+meta_screen_cast_area_stream_src_class_init (MetaScreenCastAreaStreamSrcClass *klass)
+{
+ MetaScreenCastStreamSrcClass *src_class =
+ META_SCREEN_CAST_STREAM_SRC_CLASS (klass);
+
+ src_class->get_specs = meta_screen_cast_area_stream_src_get_specs;
+ src_class->enable = meta_screen_cast_area_stream_src_enable;
+ src_class->disable = meta_screen_cast_area_stream_src_disable;
+ src_class->record_frame = meta_screen_cast_area_stream_src_record_frame;
+ src_class->blit_to_framebuffer =
+ meta_screen_cast_area_stream_src_blit_to_framebuffer;
+ src_class->set_cursor_metadata =
+ meta_screen_cast_area_stream_src_set_cursor_metadata;
+}
diff --git a/src/backends/meta-screen-cast-area-stream-src.h b/src/backends/meta-screen-cast-area-stream-src.h
new file mode 100644
index 000000000..72261642c
--- /dev/null
+++ b/src/backends/meta-screen-cast-area-stream-src.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#ifndef META_SCREEN_CAST_AREA_STREAM_SRC_H
+#define META_SCREEN_CAST_AREA_STREAM_SRC_H
+
+#include "backends/meta-screen-cast-stream-src.h"
+
+typedef struct _MetaScreenCastAreaStream MetaScreenCastAreaStream;
+
+#define META_TYPE_SCREEN_CAST_AREA_STREAM_SRC (meta_screen_cast_area_stream_src_get_type ())
+G_DECLARE_FINAL_TYPE (MetaScreenCastAreaStreamSrc,
+ meta_screen_cast_area_stream_src,
+ META, SCREEN_CAST_AREA_STREAM_SRC,
+ MetaScreenCastStreamSrc)
+
+MetaScreenCastAreaStreamSrc * meta_screen_cast_area_stream_src_new (MetaScreenCastAreaStream *area_stream,
+ GError **error);
+
+#endif /* META_SCREEN_CAST_AREA_STREAM_SRC_H */
diff --git a/src/backends/meta-screen-cast-area-stream.c b/src/backends/meta-screen-cast-area-stream.c
new file mode 100644
index 000000000..98883b2fc
--- /dev/null
+++ b/src/backends/meta-screen-cast-area-stream.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include "backends/meta-screen-cast-area-stream.h"
+
+#include "backends/meta-screen-cast-area-stream-src.h"
+
+struct _MetaScreenCastAreaStream
+{
+ MetaScreenCastStream parent;
+
+ ClutterStage *stage;
+
+ MetaRectangle area;
+ float scale;
+};
+
+G_DEFINE_TYPE (MetaScreenCastAreaStream,
+ meta_screen_cast_area_stream,
+ META_TYPE_SCREEN_CAST_STREAM)
+
+ClutterStage *
+meta_screen_cast_area_stream_get_stage (MetaScreenCastAreaStream *area_stream)
+{
+ return area_stream->stage;
+}
+
+MetaRectangle *
+meta_screen_cast_area_stream_get_area (MetaScreenCastAreaStream *area_stream)
+{
+ return &area_stream->area;
+}
+
+float
+meta_screen_cast_area_stream_get_scale (MetaScreenCastAreaStream *area_stream)
+{
+ return area_stream->scale;
+}
+
+static gboolean
+calculate_scale (ClutterStage *stage,
+ MetaRectangle *area,
+ float *out_scale)
+{
+ GList *l;
+ float scale = 0.0;
+
+ for (l = clutter_stage_peek_stage_views (stage); l; l = l->next)
+ {
+ ClutterStageView *stage_view = l->data;
+ MetaRectangle view_layout;
+
+ clutter_stage_view_get_layout (stage_view, &view_layout);
+ if (meta_rectangle_overlap (area, &view_layout))
+ scale = MAX (clutter_stage_view_get_scale (stage_view), scale);
+ }
+
+ if (scale == 0.0)
+ return FALSE;
+
+ *out_scale = scale;
+ return TRUE;
+}
+
+MetaScreenCastAreaStream *
+meta_screen_cast_area_stream_new (MetaScreenCastSession *session,
+ GDBusConnection *connection,
+ MetaRectangle *area,
+ ClutterStage *stage,
+ MetaScreenCastCursorMode cursor_mode,
+ GError **error)
+{
+ MetaScreenCastAreaStream *area_stream;
+ float scale;
+
+ if (!calculate_scale (stage, area, &scale))
+ {
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
+ "Area is off-screen");
+ return NULL;
+ }
+
+ area_stream = g_initable_new (META_TYPE_SCREEN_CAST_AREA_STREAM,
+ NULL,
+ error,
+ "session", session,
+ "connection", connection,
+ "cursor-mode", cursor_mode,
+ NULL);
+ if (!area_stream)
+ return NULL;
+
+ area_stream->area = *area;
+ area_stream->scale = scale;
+ area_stream->stage = stage;
+
+ return area_stream;
+}
+
+static MetaScreenCastStreamSrc *
+meta_screen_cast_area_stream_create_src (MetaScreenCastStream *stream,
+ GError **error)
+{
+ MetaScreenCastAreaStream *area_stream =
+ META_SCREEN_CAST_AREA_STREAM (stream);
+ MetaScreenCastAreaStreamSrc *area_stream_src;
+
+ area_stream_src = meta_screen_cast_area_stream_src_new (area_stream,
+ error);
+ if (!area_stream_src)
+ return NULL;
+
+ return META_SCREEN_CAST_STREAM_SRC (area_stream_src);
+}
+
+static void
+meta_screen_cast_area_stream_set_parameters (MetaScreenCastStream *stream,
+ GVariantBuilder *parameters_builder)
+{
+ MetaScreenCastAreaStream *area_stream =
+ META_SCREEN_CAST_AREA_STREAM (stream);
+
+ g_variant_builder_add (parameters_builder, "{sv}",
+ "size",
+ g_variant_new ("(ii)",
+ area_stream->area.width,
+ area_stream->area.height));
+}
+
+static void
+meta_screen_cast_area_stream_transform_position (MetaScreenCastStream *stream,
+ double stream_x,
+ double stream_y,
+ double *x,
+ double *y)
+{
+ MetaScreenCastAreaStream *area_stream =
+ META_SCREEN_CAST_AREA_STREAM (stream);
+
+ *x = area_stream->area.x + (int) roundf (stream_x / area_stream->scale);
+ *y = area_stream->area.y + (int) roundf (stream_y / area_stream->scale);
+}
+
+static void
+meta_screen_cast_area_stream_init (MetaScreenCastAreaStream *area_stream)
+{
+}
+
+static void
+meta_screen_cast_area_stream_class_init (MetaScreenCastAreaStreamClass *klass)
+{
+ MetaScreenCastStreamClass *stream_class =
+ META_SCREEN_CAST_STREAM_CLASS (klass);
+
+ stream_class->create_src = meta_screen_cast_area_stream_create_src;
+ stream_class->set_parameters = meta_screen_cast_area_stream_set_parameters;
+ stream_class->transform_position = meta_screen_cast_area_stream_transform_position;
+}
diff --git a/src/backends/meta-screen-cast-area-stream.h b/src/backends/meta-screen-cast-area-stream.h
new file mode 100644
index 000000000..47d1079e0
--- /dev/null
+++ b/src/backends/meta-screen-cast-area-stream.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2020 Red Hat Inc.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ */
+
+#ifndef META_SCREEN_CAST_AREA_STREAM_H
+#define META_SCREEN_CAST_AREA_STREAM_H
+
+#include <glib-object.h>
+
+#include "backends/meta-screen-cast-stream.h"
+#include "backends/meta-screen-cast.h"
+
+#define META_TYPE_SCREEN_CAST_AREA_STREAM (meta_screen_cast_area_stream_get_type ())
+G_DECLARE_FINAL_TYPE (MetaScreenCastAreaStream,
+ meta_screen_cast_area_stream,
+ META, SCREEN_CAST_AREA_STREAM,
+ MetaScreenCastStream)
+
+MetaScreenCastAreaStream * meta_screen_cast_area_stream_new (MetaScreenCastSession *session,
+ GDBusConnection *connection,
+ MetaRectangle *area,
+ ClutterStage *stage,
+ MetaScreenCastCursorMode cursor_mode,
+ GError **error);
+
+ClutterStage * meta_screen_cast_area_stream_get_stage (MetaScreenCastAreaStream *area_stream);
+
+MetaRectangle * meta_screen_cast_area_stream_get_area (MetaScreenCastAreaStream *area_stream);
+
+float meta_screen_cast_area_stream_get_scale (MetaScreenCastAreaStream *area_stream);
+
+#endif /* META_SCREEN_CAST_AREA_STREAM_H */
diff --git a/src/backends/meta-screen-cast-session.c b/src/backends/meta-screen-cast-session.c
index 7a1b8e07a..377dafd1f 100644
--- a/src/backends/meta-screen-cast-session.c
+++ b/src/backends/meta-screen-cast-session.c
@@ -27,6 +27,7 @@
#include "backends/meta-backend-private.h"
#include "backends/meta-dbus-session-watcher.h"
#include "backends/meta-remote-access-controller-private.h"
+#include "backends/meta-screen-cast-area-stream.h"
#include "backends/meta-screen-cast-monitor-stream.h"
#include "backends/meta-screen-cast-stream.h"
#include "backends/meta-screen-cast-window-stream.h"
@@ -485,6 +486,90 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
return TRUE;
}
+static gboolean
+handle_record_area (MetaDBusScreenCastSession *skeleton,
+ GDBusMethodInvocation *invocation,
+ int x,
+ int y,
+ int width,
+ int height,
+ GVariant *properties_variant)
+{
+ MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton);
+ GDBusInterfaceSkeleton *interface_skeleton;
+ GDBusConnection *connection;
+ MetaBackend *backend;
+ ClutterStage *stage;
+ MetaScreenCastCursorMode cursor_mode;
+ g_autoptr (GError) error = NULL;
+ MetaRectangle rect;
+ MetaScreenCastAreaStream *area_stream;
+ MetaScreenCastStream *stream;
+ char *stream_path;
+
+ if (!check_permission (session, invocation))
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_ACCESS_DENIED,
+ "Permission denied");
+ return TRUE;
+ }
+
+ if (!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;
+ }
+ }
+
+ interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
+ connection = g_dbus_interface_skeleton_get_connection (interface_skeleton);
+ backend = meta_screen_cast_get_backend (session->screen_cast);
+ stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
+
+ rect = (MetaRectangle) {
+ .x = x,
+ .y = y,
+ .width = width,
+ .height = height
+ };
+ area_stream = meta_screen_cast_area_stream_new (session,
+ connection,
+ &rect,
+ stage,
+ cursor_mode,
+ &error);
+ if (!area_stream)
+ {
+ g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
+ G_DBUS_ERROR_FAILED,
+ "Failed to record area: %s",
+ error->message);
+ return TRUE;
+ }
+
+ stream = META_SCREEN_CAST_STREAM (area_stream);
+ stream_path = meta_screen_cast_stream_get_object_path (stream);
+
+ session->streams = g_list_append (session->streams, stream);
+
+ g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session);
+
+ meta_dbus_screen_cast_session_complete_record_area (skeleton,
+ invocation,
+ stream_path);
+
+ return TRUE;
+}
+
static void
meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface)
{
@@ -492,6 +577,7 @@ meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface)
iface->handle_stop = handle_stop;
iface->handle_record_monitor = handle_record_monitor;
iface->handle_record_window = handle_record_window;
+ iface->handle_record_area = handle_record_area;
}
static void
diff --git a/src/meson.build b/src/meson.build
index ea337214c..39eb637ee 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -458,6 +458,10 @@ if have_remote_desktop
'backends/meta-remote-desktop-session.h',
'backends/meta-screen-cast.c',
'backends/meta-screen-cast.h',
+ 'backends/meta-screen-cast-area-stream.c',
+ 'backends/meta-screen-cast-area-stream.h',
+ 'backends/meta-screen-cast-area-stream-src.c',
+ 'backends/meta-screen-cast-area-stream-src.h',
'backends/meta-screen-cast-monitor-stream.c',
'backends/meta-screen-cast-monitor-stream.h',
'backends/meta-screen-cast-monitor-stream-src.c',
diff --git a/src/org.gnome.Mutter.ScreenCast.xml b/src/org.gnome.Mutter.ScreenCast.xml
index 1d9fa598e..e20f5e5c6 100644
--- a/src/org.gnome.Mutter.ScreenCast.xml
+++ b/src/org.gnome.Mutter.ScreenCast.xml
@@ -111,6 +111,39 @@
<arg name="properties" type="a{sv}" direction="in" />
<arg name="stream_path" type="o" direction="out" />
</method>
+
+ <!--
+ RecordArea:
+ @x: X position of the recorded area
+ @y: Y position of the recorded area
+ @width: width of the recorded area
+ @height: height of the recorded area
+ @properties: Properties
+ @stream_path: Path to the new stream object
+
+ Record an area of the stage. The coordinates are in stage coordinates.
+ The size of the stream does not necessarily match the size of the
+ recorded area, and will depend on DPI scale of the affected monitors.
+
+ Available @properties include:
+
+ * "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
+ Available since API version 2.
+
+ 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="RecordArea">
+ <arg name="x" type="i" direction="in" />
+ <arg name="y" type="i" direction="in" />
+ <arg name="width" type="i" direction="in" />
+ <arg name="height" type="i" direction="in" />
+ <arg name="properties" type="a{sv}" direction="in" />
+ <arg name="stream_path" type="o" direction="out" />
+ </method>
</interface>
<!--
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]