ShellRecorder: update to use MetaCursorTracker

Replace more direct XFixes usage with a the appropriate abstraction
API from mutter, which is guaranteed to work in wayland too.

It doesn't yet replace pointer position tracking, although probably
it should.
Also, because now we're using Mutter API, we lose the standalone
test case.

https://bugzilla.gnome.org/show_bug.cgi?id=705911
This commit is contained in:
Giovanni Campagna 2013-08-14 10:34:58 +02:00
parent 2b8414a453
commit d52c95a15f
4 changed files with 73 additions and 194 deletions

View File

@ -52,7 +52,8 @@ const ScreencastService = new Lang.Class({
_ensureRecorderForSender: function(sender) { _ensureRecorderForSender: function(sender) {
let recorder = this._recorders.get(sender); let recorder = this._recorders.get(sender);
if (!recorder) { if (!recorder) {
recorder = new Shell.Recorder({ stage: global.stage }); recorder = new Shell.Recorder({ stage: global.stage,
screen: global.screen });
recorder._watchNameId = recorder._watchNameId =
Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null, Gio.bus_watch_name(Gio.BusType.SESSION, sender, 0, null,
Lang.bind(this, this._onNameVanished)); Lang.bind(this, this._onNameVanished));

View File

@ -226,14 +226,6 @@ shell_recorder_private_sources = \
shell_private_sources += $(shell_recorder_private_sources) shell_private_sources += $(shell_recorder_private_sources)
noinst_PROGRAMS += test-recorder
test_recorder_CPPFLAGS = $(TEST_SHELL_RECORDER_CFLAGS)
test_recorder_LDADD = $(TEST_SHELL_RECORDER_LIBS)
test_recorder_SOURCES = \
$(shell_recorder_sources) $(shell_recorder_private_sources) \
test-recorder.c
endif BUILD_RECORDER endif BUILD_RECORDER
######################################## ########################################

View File

@ -14,11 +14,14 @@
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <gdk/gdk.h> #include <gdk/gdk.h>
#include <cogl/cogl.h>
#include <meta/screen.h>
#include <meta/meta-cursor-tracker.h>
#include "shell-recorder-src.h" #include "shell-recorder-src.h"
#include "shell-recorder.h" #include "shell-recorder.h"
#include <clutter/x11/clutter-x11.h> #include <clutter/x11/clutter-x11.h>
#include <X11/extensions/Xfixes.h>
#include <X11/extensions/XInput.h> #include <X11/extensions/XInput.h>
#include <X11/extensions/XInput2.h> #include <X11/extensions/XInput2.h>
@ -64,14 +67,13 @@ struct _ShellRecorder {
int pointer_x; int pointer_x;
int pointer_y; int pointer_y;
gboolean have_xfixes;
int xfixes_event_base;
int xinput_opcode; int xinput_opcode;
GSettings *a11y_settings; GSettings *a11y_settings;
gboolean draw_cursor; gboolean draw_cursor;
MetaCursorTracker *cursor_tracker;
cairo_surface_t *cursor_image; cairo_surface_t *cursor_image;
guint8 *cursor_memory;
int cursor_hot_x; int cursor_hot_x;
int cursor_hot_y; int cursor_hot_y;
@ -121,6 +123,7 @@ static void recorder_pipeline_closed (RecorderPipeline *pipeline);
enum { enum {
PROP_0, PROP_0,
PROP_SCREEN,
PROP_STAGE, PROP_STAGE,
PROP_FRAMERATE, PROP_FRAMERATE,
PROP_PIPELINE, PROP_PIPELINE,
@ -242,6 +245,8 @@ shell_recorder_finalize (GObject *object)
if (recorder->cursor_image) if (recorder->cursor_image)
cairo_surface_destroy (recorder->cursor_image); cairo_surface_destroy (recorder->cursor_image);
if (recorder->cursor_memory)
g_free (recorder->cursor_memory);
recorder_set_stage (recorder, NULL); recorder_set_stage (recorder, NULL);
recorder_set_pipeline (recorder, NULL); recorder_set_pipeline (recorder, NULL);
@ -322,38 +327,24 @@ recorder_remove_redraw_timeout (ShellRecorder *recorder)
static void static void
recorder_fetch_cursor_image (ShellRecorder *recorder) recorder_fetch_cursor_image (ShellRecorder *recorder)
{ {
XFixesCursorImage *cursor_image; CoglTexture *texture;
guchar *data; int width, height;
int stride; int stride;
int i, j; guint8 *data;
if (!recorder->have_xfixes) texture = meta_cursor_tracker_get_sprite (recorder->cursor_tracker);
return; width = cogl_texture_get_width (texture);
height = cogl_texture_get_height (texture);
stride = 4 * width;
data = g_new (guint8, stride * height);
cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32, stride, data);
cursor_image = XFixesGetCursorImage (clutter_x11_get_default_display ()); /* FIXME: cairo-gl? */
if (!cursor_image) recorder->cursor_image = cairo_image_surface_create_for_data (data,
return; CAIRO_FORMAT_ARGB32,
width, height,
recorder->cursor_hot_x = cursor_image->xhot; stride);
recorder->cursor_hot_y = cursor_image->yhot; recorder->cursor_memory = data;
recorder->cursor_image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
cursor_image->width,
cursor_image->height);
/* The pixel data (in typical Xlib breakage) is longs even on
* 64-bit platforms, so we have to data-convert there. For simplicity,
* just do it always
*/
data = cairo_image_surface_get_data (recorder->cursor_image);
stride = cairo_image_surface_get_stride (recorder->cursor_image);
for (i = 0; i < cursor_image->height; i++)
for (j = 0; j < cursor_image->width; j++)
*(guint32 *)(data + i * stride + 4 * j) = cursor_image->pixels[i * cursor_image->width + j];
cairo_surface_mark_dirty (recorder->cursor_image);
XFree (cursor_image);
} }
/* Overlay the cursor image on the frame. We draw the cursor image /* Overlay the cursor image on the frame. We draw the cursor image
@ -546,6 +537,24 @@ recorder_queue_redraw (ShellRecorder *recorder)
recorder_idle_redraw, recorder, NULL); recorder_idle_redraw, recorder, NULL);
} }
static void
on_cursor_changed (MetaCursorTracker *tracker,
ShellRecorder *recorder)
{
if (recorder->cursor_image)
{
cairo_surface_destroy (recorder->cursor_image);
recorder->cursor_image = NULL;
}
if (recorder->cursor_memory)
{
g_free (recorder->cursor_memory);
recorder->cursor_memory = NULL;
}
recorder_queue_redraw (recorder);
}
/* We use an event filter on the stage to get the XFixesCursorNotifyEvent /* We use an event filter on the stage to get the XFixesCursorNotifyEvent
* and also to track cursor position (when the cursor is over the stage's * and also to track cursor position (when the cursor is over the stage's
* input area); tracking cursor position here rather than with ClutterEvent * input area); tracking cursor position here rather than with ClutterEvent
@ -567,22 +576,7 @@ recorder_event_filter (XEvent *xev,
xev->xcookie.extension == recorder->xinput_opcode) xev->xcookie.extension == recorder->xinput_opcode)
input_event = (XIEvent *) xev->xcookie.data; input_event = (XIEvent *) xev->xcookie.data;
if (xev->xany.type == recorder->xfixes_event_base + XFixesCursorNotify) if (input_event != NULL &&
{
XFixesCursorNotifyEvent *notify_event = (XFixesCursorNotifyEvent *)xev;
if (notify_event->subtype == XFixesDisplayCursorNotify)
{
if (recorder->cursor_image)
{
cairo_surface_destroy (recorder->cursor_image);
recorder->cursor_image = NULL;
}
recorder_queue_redraw (recorder);
}
}
else if (input_event != NULL &&
input_event->evtype == XI_Motion) input_event->evtype == XI_Motion)
{ {
XIDeviceEvent *device_event = (XIDeviceEvent *) input_event; XIDeviceEvent *device_event = (XIDeviceEvent *) input_event;
@ -807,14 +801,6 @@ recorder_set_stage (ShellRecorder *recorder,
recorder_update_size (recorder); recorder_update_size (recorder);
recorder->have_xfixes = XFixesQueryExtension (clutter_x11_get_default_display (),
&recorder->xfixes_event_base,
&error_base);
if (recorder->have_xfixes)
XFixesSelectCursorInput (clutter_x11_get_default_display (),
clutter_x11_get_stage_window (stage),
XFixesDisplayCursorNotifyMask);
if (XQueryExtension (clutter_x11_get_default_display (), if (XQueryExtension (clutter_x11_get_default_display (),
"XInputExtension", "XInputExtension",
&recorder->xinput_opcode, &recorder->xinput_opcode,
@ -843,6 +829,22 @@ recorder_set_stage (ShellRecorder *recorder,
} }
} }
static void
recorder_set_screen (ShellRecorder *recorder,
MetaScreen *screen)
{
MetaCursorTracker *tracker;
tracker = meta_cursor_tracker_get_for_screen (screen);
if (tracker == recorder->cursor_tracker)
return;
recorder->cursor_tracker = tracker;
g_signal_connect_object (tracker, "cursor-changed",
G_CALLBACK (on_cursor_changed), recorder, 0);
}
static void static void
recorder_set_framerate (ShellRecorder *recorder, recorder_set_framerate (ShellRecorder *recorder,
int framerate) int framerate)
@ -918,6 +920,9 @@ shell_recorder_set_property (GObject *object,
switch (prop_id) switch (prop_id)
{ {
case PROP_SCREEN:
recorder_set_screen (recorder, g_value_get_object (value));
break;
case PROP_STAGE: case PROP_STAGE:
recorder_set_stage (recorder, g_value_get_object (value)); recorder_set_stage (recorder, g_value_get_object (value));
break; break;
@ -979,6 +984,14 @@ shell_recorder_class_init (ShellRecorderClass *klass)
gobject_class->get_property = shell_recorder_get_property; gobject_class->get_property = shell_recorder_get_property;
gobject_class->set_property = shell_recorder_set_property; gobject_class->set_property = shell_recorder_set_property;
g_object_class_install_property (gobject_class,
PROP_SCREEN,
g_param_spec_object ("screen",
"Screen",
"Screen to record",
META_TYPE_SCREEN,
G_PARAM_WRITABLE));
g_object_class_install_property (gobject_class, g_object_class_install_property (gobject_class,
PROP_STAGE, PROP_STAGE,
g_param_spec_object ("stage", g_param_spec_object ("stage",

View File

@ -1,127 +0,0 @@
/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */
#define GST_USE_UNSTABLE_API
#include "shell-recorder.h"
#include <clutter/clutter.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
/* Very simple test of the ShellRecorder class; shows some text strings
* moving around and records it.
*/
static ShellRecorder *recorder = NULL;
static gboolean
stop_recording_timeout (ClutterActor *stage)
{
if (recorder)
{
shell_recorder_close (recorder);
/* quit when the recorder finishes closing
*/
g_object_weak_ref (G_OBJECT (recorder),
(GWeakNotify)
clutter_actor_destroy,
stage);
g_object_unref (recorder);
}
else
{
clutter_actor_destroy (stage);
}
return FALSE;
}
static void
on_animation_completed (ClutterAnimation *animation,
ClutterStage *stage)
{
g_timeout_add (1000, (GSourceFunc) stop_recording_timeout, stage);
}
static void
on_stage_realized (ClutterActor *stage,
gpointer data)
{
recorder = shell_recorder_new (CLUTTER_STAGE (stage));
shell_recorder_set_file_template (recorder, "test-recorder.webm");
shell_recorder_record (recorder, NULL);
}
int main (int argc, char **argv)
{
ClutterActor *stage;
ClutterActor *text;
ClutterAnimation *animation;
ClutterColor red, green, blue;
gtk_init (&argc, &argv);
gst_init (&argc, &argv);
if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS)
return 1;
clutter_color_from_string (&red, "red");
clutter_color_from_string (&green, "green");
clutter_color_from_string (&blue, "blue");
stage = clutter_stage_new ();
g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL);
text = g_object_new (CLUTTER_TYPE_TEXT,
"text", "Red",
"font-name", "Sans 40px",
"color", &red,
NULL);
clutter_actor_add_child (stage, text);
animation = clutter_actor_animate (text,
CLUTTER_EASE_IN_OUT_QUAD,
3000,
"x", 320.0,
"y", 240.0,
NULL);
g_signal_connect (animation, "completed",
G_CALLBACK (on_animation_completed), stage);
text = g_object_new (CLUTTER_TYPE_TEXT,
"text", "Blue",
"font-name", "Sans 40px",
"color", &blue,
"x", 640.0,
"y", 0.0,
NULL);
clutter_actor_set_anchor_point_from_gravity (text, CLUTTER_GRAVITY_NORTH_EAST);
clutter_actor_add_child (stage, text);
animation = clutter_actor_animate (text,
CLUTTER_EASE_IN_OUT_QUAD,
3000,
"x", 320.0,
"y", 240.0,
NULL);
text = g_object_new (CLUTTER_TYPE_TEXT,
"text", "Green",
"font-name", "Sans 40px",
"color", &green,
"x", 0.0,
"y", 480.0,
NULL);
clutter_actor_set_anchor_point_from_gravity (text, CLUTTER_GRAVITY_SOUTH_WEST);
clutter_actor_add_child (stage, text);
animation = clutter_actor_animate (text,
CLUTTER_EASE_IN_OUT_QUAD,
3000,
"x", 320.0,
"y", 240.0,
NULL);
g_signal_connect_after (stage, "realize",
G_CALLBACK (on_stage_realized), NULL);
clutter_actor_show (stage);
clutter_main ();
return 0;
}