#include "recording.h"
#include "renderrecording.h"
#include "startrecording.h"
+#include "eventrecording.h"
struct _GtkInspectorRecorder
{
GtkWidget *node_property_tree;
GtkWidget *recording_data_stack;
GtkTreeModel *render_node_properties;
+ GtkTreeModel *event_properties;
+ GtkWidget *event_property_tree;
+ GtkWidget *event_view;
GtkInspectorRecording *recording; /* start recording if recording or NULL if not */
gint64 start_time;
g_object_unref (paintable);
}
+static void
+show_render_node (GtkInspectorRecorder *recorder,
+ GskRenderNode *node)
+{
+ graphene_rect_t bounds;
+ GdkPaintable *paintable;
+
+ gsk_render_node_get_bounds (node, &bounds);
+ paintable = gtk_render_node_paintable_new (node, &bounds);
+
+ if (strcmp (gtk_stack_get_visible_child_name (GTK_STACK (recorder->recording_data_stack)), "frame_data") == 0)
+ {
+ gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), paintable);
+
+ g_list_store_splice (recorder->render_node_root_model,
+ 0, g_list_model_get_n_items (G_LIST_MODEL (recorder->render_node_root_model)),
+ (gpointer[1]) { paintable },
+ 1);
+ }
+ else
+ {
+ gtk_picture_set_paintable (GTK_PICTURE (recorder->event_view), paintable);
+ }
+
+ g_object_unref (paintable);
+}
+
+static GskRenderNode *
+make_dot (double x, double y)
+{
+ GskRenderNode *fill, *dot;
+ GdkRGBA red = (GdkRGBA){ 1, 0, 0, 1 };
+ graphene_rect_t rect = GRAPHENE_RECT_INIT (x - 3, y - 3, 6, 6);
+ graphene_size_t corner = GRAPHENE_SIZE_INIT (3, 3);
+ GskRoundedRect clip;
+
+ fill = gsk_color_node_new (&red, &rect);
+ dot = gsk_rounded_clip_node_new (fill, gsk_rounded_rect_init (&clip, &rect,
+ &corner, &corner, &corner, &corner));
+ gsk_render_node_unref (fill);
+
+ return dot;
+}
+
+static void
+show_event (GtkInspectorRecorder *recorder,
+ GskRenderNode *node,
+ GdkEvent *event)
+{
+ GskRenderNode *temp;
+ double x, y;
+
+ if (gdk_event_get_position (event, &x, &y))
+ {
+ GskRenderNode *dot = make_dot (x, y);
+ temp = gsk_container_node_new ((GskRenderNode *[]) { node, dot }, 2);
+ gsk_render_node_unref (dot);
+ }
+ else
+ temp = gsk_render_node_ref (node);
+
+ show_render_node (recorder, temp);
+
+ gsk_render_node_unref (temp);
+}
+
+static void populate_event_properties (GtkListStore *store,
+ GdkEvent *event);
+
static void
recording_selected (GtkSingleSelection *selection,
GParamSpec *pspec,
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
{
- graphene_rect_t bounds;
GskRenderNode *node;
- GdkPaintable *paintable;
gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), "frame_data");
node = gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (recording));
- gsk_render_node_get_bounds (node, &bounds);
- paintable = gtk_render_node_paintable_new (node, &bounds);
- gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), paintable);
+ show_render_node (recorder, node);
+ }
+ else if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording))
+ {
+ GdkEvent *event;
- g_list_store_splice (recorder->render_node_root_model,
- 0, g_list_model_get_n_items (G_LIST_MODEL (recorder->render_node_root_model)),
- (gpointer[1]) { paintable },
- 1);
- g_object_unref (paintable);
+ gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), "event_data");
+
+ event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording));
+
+ for (guint pos = gtk_single_selection_get_selected (selection) - 1; pos > 0; pos--)
+ {
+ GtkInspectorRecording *item = g_list_model_get_item (G_LIST_MODEL (selection), pos);
+
+ g_object_unref (item);
+ if (GTK_INSPECTOR_IS_RENDER_RECORDING (item))
+ {
+ GskRenderNode *node;
+
+ node = gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (item));
+ show_event (recorder, node, event);
+ break;
+ }
+ }
+
+ populate_event_properties (GTK_LIST_STORE (recorder->event_properties), event);
}
else
{
gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), "no_data");
+
gtk_picture_set_paintable (GTK_PICTURE (recorder->render_node_view), NULL);
g_list_store_remove_all (recorder->render_node_root_model);
}
}
}
+static const char *
+event_type_name (GdkEventType type)
+{
+ const char *event_name[] = {
+ "Delete",
+ "Motion",
+ "Button Press",
+ "Button Release",
+ "Key Press",
+ "Key Release",
+ "Enter",
+ "Leave",
+ "Focus",
+ "Proximity In",
+ "Proximity Out",
+ "Drag Enter",
+ "Drag Leave",
+ "Drag Motion",
+ "Drop Start",
+ "Scroll",
+ "Grab Broken",
+ "Touch Begin",
+ "Touch Update",
+ "Touch End",
+ "Touch Cancel",
+ "Touchpad Swipe",
+ "Touchpad Pinch",
+ "Pad Button Press",
+ "Pad Button Release",
+ "Pad Rind",
+ "Pad Strip",
+ "Pad Group Mode"
+ };
+
+ return event_name[type];
+}
+
+static const char *
+scroll_direction_name (GdkScrollDirection dir)
+{
+ const char *scroll_dir[] = {
+ "Up", "Down", "Left", "Right", "Smooth"
+ };
+ return scroll_dir[dir];
+}
+
+static char *
+modifier_names (GdkModifierType state)
+{
+ struct {
+ const char *name;
+ int mask;
+ } mods[] = {
+ { "Shift", GDK_SHIFT_MASK },
+ { "Lock", GDK_LOCK_MASK },
+ { "Control", GDK_CONTROL_MASK },
+ { "Alt", GDK_ALT_MASK },
+ { "Button1", GDK_BUTTON1_MASK },
+ { "Button2", GDK_BUTTON2_MASK },
+ { "Button3", GDK_BUTTON3_MASK },
+ { "Button4", GDK_BUTTON4_MASK },
+ { "Button5", GDK_BUTTON5_MASK },
+ { "Super", GDK_SUPER_MASK },
+ { "Hyper", GDK_HYPER_MASK },
+ { "Meta", GDK_META_MASK },
+ };
+ GString *s;
+
+ s = g_string_new ("");
+
+ for (int i = 0; i < G_N_ELEMENTS (mods); i++)
+ {
+ if (state & mods[i].mask)
+ {
+ if (s->len > 0)
+ g_string_append (s, " ");
+ g_string_append (s, mods[i].name);
+ }
+ }
+
+ return g_string_free (s, FALSE);
+}
+
+static char *
+key_event_string (GdkEvent *event)
+{
+ guint keyval;
+ gunichar c;
+ char buf[5] = { 0, };
+
+ keyval = gdk_key_event_get_keyval (event);
+ c = gdk_keyval_to_unicode (keyval);
+ if (c)
+ {
+ g_unichar_to_utf8 (c, buf);
+ return g_strdup (buf);
+ }
+
+ return g_strdup (gdk_keyval_name (keyval));
+}
+
+static void
+populate_event_properties (GtkListStore *store,
+ GdkEvent *event)
+{
+ GdkEventType type;
+ GdkDevice *device;
+ double x, y;
+ char *tmp;
+ GdkModifierType state;
+
+ gtk_list_store_clear (store);
+
+ type = gdk_event_get_event_type (event);
+
+ add_text_row (store, "Type", event_type_name (type));
+
+ device = gdk_event_get_device (event);
+ if (device)
+ add_text_row (store, "Device", gdk_device_get_name (device));
+
+ if (gdk_event_get_position (event, &x, &y))
+ {
+ tmp = g_strdup_printf ("%.2f %.2f", x, y);
+ add_text_row (store, "Position", tmp);
+ g_free (tmp);
+ }
+
+ state = gdk_event_get_modifier_state (event);
+ if (state != 0)
+ {
+ tmp = modifier_names (state);
+ add_text_row (store, "State", tmp);
+ g_free (tmp);
+ }
+
+ switch ((int)type)
+ {
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ add_int_row (store, "Button", gdk_button_event_get_button (event));
+ break;
+
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ add_int_row (store, "Keycode", gdk_key_event_get_keycode (event));
+ add_int_row (store, "Keyval", gdk_key_event_get_keyval (event));
+ tmp = key_event_string (event);
+ add_text_row (store, "Key", tmp);
+ g_free (tmp);
+ add_int_row (store, "Layout", gdk_key_event_get_layout (event));
+ add_int_row (store, "Level", gdk_key_event_get_level (event));
+ add_boolean_row (store, "Is Modifier", gdk_key_event_is_modifier (event));
+ break;
+
+ case GDK_SCROLL:
+ if (gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH)
+ {
+ gdk_scroll_event_get_deltas (event, &x, &y);
+ tmp = g_strdup_printf ("%.2f %.2f", x, y);
+ add_text_row (store, "Delta", tmp);
+ g_free (tmp);
+ }
+ else
+ {
+ add_text_row (store, "Direction", scroll_direction_name (gdk_scroll_event_get_direction (event)));
+ }
+ add_boolean_row (store, "Is Stop", gdk_scroll_event_is_stop (event));
+ break;
+
+ case GDK_FOCUS_CHANGE:
+ add_text_row (store, "Direction", gdk_focus_event_get_in (event) ? "In" : "Out");
+ break;
+
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ add_int_row (store, "Mode", gdk_crossing_event_get_mode (event));
+ add_int_row (store, "Detail", gdk_crossing_event_get_detail (event));
+ add_boolean_row (store, "Is Focus", gdk_crossing_event_get_focus (event));
+ break;
+
+ case GDK_GRAB_BROKEN:
+ add_boolean_row (store, "Implicit", gdk_grab_broken_event_get_implicit (event));
+ break;
+
+ default:
+ /* FIXME */
+ ;
+ }
+}
+
static GskRenderNode *
get_selected_node (GtkInspectorRecorder *recorder)
{
gtk_list_item_set_child (item, widget);
}
+static char *
+get_event_summary (GdkEvent *event)
+{
+ double x, y;
+ int type;
+ const char *name;
+
+ gdk_event_get_position (event, &x, &y);
+ type = gdk_event_get_event_type (event);
+ name = event_type_name (type);
+
+ switch (type)
+ {
+ case GDK_ENTER_NOTIFY:
+ case GDK_LEAVE_NOTIFY:
+ case GDK_MOTION_NOTIFY:
+ case GDK_DRAG_ENTER:
+ case GDK_DRAG_LEAVE:
+ case GDK_DRAG_MOTION:
+ case GDK_DROP_START:
+ case GDK_TOUCH_BEGIN:
+ case GDK_TOUCH_UPDATE:
+ case GDK_TOUCH_END:
+ case GDK_TOUCH_CANCEL:
+ case GDK_TOUCHPAD_SWIPE:
+ case GDK_TOUCHPAD_PINCH:
+ case GDK_BUTTON_PRESS:
+ case GDK_BUTTON_RELEASE:
+ return g_strdup_printf ("%s (%.2f %.2f)", name, x, y);
+
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ {
+ char *tmp, *ret;
+ tmp = key_event_string (event);
+ ret = g_strdup_printf ("%s %s\n", name, tmp);
+ g_free (tmp);
+ return ret;
+ }
+
+ case GDK_FOCUS_CHANGE:
+ return g_strdup_printf ("%s %s", name, gdk_focus_event_get_in (event) ? "In" : "Out");
+
+ case GDK_GRAB_BROKEN:
+ case GDK_PROXIMITY_IN:
+ case GDK_PROXIMITY_OUT:
+ case GDK_PAD_BUTTON_PRESS:
+ case GDK_PAD_BUTTON_RELEASE:
+ case GDK_PAD_RING:
+ case GDK_PAD_STRIP:
+ case GDK_PAD_GROUP_MODE:
+ return g_strdup_printf ("%s", name);
+
+ case GDK_SCROLL:
+ if (gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH)
+ {
+ gdk_scroll_event_get_deltas (event, &x, &y);
+ return g_strdup_printf ("%s %.2f %.2f", name, x, y);
+ }
+ else
+ {
+ return g_strdup_printf ("%s %s", name, scroll_direction_name (gdk_scroll_event_get_direction (event)));
+ }
+ break;
+
+ default:
+ g_assert_not_reached ();
+ }
+}
+
static void
bind_widget_for_recording (GtkListItemFactory *factory,
GtkListItem *item,
{
GtkInspectorRecording *recording = gtk_list_item_get_item (item);
GtkWidget *widget, *label, *label2;
+ char *text;
widget = gtk_list_item_get_child (item);
label = gtk_widget_get_first_child (widget);
label2 = gtk_widget_get_next_sibling (label);
+ gtk_label_set_use_markup (GTK_LABEL (label), FALSE);
+
if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording))
{
- char *ts;
-
gtk_label_set_label (GTK_LABEL (label), "Frame");
gtk_label_set_use_markup (GTK_LABEL (label), FALSE);
- ts = g_strdup_printf ("%.3f", gtk_inspector_recording_get_timestamp (recording) / 1000.0);
- gtk_label_set_label (GTK_LABEL (label2), ts);
- g_free (ts);
+
+ text = g_strdup_printf ("%.3f", gtk_inspector_recording_get_timestamp (recording) / 1000.0);
+ gtk_label_set_label (GTK_LABEL (label2), text);
+ g_free (text);
+ }
+ else if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording))
+ {
+ GdkEvent *event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording));
+
+ text = get_event_summary (event);
+ gtk_label_set_label (GTK_LABEL (label), text);
+ g_free (text);
+
+ text = g_strdup_printf ("%.3f", gtk_inspector_recording_get_timestamp (recording) / 1000.0);
+ gtk_label_set_label (GTK_LABEL (label2), text);
+ g_free (text);
}
else
{
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_clip_button);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, node_property_tree);
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recording_data_stack);
+ gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, event_view);
+ gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, event_property_tree);
gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all);
gtk_widget_class_bind_template_callback (widget_class, recording_selected);
recorder->render_node_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE));
gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->node_property_tree), recorder->render_node_properties);
g_object_unref (recorder->render_node_properties);
+
+ recorder->event_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE));
+ gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->event_property_tree), recorder->event_properties);
+ g_object_unref (recorder->event_properties);
}
static void
g_object_unref (recording);
}
+void
+gtk_inspector_recorder_record_event (GtkInspectorRecorder *recorder,
+ GtkWidget *widget,
+ GdkEvent *event)
+{
+ GtkInspectorRecording *recording;
+ GdkFrameClock *frame_clock;
+ gint64 frame_time;
+
+ if (!gtk_inspector_recorder_is_recording (recorder))
+ return;
+
+ frame_clock = gtk_widget_get_frame_clock (widget);
+ frame_time = gdk_frame_clock_get_frame_time (frame_clock);
+
+ if (recorder->start_time == 0)
+ {
+ recorder->start_time = frame_time;
+ frame_time = 0;
+ }
+ else
+ {
+ frame_time = frame_time - recorder->start_time;
+ }
+
+ recording = gtk_inspector_event_recording_new (frame_time, event);
+ gtk_inspector_recorder_add_recording (recorder, recording);
+ g_object_unref (recording);
+}
+
void
gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder,
gboolean debug_nodes)