mirror of
https://github.com/brl/mutter.git
synced 2024-11-21 15:40:41 -05:00
screen-cast-cursor-side-channel.patch
This commit is contained in:
parent
efd7b8987f
commit
e3a42dc873
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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"])
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -30,6 +30,18 @@
|
||||
#include <meta/screen.h>
|
||||
#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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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 */
|
||||
|
@ -24,23 +24,38 @@
|
||||
|
||||
#include "backends/meta-screen-cast-monitor-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-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;
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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 =
|
||||
|
@ -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 */
|
||||
|
@ -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 *
|
||||
|
@ -24,10 +24,26 @@
|
||||
#define META_SCREEN_CAST_STREAM_SRC_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 "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 */
|
||||
|
@ -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,
|
||||
|
@ -26,8 +26,12 @@
|
||||
#include <glib-object.h>
|
||||
|
||||
#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 */
|
||||
|
@ -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 *
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
@ -25,9 +25,17 @@
|
||||
|
||||
#include <glib-object.h>
|
||||
|
||||
#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 */
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
-->
|
||||
<method name="RecordMonitor">
|
||||
<arg name="connector" type="s" direction="in" />
|
||||
@ -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:
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user