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_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:
|
* clutter_stage_queue_redraw:
|
||||||
* @stage: the #ClutterStage
|
* @stage: the #ClutterStage
|
||||||
|
@ -250,6 +250,9 @@ void clutter_stage_ensure_viewport (ClutterStage
|
|||||||
CLUTTER_AVAILABLE_IN_ALL
|
CLUTTER_AVAILABLE_IN_ALL
|
||||||
void clutter_stage_ensure_redraw (ClutterStage *stage);
|
void clutter_stage_ensure_redraw (ClutterStage *stage);
|
||||||
|
|
||||||
|
CLUTTER_AVAILABLE_IN_ALL
|
||||||
|
gboolean clutter_stage_is_redraw_queued (ClutterStage *stage);
|
||||||
|
|
||||||
#ifdef CLUTTER_ENABLE_EXPERIMENTAL_API
|
#ifdef CLUTTER_ENABLE_EXPERIMENTAL_API
|
||||||
CLUTTER_AVAILABLE_IN_1_14
|
CLUTTER_AVAILABLE_IN_1_14
|
||||||
void clutter_stage_set_sync_delay (ClutterStage *stage,
|
void clutter_stage_set_sync_delay (ClutterStage *stage,
|
||||||
|
@ -245,7 +245,7 @@ AC_ARG_ENABLE(remote-desktop,
|
|||||||
enable_remote_desktop=no
|
enable_remote_desktop=no
|
||||||
)
|
)
|
||||||
AS_IF([test "$enable_remote_desktop" = "yes"], [
|
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])
|
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"])
|
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 =
|
priv->remote_access_controller =
|
||||||
g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL);
|
g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL);
|
||||||
priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, 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);
|
priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher);
|
||||||
#endif /* HAVE_REMOTE_DESKTOP */
|
#endif /* HAVE_REMOTE_DESKTOP */
|
||||||
|
|
||||||
|
@ -35,6 +35,9 @@
|
|||||||
|
|
||||||
#include "meta-stage-private.h"
|
#include "meta-stage-private.h"
|
||||||
|
|
||||||
|
G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor,
|
||||||
|
G_TYPE_OBJECT)
|
||||||
|
|
||||||
struct _MetaCursorRendererPrivate
|
struct _MetaCursorRendererPrivate
|
||||||
{
|
{
|
||||||
float current_x;
|
float current_x;
|
||||||
@ -44,6 +47,8 @@ struct _MetaCursorRendererPrivate
|
|||||||
MetaOverlay *stage_overlay;
|
MetaOverlay *stage_overlay;
|
||||||
gboolean handled_by_backend;
|
gboolean handled_by_backend;
|
||||||
guint post_paint_func_id;
|
guint post_paint_func_id;
|
||||||
|
|
||||||
|
GList *hw_cursor_inhibitors;
|
||||||
};
|
};
|
||||||
typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate;
|
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);
|
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
|
void
|
||||||
meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer,
|
meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer,
|
||||||
MetaCursorSprite *cursor_sprite)
|
MetaCursorSprite *cursor_sprite)
|
||||||
@ -283,3 +303,45 @@ meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer)
|
|||||||
|
|
||||||
return priv->displayed_cursor;
|
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/screen.h>
|
||||||
#include "meta-cursor.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 ())
|
#define META_TYPE_CURSOR_RENDERER (meta_cursor_renderer_get_type ())
|
||||||
G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer,
|
G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer,
|
||||||
META, CURSOR_RENDERER, GObject);
|
META, CURSOR_RENDERER, GObject);
|
||||||
@ -55,6 +67,15 @@ void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer);
|
|||||||
|
|
||||||
MetaCursorSprite * meta_cursor_renderer_get_cursor (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,
|
ClutterRect meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer,
|
||||||
MetaCursorSprite *cursor_sprite);
|
MetaCursorSprite *cursor_sprite);
|
||||||
|
|
||||||
|
@ -48,6 +48,7 @@ G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT);
|
|||||||
|
|
||||||
enum {
|
enum {
|
||||||
CURSOR_CHANGED,
|
CURSOR_CHANGED,
|
||||||
|
CURSOR_MOVED,
|
||||||
LAST_SIGNAL
|
LAST_SIGNAL
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -117,11 +118,15 @@ change_cursor_renderer (MetaCursorTracker *tracker)
|
|||||||
static void
|
static void
|
||||||
sync_cursor (MetaCursorTracker *tracker)
|
sync_cursor (MetaCursorTracker *tracker)
|
||||||
{
|
{
|
||||||
if (update_displayed_cursor (tracker))
|
gboolean cursor_changed = FALSE;
|
||||||
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
|
||||||
|
cursor_changed = update_displayed_cursor (tracker);
|
||||||
|
|
||||||
if (update_effective_cursor (tracker))
|
if (update_effective_cursor (tracker))
|
||||||
change_cursor_renderer (tracker);
|
change_cursor_renderer (tracker);
|
||||||
|
|
||||||
|
if (cursor_changed)
|
||||||
|
g_signal_emit (tracker, signals[CURSOR_CHANGED], 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -158,6 +163,15 @@ meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass)
|
|||||||
0,
|
0,
|
||||||
NULL, NULL, NULL,
|
NULL, NULL, NULL,
|
||||||
G_TYPE_NONE, 0);
|
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 ());
|
g_assert (meta_is_wayland_compositor ());
|
||||||
|
|
||||||
meta_cursor_renderer_set_position (cursor_renderer, new_x, new_y);
|
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
|
static void
|
||||||
|
@ -94,6 +94,24 @@ meta_renderer_get_views (MetaRenderer *renderer)
|
|||||||
return priv->views;
|
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
|
static void
|
||||||
meta_renderer_finalize (GObject *object)
|
meta_renderer_finalize (GObject *object)
|
||||||
{
|
{
|
||||||
|
@ -53,4 +53,7 @@ void meta_renderer_set_legacy_view (MetaRenderer *renderer,
|
|||||||
|
|
||||||
GList * meta_renderer_get_views (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 */
|
#endif /* META_RENDERER_H */
|
||||||
|
@ -24,23 +24,38 @@
|
|||||||
|
|
||||||
#include "backends/meta-screen-cast-monitor-stream-src.h"
|
#include "backends/meta-screen-cast-monitor-stream-src.h"
|
||||||
|
|
||||||
|
#include <spa/buffer/meta.h>
|
||||||
|
|
||||||
#include "backends/meta-backend-private.h"
|
#include "backends/meta-backend-private.h"
|
||||||
|
#include "backends/meta-cursor-tracker-private.h"
|
||||||
#include "backends/meta-screen-cast-monitor-stream.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-logical-monitor.h"
|
||||||
#include "backends/meta-monitor.h"
|
#include "backends/meta-monitor.h"
|
||||||
#include "clutter/clutter.h"
|
#include "clutter/clutter.h"
|
||||||
#include "clutter/clutter-mutter.h"
|
#include "clutter/clutter-mutter.h"
|
||||||
|
#include "core/boxes-private.h"
|
||||||
|
|
||||||
struct _MetaScreenCastMonitorStreamSrc
|
struct _MetaScreenCastMonitorStreamSrc
|
||||||
{
|
{
|
||||||
MetaScreenCastStreamSrc parent;
|
MetaScreenCastStreamSrc parent;
|
||||||
|
|
||||||
gulong 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,
|
static void
|
||||||
meta_screen_cast_monitor_stream_src,
|
hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface);
|
||||||
META_TYPE_SCREEN_CAST_STREAM_SRC)
|
|
||||||
|
G_DEFINE_TYPE_WITH_CODE (MetaScreenCastMonitorStreamSrc,
|
||||||
|
meta_screen_cast_monitor_stream_src,
|
||||||
|
META_TYPE_SCREEN_CAST_STREAM_SRC,
|
||||||
|
G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR,
|
||||||
|
hw_cursor_inhibitor_iface_init))
|
||||||
|
|
||||||
static ClutterStage *
|
static ClutterStage *
|
||||||
get_stage (MetaScreenCastMonitorStreamSrc *monitor_src)
|
get_stage (MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
@ -102,18 +117,164 @@ stage_painted (ClutterActor *actor,
|
|||||||
meta_screen_cast_stream_src_maybe_record_frame (src);
|
meta_screen_cast_stream_src_maybe_record_frame (src);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static MetaBackend *
|
||||||
|
get_backend (MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
|
||||||
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||||
|
MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream);
|
||||||
|
MetaScreenCast *screen_cast =
|
||||||
|
meta_screen_cast_session_get_screen_cast (session);
|
||||||
|
|
||||||
|
return meta_screen_cast_get_backend (screen_cast);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_cursor_in_stream (MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
|
{
|
||||||
|
MetaBackend *backend = get_backend (monitor_src);
|
||||||
|
MetaCursorRenderer *cursor_renderer =
|
||||||
|
meta_backend_get_cursor_renderer (backend);
|
||||||
|
MetaMonitor *monitor;
|
||||||
|
MetaLogicalMonitor *logical_monitor;
|
||||||
|
MetaRectangle logical_monitor_layout;
|
||||||
|
ClutterRect logical_monitor_rect;
|
||||||
|
MetaCursorSprite *cursor_sprite;
|
||||||
|
|
||||||
|
monitor = get_monitor (monitor_src);
|
||||||
|
logical_monitor = meta_monitor_get_logical_monitor (monitor);
|
||||||
|
logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
|
||||||
|
logical_monitor_rect =
|
||||||
|
meta_rectangle_to_clutter_rect (&logical_monitor_layout);
|
||||||
|
|
||||||
|
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
|
||||||
|
if (cursor_sprite)
|
||||||
|
{
|
||||||
|
ClutterRect cursor_rect;
|
||||||
|
|
||||||
|
cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer,
|
||||||
|
cursor_sprite);
|
||||||
|
return clutter_rect_intersection (&cursor_rect,
|
||||||
|
&logical_monitor_rect,
|
||||||
|
NULL);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClutterPoint cursor_position;
|
||||||
|
|
||||||
|
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
|
||||||
|
return clutter_rect_contains_point (&logical_monitor_rect,
|
||||||
|
&cursor_position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src);
|
||||||
|
ClutterStage *stage = get_stage (monitor_src);
|
||||||
|
|
||||||
|
if (!is_cursor_in_stream (monitor_src))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (clutter_stage_is_redraw_queued (stage))
|
||||||
|
return;
|
||||||
|
|
||||||
|
meta_screen_cast_stream_src_maybe_record_frame (src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cursor_moved (MetaCursorTracker *cursor_tracker,
|
||||||
|
float x,
|
||||||
|
float y,
|
||||||
|
MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
|
{
|
||||||
|
sync_cursor_state (monitor_src);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
cursor_changed (MetaCursorTracker *cursor_tracker,
|
||||||
|
MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
|
{
|
||||||
|
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
|
static void
|
||||||
meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
|
meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src)
|
||||||
{
|
{
|
||||||
MetaScreenCastMonitorStreamSrc *monitor_src =
|
MetaScreenCastMonitorStreamSrc *monitor_src =
|
||||||
META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
|
META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
|
||||||
|
MetaBackend *backend = get_backend (monitor_src);
|
||||||
|
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
|
||||||
ClutterStage *stage;
|
ClutterStage *stage;
|
||||||
|
MetaScreenCastStream *stream;
|
||||||
|
|
||||||
|
stream = meta_screen_cast_stream_src_get_stream (src);
|
||||||
stage = get_stage (monitor_src);
|
stage = get_stage (monitor_src);
|
||||||
monitor_src->stage_painted_handler_id =
|
|
||||||
g_signal_connect_after (stage, "paint",
|
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
||||||
G_CALLBACK (stage_painted),
|
{
|
||||||
monitor_src);
|
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||||
|
monitor_src->cursor_moved_handler_id =
|
||||||
|
g_signal_connect_after (cursor_tracker, "cursor-moved",
|
||||||
|
G_CALLBACK (cursor_moved),
|
||||||
|
monitor_src);
|
||||||
|
monitor_src->cursor_changed_handler_id =
|
||||||
|
g_signal_connect_after (cursor_tracker, "cursor-changed",
|
||||||
|
G_CALLBACK (cursor_changed),
|
||||||
|
monitor_src);
|
||||||
|
/* Intentional fall-through */
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||||
|
monitor_src->actors_painted_handler_id =
|
||||||
|
g_signal_connect (stage, "actors-painted",
|
||||||
|
G_CALLBACK (stage_painted),
|
||||||
|
monitor_src);
|
||||||
|
break;
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||||
|
inhibit_hw_cursor (monitor_src);
|
||||||
|
monitor_src->paint_handler_id =
|
||||||
|
g_signal_connect_after (stage, "paint",
|
||||||
|
G_CALLBACK (stage_painted),
|
||||||
|
monitor_src);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
clutter_actor_queue_redraw (CLUTTER_ACTOR (stage));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -122,14 +283,43 @@ meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src)
|
|||||||
{
|
{
|
||||||
MetaScreenCastMonitorStreamSrc *monitor_src =
|
MetaScreenCastMonitorStreamSrc *monitor_src =
|
||||||
META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
|
META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
|
||||||
|
MetaBackend *backend = get_backend (monitor_src);
|
||||||
|
MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend);
|
||||||
ClutterStage *stage;
|
ClutterStage *stage;
|
||||||
|
|
||||||
stage = get_stage (monitor_src);
|
stage = get_stage (monitor_src);
|
||||||
g_signal_handler_disconnect (stage, monitor_src->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,
|
meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
||||||
uint8_t *data)
|
uint8_t *data)
|
||||||
{
|
{
|
||||||
@ -140,9 +330,226 @@ meta_screen_cast_monitor_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
|||||||
MetaLogicalMonitor *logical_monitor;
|
MetaLogicalMonitor *logical_monitor;
|
||||||
|
|
||||||
stage = get_stage (monitor_src);
|
stage = get_stage (monitor_src);
|
||||||
|
if (!clutter_stage_is_redraw_queued (stage))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
monitor = get_monitor (monitor_src);
|
monitor = get_monitor (monitor_src);
|
||||||
logical_monitor = meta_monitor_get_logical_monitor (monitor);
|
logical_monitor = meta_monitor_get_logical_monitor (monitor);
|
||||||
clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data);
|
clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
draw_cursor_sprite_via_offscreen (MetaScreenCastMonitorStreamSrc *monitor_src,
|
||||||
|
CoglTexture *cursor_texture,
|
||||||
|
int bitmap_width,
|
||||||
|
int bitmap_height,
|
||||||
|
uint32_t *bitmap_data,
|
||||||
|
GError **error)
|
||||||
|
{
|
||||||
|
MetaBackend *backend = get_backend (monitor_src);
|
||||||
|
ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend);
|
||||||
|
CoglContext *cogl_context =
|
||||||
|
clutter_backend_get_cogl_context (clutter_backend);
|
||||||
|
CoglTexture2D *bitmap_texture;
|
||||||
|
CoglOffscreen *offscreen;
|
||||||
|
CoglFramebuffer *fb;
|
||||||
|
CoglPipeline *pipeline;
|
||||||
|
CoglColor clear_color;
|
||||||
|
|
||||||
|
bitmap_texture = cogl_texture_2d_new_with_size (cogl_context,
|
||||||
|
bitmap_width, bitmap_height);
|
||||||
|
cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture),
|
||||||
|
FALSE);
|
||||||
|
if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error))
|
||||||
|
{
|
||||||
|
cogl_object_unref (bitmap_texture);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture));
|
||||||
|
fb = COGL_FRAMEBUFFER (offscreen);
|
||||||
|
cogl_object_unref (bitmap_texture);
|
||||||
|
if (!cogl_framebuffer_allocate (fb, error))
|
||||||
|
{
|
||||||
|
cogl_object_unref (fb);
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
pipeline = cogl_pipeline_new (cogl_context);
|
||||||
|
cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture);
|
||||||
|
cogl_pipeline_set_layer_filters (pipeline, 0,
|
||||||
|
COGL_PIPELINE_FILTER_LINEAR,
|
||||||
|
COGL_PIPELINE_FILTER_LINEAR);
|
||||||
|
cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0);
|
||||||
|
cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color);
|
||||||
|
cogl_framebuffer_draw_rectangle (fb, pipeline,
|
||||||
|
-1, 1, 1, -1);
|
||||||
|
cogl_object_unref (pipeline);
|
||||||
|
|
||||||
|
cogl_framebuffer_read_pixels (fb,
|
||||||
|
0, 0,
|
||||||
|
bitmap_width, bitmap_height,
|
||||||
|
COGL_PIXEL_FORMAT_RGBA_8888_PRE,
|
||||||
|
(uint8_t *) bitmap_data);
|
||||||
|
cogl_object_unref (fb);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
|
||||||
|
struct spa_meta_cursor *spa_meta_cursor)
|
||||||
|
{
|
||||||
|
MetaScreenCastMonitorStreamSrc *monitor_src =
|
||||||
|
META_SCREEN_CAST_MONITOR_STREAM_SRC (src);
|
||||||
|
MetaBackend *backend = get_backend (monitor_src);
|
||||||
|
MetaCursorRenderer *cursor_renderer =
|
||||||
|
meta_backend_get_cursor_renderer (backend);
|
||||||
|
MetaRenderer *renderer = meta_backend_get_renderer (backend);
|
||||||
|
MetaSpaType *spa_type = meta_screen_cast_stream_src_get_spa_type (src);
|
||||||
|
GError *error = NULL;
|
||||||
|
MetaCursorSprite *cursor_sprite;
|
||||||
|
CoglTexture *cursor_texture;
|
||||||
|
MetaMonitor *monitor;
|
||||||
|
MetaLogicalMonitor *logical_monitor;
|
||||||
|
MetaRectangle logical_monitor_layout;
|
||||||
|
ClutterRect logical_monitor_rect;
|
||||||
|
MetaRendererView *view;
|
||||||
|
float view_scale;
|
||||||
|
ClutterPoint cursor_position;
|
||||||
|
struct spa_meta_bitmap *spa_meta_bitmap;
|
||||||
|
|
||||||
|
cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer);
|
||||||
|
if (cursor_sprite)
|
||||||
|
cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite);
|
||||||
|
else
|
||||||
|
cursor_texture = NULL;
|
||||||
|
|
||||||
|
if (!is_cursor_in_stream (monitor_src))
|
||||||
|
{
|
||||||
|
spa_meta_cursor->id = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
monitor = get_monitor (monitor_src);
|
||||||
|
logical_monitor = meta_monitor_get_logical_monitor (monitor);
|
||||||
|
logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor);
|
||||||
|
logical_monitor_rect =
|
||||||
|
meta_rectangle_to_clutter_rect (&logical_monitor_layout);
|
||||||
|
|
||||||
|
view = meta_renderer_get_view_from_logical_monitor (renderer,
|
||||||
|
logical_monitor);
|
||||||
|
if (view)
|
||||||
|
view_scale = clutter_stage_view_get_scale (CLUTTER_STAGE_VIEW (view));
|
||||||
|
else
|
||||||
|
view_scale = 1.0;
|
||||||
|
|
||||||
|
cursor_position = meta_cursor_renderer_get_position (cursor_renderer);
|
||||||
|
cursor_position.x -= logical_monitor_rect.origin.x;
|
||||||
|
cursor_position.y -= logical_monitor_rect.origin.y;
|
||||||
|
cursor_position.x *= view_scale;
|
||||||
|
cursor_position.y *= view_scale;
|
||||||
|
|
||||||
|
spa_meta_cursor->id = 1;
|
||||||
|
spa_meta_cursor->position.x = (int32_t) roundf (cursor_position.x);
|
||||||
|
spa_meta_cursor->position.y = (int32_t) roundf (cursor_position.y);
|
||||||
|
|
||||||
|
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 *
|
MetaScreenCastMonitorStreamSrc *
|
||||||
@ -157,6 +564,7 @@ meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream *monitor_s
|
|||||||
static void
|
static void
|
||||||
meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src)
|
meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src)
|
||||||
{
|
{
|
||||||
|
monitor_src->cursor_bitmap_invalid = TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
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->enable = meta_screen_cast_monitor_stream_src_enable;
|
||||||
src_class->disable = meta_screen_cast_monitor_stream_src_disable;
|
src_class->disable = meta_screen_cast_monitor_stream_src_disable;
|
||||||
src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame;
|
src_class->record_frame = meta_screen_cast_monitor_stream_src_record_frame;
|
||||||
|
src_class->set_cursor_metadata =
|
||||||
|
meta_screen_cast_monitor_stream_src_set_cursor_metadata;
|
||||||
}
|
}
|
||||||
|
@ -105,12 +105,15 @@ meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monito
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetaScreenCastMonitorStream *
|
MetaScreenCastMonitorStream *
|
||||||
meta_screen_cast_monitor_stream_new (GDBusConnection *connection,
|
meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session,
|
||||||
MetaMonitorManager *monitor_manager,
|
GDBusConnection *connection,
|
||||||
MetaMonitor *monitor,
|
MetaMonitor *monitor,
|
||||||
ClutterStage *stage,
|
ClutterStage *stage,
|
||||||
GError **error)
|
MetaScreenCastCursorMode cursor_mode,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
|
MetaGpu *gpu = meta_monitor_get_gpu (monitor);
|
||||||
|
MetaMonitorManager *monitor_manager = meta_gpu_get_monitor_manager (gpu);
|
||||||
MetaScreenCastMonitorStream *monitor_stream;
|
MetaScreenCastMonitorStream *monitor_stream;
|
||||||
|
|
||||||
if (!meta_monitor_is_active (monitor))
|
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,
|
monitor_stream = g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM,
|
||||||
NULL,
|
NULL,
|
||||||
error,
|
error,
|
||||||
|
"session", session,
|
||||||
"connection", connection,
|
"connection", connection,
|
||||||
|
"cursor-mode", cursor_mode,
|
||||||
"monitor", monitor,
|
"monitor", monitor,
|
||||||
NULL);
|
NULL);
|
||||||
if (!monitor_stream)
|
if (!monitor_stream)
|
||||||
|
@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
#include "backends/meta-monitor-manager-private.h"
|
#include "backends/meta-monitor-manager-private.h"
|
||||||
#include "backends/meta-screen-cast-stream.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 ())
|
#define META_TYPE_SCREEN_CAST_MONITOR_STREAM (meta_screen_cast_monitor_stream_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
|
G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
|
||||||
@ -34,11 +35,12 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream,
|
|||||||
META, SCREEN_CAST_MONITOR_STREAM,
|
META, SCREEN_CAST_MONITOR_STREAM,
|
||||||
MetaScreenCastStream)
|
MetaScreenCastStream)
|
||||||
|
|
||||||
MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (GDBusConnection *connection,
|
MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session,
|
||||||
MetaMonitorManager *monitor_manager,
|
GDBusConnection *connection,
|
||||||
MetaMonitor *monitor,
|
MetaMonitor *monitor,
|
||||||
ClutterStage *stage,
|
ClutterStage *stage,
|
||||||
GError **error);
|
MetaScreenCastCursorMode cursor_mode,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
|
ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream);
|
||||||
|
|
||||||
|
@ -38,6 +38,8 @@ struct _MetaScreenCastSession
|
|||||||
{
|
{
|
||||||
MetaDBusScreenCastSessionSkeleton parent;
|
MetaDBusScreenCastSessionSkeleton parent;
|
||||||
|
|
||||||
|
MetaScreenCast *screen_cast;
|
||||||
|
|
||||||
char *peer_name;
|
char *peer_name;
|
||||||
|
|
||||||
MetaScreenCastSessionType session_type;
|
MetaScreenCastSessionType session_type;
|
||||||
@ -159,6 +161,12 @@ meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaScreenCast *
|
||||||
|
meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session)
|
||||||
|
{
|
||||||
|
return session->screen_cast;
|
||||||
|
}
|
||||||
|
|
||||||
char *
|
char *
|
||||||
meta_screen_cast_session_get_object_path (MetaScreenCastSession *session)
|
meta_screen_cast_session_get_object_path (MetaScreenCastSession *session)
|
||||||
{
|
{
|
||||||
@ -254,6 +262,20 @@ on_stream_closed (MetaScreenCastStream *stream,
|
|||||||
meta_screen_cast_session_close (session);
|
meta_screen_cast_session_close (session);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode)
|
||||||
|
{
|
||||||
|
switch (cursor_mode)
|
||||||
|
{
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
handle_record_monitor (MetaDBusScreenCastSession *skeleton,
|
handle_record_monitor (MetaDBusScreenCastSession *skeleton,
|
||||||
GDBusMethodInvocation *invocation,
|
GDBusMethodInvocation *invocation,
|
||||||
@ -267,6 +289,7 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
|
|||||||
MetaMonitorManager *monitor_manager =
|
MetaMonitorManager *monitor_manager =
|
||||||
meta_backend_get_monitor_manager (backend);
|
meta_backend_get_monitor_manager (backend);
|
||||||
MetaMonitor *monitor;
|
MetaMonitor *monitor;
|
||||||
|
MetaScreenCastCursorMode cursor_mode;
|
||||||
ClutterStage *stage;
|
ClutterStage *stage;
|
||||||
GError *error = NULL;
|
GError *error = NULL;
|
||||||
MetaScreenCastMonitorStream *monitor_stream;
|
MetaScreenCastMonitorStream *monitor_stream;
|
||||||
@ -298,12 +321,28 @@ handle_record_monitor (MetaDBusScreenCastSession *skeleton,
|
|||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode))
|
||||||
|
{
|
||||||
|
cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (!is_valid_cursor_mode (cursor_mode))
|
||||||
|
{
|
||||||
|
g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR,
|
||||||
|
G_DBUS_ERROR_FAILED,
|
||||||
|
"Unknown cursor mode");
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
stage = CLUTTER_STAGE (meta_backend_get_stage (backend));
|
||||||
|
|
||||||
monitor_stream = meta_screen_cast_monitor_stream_new (connection,
|
monitor_stream = meta_screen_cast_monitor_stream_new (session,
|
||||||
monitor_manager,
|
connection,
|
||||||
monitor,
|
monitor,
|
||||||
stage,
|
stage,
|
||||||
|
cursor_mode,
|
||||||
&error);
|
&error);
|
||||||
if (!monitor_stream)
|
if (!monitor_stream)
|
||||||
{
|
{
|
||||||
@ -382,7 +421,8 @@ handle_record_window (MetaDBusScreenCastSession *skeleton,
|
|||||||
interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
|
interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton);
|
||||||
connection = g_dbus_interface_skeleton_get_connection (interface_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,
|
window,
|
||||||
&error);
|
&error);
|
||||||
if (!window_stream)
|
if (!window_stream)
|
||||||
@ -442,6 +482,7 @@ meta_screen_cast_session_new (MetaScreenCast *screen_cast,
|
|||||||
static unsigned int global_session_number = 0;
|
static unsigned int global_session_number = 0;
|
||||||
|
|
||||||
session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL);
|
session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL);
|
||||||
|
session->screen_cast = screen_cast;
|
||||||
session->session_type = session_type;
|
session->session_type = session_type;
|
||||||
session->peer_name = g_strdup (peer_name);
|
session->peer_name = g_strdup (peer_name);
|
||||||
session->object_path =
|
session->object_path =
|
||||||
|
@ -60,4 +60,6 @@ void meta_screen_cast_session_close (MetaScreenCastSession *session);
|
|||||||
MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
|
MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session,
|
||||||
const char *path);
|
const char *path);
|
||||||
|
|
||||||
|
MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session);
|
||||||
|
|
||||||
#endif /* META_SCREEN_CAST_SESSION_H */
|
#endif /* META_SCREEN_CAST_SESSION_H */
|
||||||
|
@ -40,6 +40,10 @@
|
|||||||
#define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
|
#define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \
|
||||||
(TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name))
|
(TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name))
|
||||||
|
|
||||||
|
#define CURSOR_META_SIZE(width, height) \
|
||||||
|
(sizeof (struct spa_meta_cursor) + \
|
||||||
|
sizeof (struct spa_meta_bitmap) + width * height * 4)
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
@ -57,14 +61,6 @@ enum
|
|||||||
|
|
||||||
static guint signals[N_SIGNALS];
|
static guint signals[N_SIGNALS];
|
||||||
|
|
||||||
typedef struct _MetaSpaType
|
|
||||||
{
|
|
||||||
struct spa_type_media_type media_type;
|
|
||||||
struct spa_type_media_subtype media_subtype;
|
|
||||||
struct spa_type_format_video format_video;
|
|
||||||
struct spa_type_video_format video_format;
|
|
||||||
} MetaSpaType;
|
|
||||||
|
|
||||||
typedef struct _MetaPipeWireSource
|
typedef struct _MetaPipeWireSource
|
||||||
{
|
{
|
||||||
GSource base;
|
GSource base;
|
||||||
@ -133,14 +129,68 @@ meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src,
|
|||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
meta_screen_cast_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
||||||
uint8_t *data)
|
uint8_t *data)
|
||||||
{
|
{
|
||||||
MetaScreenCastStreamSrcClass *klass =
|
MetaScreenCastStreamSrcClass *klass =
|
||||||
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
||||||
|
|
||||||
klass->record_frame (src, data);
|
return klass->record_frame (src, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src,
|
||||||
|
struct spa_meta_cursor *spa_meta_cursor)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamSrcClass *klass =
|
||||||
|
META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src);
|
||||||
|
|
||||||
|
if (klass->set_cursor_metadata)
|
||||||
|
klass->set_cursor_metadata (src, spa_meta_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
MetaSpaType *
|
||||||
|
meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamSrcPrivate *priv =
|
||||||
|
meta_screen_cast_stream_src_get_instance_private (src);
|
||||||
|
|
||||||
|
return &priv->spa_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
add_cursor_metadata (MetaScreenCastStreamSrc *src,
|
||||||
|
struct spa_buffer *spa_buffer)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamSrcPrivate *priv =
|
||||||
|
meta_screen_cast_stream_src_get_instance_private (src);
|
||||||
|
MetaSpaType *spa_type = &priv->spa_type;
|
||||||
|
struct spa_meta_cursor *spa_meta_cursor;
|
||||||
|
|
||||||
|
spa_meta_cursor = spa_buffer_find_meta (spa_buffer, spa_type->meta_cursor);
|
||||||
|
if (spa_meta_cursor)
|
||||||
|
meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_record_cursor (MetaScreenCastStreamSrc *src,
|
||||||
|
struct spa_buffer *spa_buffer,
|
||||||
|
uint8_t *data)
|
||||||
|
{
|
||||||
|
MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src);
|
||||||
|
|
||||||
|
switch (meta_screen_cast_stream_get_cursor_mode (stream))
|
||||||
|
{
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_HIDDEN:
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED:
|
||||||
|
return;
|
||||||
|
case META_SCREEN_CAST_CURSOR_MODE_METADATA:
|
||||||
|
add_cursor_metadata (src, spa_buffer);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_assert_not_reached ();
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@ -151,7 +201,6 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
|
|||||||
MetaRectangle crop_rect;
|
MetaRectangle crop_rect;
|
||||||
struct pw_buffer *buffer;
|
struct pw_buffer *buffer;
|
||||||
struct spa_buffer *spa_buffer;
|
struct spa_buffer *spa_buffer;
|
||||||
struct spa_meta_video_crop *spa_meta_video_crop;
|
|
||||||
uint8_t *map = NULL;
|
uint8_t *map = NULL;
|
||||||
uint8_t *data;
|
uint8_t *data;
|
||||||
uint64_t now_us;
|
uint64_t now_us;
|
||||||
@ -199,35 +248,45 @@ meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src)
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
meta_screen_cast_stream_src_record_frame (src, data);
|
if (meta_screen_cast_stream_src_record_frame (src, data))
|
||||||
|
|
||||||
/* Update VideoCrop if needed */
|
|
||||||
spa_meta_video_crop = spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
|
|
||||||
if (spa_meta_video_crop)
|
|
||||||
{
|
{
|
||||||
if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
|
struct spa_meta_video_crop *spa_meta_video_crop;
|
||||||
|
|
||||||
|
spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
|
||||||
|
|
||||||
|
/* Update VideoCrop if needed */
|
||||||
|
spa_meta_video_crop =
|
||||||
|
spa_buffer_find_meta (spa_buffer, priv->pipewire_type->meta.VideoCrop);
|
||||||
|
if (spa_meta_video_crop)
|
||||||
{
|
{
|
||||||
spa_meta_video_crop->x = crop_rect.x;
|
if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect))
|
||||||
spa_meta_video_crop->y = crop_rect.y;
|
{
|
||||||
spa_meta_video_crop->width = crop_rect.width;
|
spa_meta_video_crop->x = crop_rect.x;
|
||||||
spa_meta_video_crop->height = crop_rect.height;
|
spa_meta_video_crop->y = crop_rect.y;
|
||||||
}
|
spa_meta_video_crop->width = crop_rect.width;
|
||||||
else
|
spa_meta_video_crop->height = crop_rect.height;
|
||||||
{
|
}
|
||||||
spa_meta_video_crop->x = 0;
|
else
|
||||||
spa_meta_video_crop->y = 0;
|
{
|
||||||
spa_meta_video_crop->width = priv->stream_width;
|
spa_meta_video_crop->x = 0;
|
||||||
spa_meta_video_crop->height = priv->stream_height;
|
spa_meta_video_crop->y = 0;
|
||||||
|
spa_meta_video_crop->width = priv->stream_width;
|
||||||
|
spa_meta_video_crop->height = priv->stream_height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
spa_buffer->datas[0].chunk->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
maybe_record_cursor (src, spa_buffer, data);
|
||||||
|
|
||||||
priv->last_frame_timestamp_us = now_us;
|
priv->last_frame_timestamp_us = now_us;
|
||||||
|
|
||||||
if (map)
|
if (map)
|
||||||
munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset);
|
munmap (map, spa_buffer->datas[0].maxsize + spa_buffer->datas[0].mapoffset);
|
||||||
|
|
||||||
spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize;
|
|
||||||
|
|
||||||
pw_stream_queue_buffer (priv->pipewire_stream, buffer);
|
pw_stream_queue_buffer (priv->pipewire_stream, buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -314,7 +373,7 @@ on_stream_format_changed (void *data,
|
|||||||
uint8_t params_buffer[1024];
|
uint8_t params_buffer[1024];
|
||||||
int32_t width, height, stride, size;
|
int32_t width, height, stride, size;
|
||||||
struct spa_pod_builder pod_builder;
|
struct spa_pod_builder pod_builder;
|
||||||
const struct spa_pod *params[2];
|
const struct spa_pod *params[3];
|
||||||
const int bpp = 4;
|
const int bpp = 4;
|
||||||
|
|
||||||
if (!format)
|
if (!format)
|
||||||
@ -348,6 +407,12 @@ on_stream_format_changed (void *data,
|
|||||||
":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop,
|
":", pipewire_type->param_meta.type, "I", pipewire_type->meta.VideoCrop,
|
||||||
":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop));
|
":", pipewire_type->param_meta.size, "i", sizeof (struct spa_meta_video_crop));
|
||||||
|
|
||||||
|
params[2] = spa_pod_builder_object (
|
||||||
|
&pod_builder,
|
||||||
|
pipewire_type->param.idMeta, pipewire_type->param_meta.Meta,
|
||||||
|
":", pipewire_type->param_meta.type, "I", priv->spa_type.meta_cursor,
|
||||||
|
":", pipewire_type->param_meta.size, "i", CURSOR_META_SIZE (64, 64));
|
||||||
|
|
||||||
pw_stream_finish_format (priv->pipewire_stream, 0,
|
pw_stream_finish_format (priv->pipewire_stream, 0,
|
||||||
params, G_N_ELEMENTS (params));
|
params, G_N_ELEMENTS (params));
|
||||||
}
|
}
|
||||||
@ -517,6 +582,7 @@ init_spa_type (MetaSpaType *type,
|
|||||||
spa_type_media_subtype_map (map, &type->media_subtype);
|
spa_type_media_subtype_map (map, &type->media_subtype);
|
||||||
spa_type_format_video_map (map, &type->format_video);
|
spa_type_format_video_map (map, &type->format_video);
|
||||||
spa_type_video_format_map (map, &type->video_format);
|
spa_type_video_format_map (map, &type->video_format);
|
||||||
|
type->meta_cursor = spa_type_map_get_id(map, SPA_TYPE_META__Cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
static MetaPipeWireSource *
|
static MetaPipeWireSource *
|
||||||
|
@ -24,10 +24,26 @@
|
|||||||
#define META_SCREEN_CAST_STREAM_SRC_H
|
#define META_SCREEN_CAST_STREAM_SRC_H
|
||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
#include <spa/param/video/format-utils.h>
|
||||||
|
#include <spa/buffer/meta.h>
|
||||||
|
|
||||||
|
#include "backends/meta-backend-private.h"
|
||||||
|
#include "backends/meta-cursor-renderer.h"
|
||||||
|
#include "backends/meta-cursor.h"
|
||||||
|
#include "backends/meta-renderer.h"
|
||||||
#include "clutter/clutter.h"
|
#include "clutter/clutter.h"
|
||||||
|
#include "cogl/cogl.h"
|
||||||
#include "meta/boxes.h"
|
#include "meta/boxes.h"
|
||||||
|
|
||||||
|
typedef struct _MetaSpaType
|
||||||
|
{
|
||||||
|
struct spa_type_media_type media_type;
|
||||||
|
struct spa_type_media_subtype media_subtype;
|
||||||
|
struct spa_type_format_video format_video;
|
||||||
|
struct spa_type_video_format video_format;
|
||||||
|
uint32_t meta_cursor;
|
||||||
|
} MetaSpaType;
|
||||||
|
|
||||||
typedef struct _MetaScreenCastStream MetaScreenCastStream;
|
typedef struct _MetaScreenCastStream MetaScreenCastStream;
|
||||||
|
|
||||||
#define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
|
#define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ())
|
||||||
@ -46,14 +62,18 @@ struct _MetaScreenCastStreamSrcClass
|
|||||||
float *frame_rate);
|
float *frame_rate);
|
||||||
void (* enable) (MetaScreenCastStreamSrc *src);
|
void (* enable) (MetaScreenCastStreamSrc *src);
|
||||||
void (* disable) (MetaScreenCastStreamSrc *src);
|
void (* disable) (MetaScreenCastStreamSrc *src);
|
||||||
void (* record_frame) (MetaScreenCastStreamSrc *src,
|
gboolean (* record_frame) (MetaScreenCastStreamSrc *src,
|
||||||
uint8_t *data);
|
uint8_t *data);
|
||||||
gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
|
gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src,
|
||||||
MetaRectangle *crop_rect);
|
MetaRectangle *crop_rect);
|
||||||
|
void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src,
|
||||||
|
struct spa_meta_cursor *spa_meta_cursor);
|
||||||
};
|
};
|
||||||
|
|
||||||
void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
|
void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src);
|
||||||
|
|
||||||
MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
|
MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src);
|
||||||
|
|
||||||
|
MetaSpaType * meta_screen_cast_stream_src_get_spa_type (MetaScreenCastStreamSrc *src);
|
||||||
|
|
||||||
#endif /* META_SCREEN_CAST_STREAM_SRC_H */
|
#endif /* META_SCREEN_CAST_STREAM_SRC_H */
|
||||||
|
@ -24,13 +24,17 @@
|
|||||||
|
|
||||||
#include "backends/meta-screen-cast-stream.h"
|
#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"
|
#define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/gnome/Mutter/ScreenCast/Stream"
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
|
||||||
|
PROP_SESSION,
|
||||||
PROP_CONNECTION,
|
PROP_CONNECTION,
|
||||||
|
PROP_CURSOR_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
@ -44,9 +48,13 @@ static guint signals[N_SIGNALS];
|
|||||||
|
|
||||||
typedef struct _MetaScreenCastStreamPrivate
|
typedef struct _MetaScreenCastStreamPrivate
|
||||||
{
|
{
|
||||||
|
MetaScreenCastSession *session;
|
||||||
|
|
||||||
GDBusConnection *connection;
|
GDBusConnection *connection;
|
||||||
char *object_path;
|
char *object_path;
|
||||||
|
|
||||||
|
MetaScreenCastCursorMode cursor_mode;
|
||||||
|
|
||||||
MetaScreenCastStreamSrc *src;
|
MetaScreenCastStreamSrc *src;
|
||||||
} MetaScreenCastStreamPrivate;
|
} MetaScreenCastStreamPrivate;
|
||||||
|
|
||||||
@ -97,6 +105,15 @@ on_stream_src_ready (MetaScreenCastStreamSrc *src,
|
|||||||
meta_dbus_screen_cast_stream_emit_pipewire_stream_added (skeleton, node_id);
|
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
|
gboolean
|
||||||
meta_screen_cast_stream_start (MetaScreenCastStream *stream,
|
meta_screen_cast_stream_start (MetaScreenCastStream *stream,
|
||||||
GError **error)
|
GError **error)
|
||||||
@ -150,6 +167,15 @@ meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
|
|||||||
y);
|
y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaScreenCastCursorMode
|
||||||
|
meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream)
|
||||||
|
{
|
||||||
|
MetaScreenCastStreamPrivate *priv =
|
||||||
|
meta_screen_cast_stream_get_instance_private (stream);
|
||||||
|
|
||||||
|
return priv->cursor_mode;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
meta_screen_cast_stream_set_property (GObject *object,
|
meta_screen_cast_stream_set_property (GObject *object,
|
||||||
guint prop_id,
|
guint prop_id,
|
||||||
@ -162,9 +188,15 @@ meta_screen_cast_stream_set_property (GObject *object,
|
|||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
|
case PROP_SESSION:
|
||||||
|
priv->session = g_value_get_object (value);
|
||||||
|
break;
|
||||||
case PROP_CONNECTION:
|
case PROP_CONNECTION:
|
||||||
priv->connection = g_value_get_object (value);
|
priv->connection = g_value_get_object (value);
|
||||||
break;
|
break;
|
||||||
|
case PROP_CURSOR_MODE:
|
||||||
|
priv->cursor_mode = g_value_get_uint (value);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
}
|
}
|
||||||
@ -182,9 +214,15 @@ meta_screen_cast_stream_get_property (GObject *object,
|
|||||||
|
|
||||||
switch (prop_id)
|
switch (prop_id)
|
||||||
{
|
{
|
||||||
|
case PROP_SESSION:
|
||||||
|
g_value_set_object (value, priv->session);
|
||||||
|
break;
|
||||||
case PROP_CONNECTION:
|
case PROP_CONNECTION:
|
||||||
g_value_set_object (value, priv->connection);
|
g_value_set_object (value, priv->connection);
|
||||||
break;
|
break;
|
||||||
|
case PROP_CURSOR_MODE:
|
||||||
|
g_value_set_uint (value, priv->cursor_mode);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||||
}
|
}
|
||||||
@ -256,6 +294,16 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
|
|||||||
object_class->set_property = meta_screen_cast_stream_set_property;
|
object_class->set_property = meta_screen_cast_stream_set_property;
|
||||||
object_class->get_property = meta_screen_cast_stream_get_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,
|
g_object_class_install_property (object_class,
|
||||||
PROP_CONNECTION,
|
PROP_CONNECTION,
|
||||||
g_param_spec_object ("connection",
|
g_param_spec_object ("connection",
|
||||||
@ -266,6 +314,18 @@ meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass)
|
|||||||
G_PARAM_CONSTRUCT_ONLY |
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
G_PARAM_STATIC_STRINGS));
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
|
g_object_class_install_property (object_class,
|
||||||
|
PROP_CURSOR_MODE,
|
||||||
|
g_param_spec_uint ("cursor-mode",
|
||||||
|
"cursor-mode",
|
||||||
|
"Cursor mode",
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_METADATA,
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_HIDDEN,
|
||||||
|
G_PARAM_READWRITE |
|
||||||
|
G_PARAM_CONSTRUCT_ONLY |
|
||||||
|
G_PARAM_STATIC_STRINGS));
|
||||||
|
|
||||||
signals[CLOSED] = g_signal_new ("closed",
|
signals[CLOSED] = g_signal_new ("closed",
|
||||||
G_TYPE_FROM_CLASS (klass),
|
G_TYPE_FROM_CLASS (klass),
|
||||||
G_SIGNAL_RUN_LAST,
|
G_SIGNAL_RUN_LAST,
|
||||||
|
@ -26,8 +26,12 @@
|
|||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
|
||||||
#include "backends/meta-screen-cast-stream-src.h"
|
#include "backends/meta-screen-cast-stream-src.h"
|
||||||
|
#include "backends/meta-screen-cast.h"
|
||||||
|
|
||||||
#include "meta-dbus-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 ())
|
#define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ())
|
||||||
G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream,
|
G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream,
|
||||||
META, SCREEN_CAST_STREAM,
|
META, SCREEN_CAST_STREAM,
|
||||||
@ -48,6 +52,8 @@ struct _MetaScreenCastStreamClass
|
|||||||
double *y);
|
double *y);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream);
|
||||||
|
|
||||||
gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream,
|
gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream,
|
||||||
GError **error);
|
GError **error);
|
||||||
|
|
||||||
@ -61,4 +67,6 @@ void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream,
|
|||||||
double *x,
|
double *x,
|
||||||
double *y);
|
double *y);
|
||||||
|
|
||||||
|
MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream);
|
||||||
|
|
||||||
#endif /* META_SCREEN_CAST_STREAM_H */
|
#endif /* META_SCREEN_CAST_STREAM_H */
|
||||||
|
@ -207,7 +207,7 @@ meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src)
|
|||||||
meta_screen_cast_window_stream_src_stop (window_src);
|
meta_screen_cast_window_stream_src_stop (window_src);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static gboolean
|
||||||
meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
||||||
uint8_t *data)
|
uint8_t *data)
|
||||||
{
|
{
|
||||||
@ -215,6 +215,8 @@ meta_screen_cast_window_stream_src_record_frame (MetaScreenCastStreamSrc *src,
|
|||||||
META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
|
META_SCREEN_CAST_WINDOW_STREAM_SRC (src);
|
||||||
|
|
||||||
capture_into (window_src, data);
|
capture_into (window_src, data);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
MetaScreenCastWindowStreamSrc *
|
MetaScreenCastWindowStreamSrc *
|
||||||
|
@ -71,9 +71,10 @@ meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_st
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetaScreenCastWindowStream *
|
MetaScreenCastWindowStream *
|
||||||
meta_screen_cast_window_stream_new (GDBusConnection *connection,
|
meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
|
||||||
MetaWindow *window,
|
GDBusConnection *connection,
|
||||||
GError **error)
|
MetaWindow *window,
|
||||||
|
GError **error)
|
||||||
{
|
{
|
||||||
MetaScreenCastWindowStream *window_stream;
|
MetaScreenCastWindowStream *window_stream;
|
||||||
MetaLogicalMonitor *logical_monitor;
|
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,
|
window_stream = g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM,
|
||||||
NULL,
|
NULL,
|
||||||
error,
|
error,
|
||||||
|
"session", session,
|
||||||
"connection", connection,
|
"connection", connection,
|
||||||
"window", window,
|
"window", window,
|
||||||
NULL);
|
NULL);
|
||||||
|
@ -32,9 +32,10 @@ G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream,
|
|||||||
META, SCREEN_CAST_WINDOW_STREAM,
|
META, SCREEN_CAST_WINDOW_STREAM,
|
||||||
MetaScreenCastStream)
|
MetaScreenCastStream)
|
||||||
|
|
||||||
MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (GDBusConnection *connection,
|
MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session,
|
||||||
MetaWindow *window,
|
GDBusConnection *connection,
|
||||||
GError **error);
|
MetaWindow *window,
|
||||||
|
GError **error);
|
||||||
|
|
||||||
MetaWindow * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream);
|
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_width (MetaScreenCastWindowStream *window_stream);
|
||||||
|
@ -43,6 +43,7 @@ struct _MetaScreenCast
|
|||||||
GList *sessions;
|
GList *sessions;
|
||||||
|
|
||||||
MetaDbusSessionWatcher *session_watcher;
|
MetaDbusSessionWatcher *session_watcher;
|
||||||
|
MetaBackend *backend;
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -62,12 +63,20 @@ meta_screen_cast_get_connection (MetaScreenCast *screen_cast)
|
|||||||
return g_dbus_interface_skeleton_get_connection (interface_skeleton);
|
return g_dbus_interface_skeleton_get_connection (interface_skeleton);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MetaBackend *
|
||||||
|
meta_screen_cast_get_backend (MetaScreenCast *screen_cast)
|
||||||
|
{
|
||||||
|
return screen_cast->backend;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
register_remote_desktop_screen_cast_session (MetaScreenCastSession *session,
|
register_remote_desktop_screen_cast_session (MetaScreenCastSession *session,
|
||||||
const char *remote_desktop_session_id,
|
const char *remote_desktop_session_id,
|
||||||
GError **error)
|
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);
|
MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend);
|
||||||
MetaRemoteDesktopSession *remote_desktop_session;
|
MetaRemoteDesktopSession *remote_desktop_session;
|
||||||
|
|
||||||
@ -244,11 +253,13 @@ meta_screen_cast_finalize (GObject *object)
|
|||||||
}
|
}
|
||||||
|
|
||||||
MetaScreenCast *
|
MetaScreenCast *
|
||||||
meta_screen_cast_new (MetaDbusSessionWatcher *session_watcher)
|
meta_screen_cast_new (MetaBackend *backend,
|
||||||
|
MetaDbusSessionWatcher *session_watcher)
|
||||||
{
|
{
|
||||||
MetaScreenCast *screen_cast;
|
MetaScreenCast *screen_cast;
|
||||||
|
|
||||||
screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL);
|
screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL);
|
||||||
|
screen_cast->backend = backend;
|
||||||
screen_cast->session_watcher = session_watcher;
|
screen_cast->session_watcher = session_watcher;
|
||||||
|
|
||||||
return screen_cast;
|
return screen_cast;
|
||||||
|
@ -25,9 +25,17 @@
|
|||||||
|
|
||||||
#include <glib-object.h>
|
#include <glib-object.h>
|
||||||
|
|
||||||
|
#include "backends/meta-backend-private.h"
|
||||||
#include "backends/meta-dbus-session-watcher.h"
|
#include "backends/meta-dbus-session-watcher.h"
|
||||||
#include "meta-dbus-screen-cast.h"
|
#include "meta-dbus-screen-cast.h"
|
||||||
|
|
||||||
|
typedef enum _MetaScreenCastCursorMode
|
||||||
|
{
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0,
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1,
|
||||||
|
META_SCREEN_CAST_CURSOR_MODE_METADATA = 2,
|
||||||
|
} MetaScreenCastCursorMode;
|
||||||
|
|
||||||
#define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ())
|
#define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ())
|
||||||
G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
|
G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
|
||||||
META, SCREEN_CAST,
|
META, SCREEN_CAST,
|
||||||
@ -35,6 +43,9 @@ G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast,
|
|||||||
|
|
||||||
GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *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 */
|
#endif /* META_SCREEN_CAST_H */
|
||||||
|
@ -30,7 +30,17 @@
|
|||||||
#include "backends/meta-backend-private.h"
|
#include "backends/meta-backend-private.h"
|
||||||
#include "clutter/clutter-mutter.h"
|
#include "clutter/clutter-mutter.h"
|
||||||
|
|
||||||
struct _MetaOverlay {
|
enum
|
||||||
|
{
|
||||||
|
ACTORS_PAINTED,
|
||||||
|
|
||||||
|
N_SIGNALS
|
||||||
|
};
|
||||||
|
|
||||||
|
static guint signals[N_SIGNALS];
|
||||||
|
|
||||||
|
struct _MetaOverlay
|
||||||
|
{
|
||||||
gboolean enabled;
|
gboolean enabled;
|
||||||
|
|
||||||
CoglPipeline *pipeline;
|
CoglPipeline *pipeline;
|
||||||
@ -140,6 +150,8 @@ meta_stage_paint (ClutterActor *actor)
|
|||||||
|
|
||||||
CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (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)
|
for (l = priv->overlays; l; l = l->next)
|
||||||
meta_overlay_paint (l->data);
|
meta_overlay_paint (l->data);
|
||||||
}
|
}
|
||||||
@ -179,6 +191,13 @@ meta_stage_class_init (MetaStageClass *klass)
|
|||||||
|
|
||||||
stage_class->activate = meta_stage_activate;
|
stage_class->activate = meta_stage_activate;
|
||||||
stage_class->deactivate = meta_stage_deactivate;
|
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
|
static void
|
||||||
|
@ -587,6 +587,10 @@ should_have_hw_cursor (MetaCursorRenderer *renderer,
|
|||||||
if (!cursor_sprite)
|
if (!cursor_sprite)
|
||||||
return FALSE;
|
return FALSE;
|
||||||
|
|
||||||
|
if (meta_cursor_renderer_is_hw_cursors_inhibited (renderer,
|
||||||
|
cursor_sprite))
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
for (l = gpus; l; l = l->next)
|
for (l = gpus; l; l = l->next)
|
||||||
{
|
{
|
||||||
MetaGpuKms *gpu_kms = l->data;
|
MetaGpuKms *gpu_kms = l->data;
|
||||||
|
@ -71,7 +71,15 @@
|
|||||||
|
|
||||||
Record a single monitor.
|
Record a single monitor.
|
||||||
|
|
||||||
Available @properties include: (none)
|
Available @properties include:
|
||||||
|
|
||||||
|
* "cursor-mode" (u): Cursor mode. Default: 'hidden' (see below)
|
||||||
|
|
||||||
|
Available cursor mode values:
|
||||||
|
|
||||||
|
0: hidden - cursor is not included in the stream
|
||||||
|
1: embedded - cursor is included in the framebuffer
|
||||||
|
2: metadata - cursor is included as metadata in the PipeWire stream
|
||||||
-->
|
-->
|
||||||
<method name="RecordMonitor">
|
<method name="RecordMonitor">
|
||||||
<arg name="connector" type="s" direction="in" />
|
<arg name="connector" type="s" direction="in" />
|
||||||
@ -84,7 +92,7 @@
|
|||||||
@properties: Properties used determining what window to select
|
@properties: Properties used determining what window to select
|
||||||
@stream_path: Path to the new stream object
|
@stream_path: Path to the new stream object
|
||||||
|
|
||||||
Record a single window.
|
Record a single window. The cursor will not be included.
|
||||||
|
|
||||||
Available @properties include:
|
Available @properties include:
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user