From fab59c0866bd13b944f0e9a3a251ba1a70ca31d4 Mon Sep 17 00:00:00 2001 From: Olivier Fourdan Date: Tue, 9 Oct 2018 15:35:14 +0200 Subject: [PATCH 07/12] screen-cast-session: Add screen-cast window mode Window mode will cast the content of a single window using the `MetaScreenCastWindow` interface. https://gitlab.gnome.org/GNOME/mutter/merge_requests/306 (cherry picked from commit dbe7279c7fe6fb793292cd6740c900e6f0c21975) --- src/Makefile.am | 4 + .../meta-screen-cast-window-stream-src.c | 245 ++++++++++++++++ .../meta-screen-cast-window-stream-src.h | 37 +++ src/backends/meta-screen-cast-window-stream.c | 270 ++++++++++++++++++ src/backends/meta-screen-cast-window-stream.h | 43 +++ 5 files changed, 599 insertions(+) create mode 100644 src/backends/meta-screen-cast-window-stream-src.c create mode 100644 src/backends/meta-screen-cast-window-stream-src.h create mode 100644 src/backends/meta-screen-cast-window-stream.c create mode 100644 src/backends/meta-screen-cast-window-stream.h diff --git a/src/Makefile.am b/src/Makefile.am index 93586a2..0606efa 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -372,6 +372,10 @@ libmutter_@LIBMUTTER_API_VERSION@_la_SOURCES += \ backends/meta-screen-cast-monitor-stream.h \ backends/meta-screen-cast-monitor-stream-src.c \ backends/meta-screen-cast-monitor-stream-src.h \ + backends/meta-screen-cast-window-stream-src.c \ + backends/meta-screen-cast-window-stream-src.h \ + backends/meta-screen-cast-window-stream.c \ + backends/meta-screen-cast-window-stream.h \ backends/meta-screen-cast-session.c \ backends/meta-screen-cast-session.h \ backends/meta-screen-cast-stream.c \ diff --git a/src/backends/meta-screen-cast-window-stream-src.c b/src/backends/meta-screen-cast-window-stream-src.c new file mode 100644 index 0000000..c3f9cf5 --- /dev/null +++ b/src/backends/meta-screen-cast-window-stream-src.c @@ -0,0 +1,245 @@ +/* + * Copyright (C) 2018 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-window-stream-src.h" + +#include "backends/meta-backend-private.h" +#include "backends/meta-screen-cast-window.h" +#include "backends/meta-screen-cast-window-stream.h" +#include "compositor/meta-window-actor-private.h" + +struct _MetaScreenCastWindowStreamSrc +{ + MetaScreenCastStreamSrc parent; + + MetaWindowActor *window_actor; + + unsigned long actor_painted_handler_id; + unsigned long actor_destroyed_handler_id; +}; + +G_DEFINE_TYPE (MetaScreenCastWindowStreamSrc, + meta_screen_cast_window_stream_src, + META_TYPE_SCREEN_CAST_STREAM_SRC) + +static MetaScreenCastWindowStream * +get_window_stream (MetaScreenCastWindowStreamSrc *window_src) +{ + MetaScreenCastStreamSrc *src; + MetaScreenCastStream *stream; + + src = META_SCREEN_CAST_STREAM_SRC (window_src); + stream = meta_screen_cast_stream_src_get_stream (src); + + return META_SCREEN_CAST_WINDOW_STREAM (stream); +} + +static MetaWindow * +get_window (MetaScreenCastWindowStreamSrc *window_src) +{ + MetaScreenCastWindowStream *window_stream; + + window_stream = get_window_stream (window_src); + + return meta_screen_cast_window_stream_get_window (window_stream); +} + +static int +get_stream_width (MetaScreenCastWindowStreamSrc *window_src) +{ + MetaScreenCastWindowStream *window_stream; + + window_stream = get_window_stream (window_src); + + return meta_screen_cast_window_stream_get_width (window_stream); +} + +static int +get_stream_height (MetaScreenCastWindowStreamSrc *window_src) +{ + MetaScreenCastWindowStream *window_stream; + + window_stream = get_window_stream (window_src); + + return meta_screen_cast_window_stream_get_height (window_stream); +} + +static gboolean +capture_into (MetaScreenCastWindowStreamSrc *window_src, + uint8_t *data) +{ + MetaRectangle stream_rect; + MetaScreenCastWindow *screen_cast_window; + + stream_rect.x = 0; + stream_rect.y = 0; + stream_rect.width = get_stream_width (window_src); + stream_rect.height = get_stream_height (window_src); + + screen_cast_window = META_SCREEN_CAST_WINDOW (window_src->window_actor); + meta_screen_cast_window_capture_into (screen_cast_window, &stream_rect, data); + + return TRUE; +} + +static void +meta_screen_cast_window_stream_src_get_specs (MetaScreenCastStreamSrc *src, + int *width, + int *height, + float *frame_rate) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + + *width = get_stream_width (window_src); + *height = get_stream_height (window_src); + *frame_rate = 60.0f; +} + +static gboolean +meta_screen_cast_window_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, + MetaRectangle *crop_rect) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + MetaScreenCastWindow *screen_cast_window; + MetaRectangle stream_rect; + + screen_cast_window = META_SCREEN_CAST_WINDOW (window_src->window_actor); + meta_screen_cast_window_get_frame_bounds (screen_cast_window, crop_rect); + + stream_rect.x = 0; + stream_rect.y = 0; + stream_rect.width = get_stream_width (window_src); + stream_rect.height = get_stream_height (window_src); + + meta_rectangle_intersect (crop_rect, &stream_rect, crop_rect); + + return TRUE; +} + +static void +meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_src) + +{ + if (!window_src->window_actor) + return; + + if (window_src->actor_painted_handler_id) + g_signal_handler_disconnect (window_src->window_actor, + window_src->actor_painted_handler_id); + window_src->actor_painted_handler_id = 0; + + if (window_src->actor_destroyed_handler_id) + g_signal_handler_disconnect (window_src->window_actor, + window_src->actor_destroyed_handler_id); + window_src->actor_destroyed_handler_id = 0; +} + +static void +window_actor_painted (MetaWindowActor *actor, + MetaScreenCastWindowStreamSrc *window_src) +{ + MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); + + meta_screen_cast_stream_src_maybe_record_frame (src); +} + +static void +window_actor_destroyed (MetaWindowActor *actor, + MetaScreenCastWindowStreamSrc *window_src) +{ + meta_screen_cast_window_stream_src_stop (window_src); + window_src->window_actor = NULL; +} + +static void +meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + MetaWindowActor *window_actor; + + window_actor = meta_window_actor_from_window (get_window (window_src)); + if (!window_actor) + return; + + window_src->window_actor = window_actor; + + window_src->actor_painted_handler_id = + g_signal_connect_after (window_src->window_actor, + "paint", + G_CALLBACK (window_actor_painted), + window_src); + + window_src->actor_destroyed_handler_id = + g_signal_connect (window_src->window_actor, + "destroy", + G_CALLBACK (window_actor_destroyed), + window_src); +} + +static void +meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + + meta_screen_cast_window_stream_src_stop (window_src); +} + +static void +meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src, + uint8_t *data) +{ + MetaScreenCastWindowStreamSrc *window_src = + META_SCREEN_CAST_WINDOW_STREAM_SRC (src); + + capture_into (window_src, data); +} + +MetaScreenCastWindowStreamSrc * +meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stream, + GError **error) +{ + return g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC, NULL, error, + "stream", window_stream, + NULL); +} + +static void +meta_screen_cast_window_stream_src_init (MetaScreenCastWindowStreamSrc *window_src) +{ +} + +static void +meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClass *klass) +{ + MetaScreenCastStreamSrcClass *src_class = + META_SCREEN_CAST_STREAM_SRC_CLASS (klass); + + src_class->get_specs = meta_screen_cast_window_stream_src_get_specs; + src_class->enable = meta_screen_cast_window_stream_src_enable; + src_class->disable = meta_screen_cast_window_stream_src_disable; + src_class->record_frame = meta_screen_cast_window_stream_src_record_frame; + src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; +} diff --git a/src/backends/meta-screen-cast-window-stream-src.h b/src/backends/meta-screen-cast-window-stream-src.h new file mode 100644 index 0000000..37f7869 --- /dev/null +++ b/src/backends/meta-screen-cast-window-stream-src.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2018 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_WINDOW_STREAM_SRC_H +#define META_SCREEN_CAST_WINDOW_STREAM_SRC_H + +#include "backends/meta-screen-cast-stream-src.h" + +typedef struct _MetaScreenCastWindowStream MetaScreenCastWindowStream; + +#define META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC (meta_screen_cast_window_stream_src_get_type ()) +G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStreamSrc, + meta_screen_cast_window_stream_src, + META, SCREEN_CAST_WINDOW_STREAM_SRC, + MetaScreenCastStreamSrc) + +MetaScreenCastWindowStreamSrc * meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stream, + GError **error); + +#endif /* META_SCREEN_CAST_WINDOW_STREAM_SRC_H */ diff --git a/src/backends/meta-screen-cast-window-stream.c b/src/backends/meta-screen-cast-window-stream.c new file mode 100644 index 0000000..1200a39 --- /dev/null +++ b/src/backends/meta-screen-cast-window-stream.c @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2018 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-window-stream.h" + +#include "backends/meta-logical-monitor.h" +#include "backends/meta-monitor-manager-private.h" +#include "backends/meta-screen-cast-window.h" +#include "backends/meta-screen-cast-window-stream-src.h" +#include "compositor/meta-window-actor-private.h" +#include "core/window-private.h" + +enum +{ + PROP_0, + + PROP_WINDOW, +}; + +struct _MetaScreenCastWindowStream +{ + MetaScreenCastStream parent; + + MetaWindow *window; + + int stream_width; + int stream_height; + + unsigned long window_unmanaged_handler_id; +}; + +G_DEFINE_TYPE (MetaScreenCastWindowStream, + meta_screen_cast_window_stream, + META_TYPE_SCREEN_CAST_STREAM) + +MetaWindow * +meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream) +{ + return window_stream->window; +} + +int +meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream) +{ + return window_stream->stream_width; +} + +int +meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream) +{ + return window_stream->stream_height; +} + +MetaScreenCastWindowStream * +meta_screen_cast_window_stream_new (GDBusConnection *connection, + MetaWindow *window, + GError **error) +{ + MetaScreenCastWindowStream *window_stream; + MetaLogicalMonitor *logical_monitor; + int scale; + + logical_monitor = meta_window_get_main_logical_monitor (window); + if (!logical_monitor) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Main logical monitor not found"); + return NULL; + } + + window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM, + NULL, + error, + "connection", connection, + "window", window, + NULL); + if (!window_stream) + return NULL; + + window_stream->window = window; + /* We cannot set the stream size to the exact size of the window, because + * windows can be resized, whereas streams cannot. + * So we set a size equals to the size of the logical monitor for the window. + */ + scale = (int) ceil (meta_logical_monitor_get_scale (logical_monitor)); + window_stream->stream_width = logical_monitor->rect.width * scale; + window_stream->stream_height = logical_monitor->rect.height * scale; + + return window_stream; +} + +static MetaScreenCastStreamSrc * +meta_screen_cast_window_stream_create_src (MetaScreenCastStream *stream, + GError **error) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (stream); + MetaScreenCastWindowStreamSrc *window_stream_src; + + window_stream_src = meta_screen_cast_window_stream_src_new (window_stream, + error); + if (!window_stream_src) + return NULL; + + return META_SCREEN_CAST_STREAM_SRC (window_stream_src); +} + +static void +meta_screen_cast_window_stream_set_parameters (MetaScreenCastStream *stream, + GVariantBuilder *parameters_builder) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (stream); + MetaScreenCastWindow *screen_cast_window = + META_SCREEN_CAST_WINDOW (meta_window_actor_from_window (window_stream->window)); + MetaRectangle bounds; + + meta_screen_cast_window_get_buffer_bounds (screen_cast_window, &bounds); + + g_variant_builder_add (parameters_builder, "{sv}", + "position", + g_variant_new ("(ii)", + bounds.x, bounds.y)); + + g_variant_builder_add (parameters_builder, "{sv}", + "size", + g_variant_new ("(ii)", + bounds.width, + bounds.height)); +} + +static void +meta_screen_cast_window_stream_transform_position (MetaScreenCastStream *stream, + double stream_x, + double stream_y, + double *x, + double *y) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (stream); + MetaScreenCastWindow *screen_cast_window = + META_SCREEN_CAST_WINDOW (meta_window_actor_from_window (window_stream->window)); + + meta_screen_cast_window_transform_relative_position (screen_cast_window, + stream_x, + stream_y, + x, + y); +} + +static void +on_window_unmanaged (MetaScreenCastWindowStream *window_stream) +{ + meta_screen_cast_stream_close (META_SCREEN_CAST_STREAM (window_stream)); +} + +static void +meta_screen_cast_window_stream_constructed (GObject *object) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (object); + + window_stream->window_unmanaged_handler_id = + g_signal_connect_swapped (window_stream->window, "unmanaged", + G_CALLBACK (on_window_unmanaged), + window_stream); + + G_OBJECT_CLASS (meta_screen_cast_window_stream_parent_class)->constructed (object); +} + +static void +meta_screen_cast_window_stream_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (object); + + switch (prop_id) + { + case PROP_WINDOW: + window_stream->window = g_value_get_object (value); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meta_screen_cast_window_stream_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (object); + + switch (prop_id) + { + case PROP_WINDOW: + g_value_set_object (value, window_stream->window); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + } +} + +static void +meta_screen_cast_window_stream_finalize (GObject *object) +{ + MetaScreenCastWindowStream *window_stream = + META_SCREEN_CAST_WINDOW_STREAM (object); + + g_signal_handler_disconnect (window_stream->window, + window_stream->window_unmanaged_handler_id); + + G_OBJECT_CLASS (meta_screen_cast_window_stream_parent_class)->finalize (object); +} + +static void +meta_screen_cast_window_stream_init (MetaScreenCastWindowStream *window_stream) +{ +} + +static void +meta_screen_cast_window_stream_class_init (MetaScreenCastWindowStreamClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + MetaScreenCastStreamClass *stream_class = + META_SCREEN_CAST_STREAM_CLASS (klass); + + object_class->constructed = meta_screen_cast_window_stream_constructed; + object_class->set_property = meta_screen_cast_window_stream_set_property; + object_class->get_property = meta_screen_cast_window_stream_get_property; + object_class->finalize = meta_screen_cast_window_stream_finalize; + + stream_class->create_src = meta_screen_cast_window_stream_create_src; + stream_class->set_parameters = meta_screen_cast_window_stream_set_parameters; + stream_class->transform_position = meta_screen_cast_window_stream_transform_position; + + g_object_class_install_property (object_class, + PROP_WINDOW, + g_param_spec_object ("window", + "window", + "MetaWindow", + META_TYPE_WINDOW, + G_PARAM_READWRITE | + G_PARAM_CONSTRUCT_ONLY | + G_PARAM_STATIC_STRINGS)); +} diff --git a/src/backends/meta-screen-cast-window-stream.h b/src/backends/meta-screen-cast-window-stream.h new file mode 100644 index 0000000..6726ef8 --- /dev/null +++ b/src/backends/meta-screen-cast-window-stream.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2018 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_WINDOW_STREAM_H +#define META_SCREEN_CAST_WINDOW_STREAM_H + +#include + +#include "backends/meta-screen-cast-stream.h" +#include "meta/window.h" + +#define META_TYPE_SCREEN_CAST_WINDOW_STREAM (meta_screen_cast_window_stream_get_type ()) +G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream, + meta_screen_cast_window_stream, + META, SCREEN_CAST_WINDOW_STREAM, + MetaScreenCastStream) + +MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (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); +int meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream); + +#endif /* META_SCREEN_CAST_WINDOW_STREAM_H */ -- 2.19.2