[gtk/event-recorder: 5/5] inspector: Add event recording




commit 46c08f6a84076654e401aa6b5945dfa5a34e73ad
Author: Matthias Clasen <mclasen redhat com>
Date:   Tue Dec 14 21:52:20 2021 -0500

    inspector: Add event recording
    
    Make the recorder also keep track of events,
    and show them in some detail.

 gtk/inspector/eventrecording.c |  69 +++++++
 gtk/inspector/eventrecording.h |  64 +++++++
 gtk/inspector/meson.build      |   1 +
 gtk/inspector/recorder.c       | 425 +++++++++++++++++++++++++++++++++++++++--
 gtk/inspector/recorder.h       |   4 +
 gtk/inspector/recorder.ui      |  56 ++++++
 gtk/inspector/window.c         |   8 +-
 7 files changed, 610 insertions(+), 17 deletions(-)
---
diff --git a/gtk/inspector/eventrecording.c b/gtk/inspector/eventrecording.c
new file mode 100644
index 0000000000..d24d51455a
--- /dev/null
+++ b/gtk/inspector/eventrecording.c
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#include "eventrecording.h"
+
+G_DEFINE_TYPE (GtkInspectorEventRecording, gtk_inspector_event_recording, GTK_TYPE_INSPECTOR_RECORDING)
+
+static void
+gtk_inspector_event_recording_finalize (GObject *object)
+{
+  GtkInspectorEventRecording *recording = GTK_INSPECTOR_EVENT_RECORDING (object);
+
+  g_clear_pointer (&recording->event, gdk_event_unref);
+
+  G_OBJECT_CLASS (gtk_inspector_event_recording_parent_class)->finalize (object);
+}
+
+static void
+gtk_inspector_event_recording_class_init (GtkInspectorEventRecordingClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gtk_inspector_event_recording_finalize;
+}
+
+static void
+gtk_inspector_event_recording_init (GtkInspectorEventRecording *vis)
+{
+}
+
+GtkInspectorRecording *
+gtk_inspector_event_recording_new (gint64    timestamp,
+                                   GdkEvent *event)
+{
+  GtkInspectorEventRecording *recording;
+
+  recording = g_object_new (GTK_TYPE_INSPECTOR_EVENT_RECORDING,
+                            "timestamp", timestamp,
+                            NULL);
+
+  recording->event = gdk_event_ref (event);
+
+  return GTK_INSPECTOR_RECORDING (recording);
+}
+
+GdkEvent *
+gtk_inspector_event_recording_get_event (GtkInspectorEventRecording *recording)
+{
+  return recording->event;
+}
+
+// vim: set et sw=2 ts=2:
diff --git a/gtk/inspector/eventrecording.h b/gtk/inspector/eventrecording.h
new file mode 100644
index 0000000000..11c126c6a0
--- /dev/null
+++ b/gtk/inspector/eventrecording.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2021 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef _GTK_INSPECTOR_EVENT_RECORDING_H_
+#define _GTK_INSPECTOR_EVENT_RECORDING_H_
+
+#include <gdk/gdk.h>
+#include <gsk/gsk.h>
+#include "gsk/gskprofilerprivate.h"
+
+#include "inspector/recording.h"
+
+G_BEGIN_DECLS
+
+#define GTK_TYPE_INSPECTOR_EVENT_RECORDING            (gtk_inspector_event_recording_get_type())
+#define GTK_INSPECTOR_EVENT_RECORDING(obj)            (G_TYPE_CHECK_INSTANCE_CAST((obj), 
GTK_TYPE_INSPECTOR_EVENT_RECORDING, GtkInspectorEventRecording))
+#define GTK_INSPECTOR_EVENT_RECORDING_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass), 
GTK_TYPE_INSPECTOR_EVENT_RECORDING, GtkInspectorEventRecordingClass))
+#define GTK_INSPECTOR_IS_EVENT_RECORDING(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj), 
GTK_TYPE_INSPECTOR_EVENT_RECORDING))
+#define GTK_INSPECTOR_IS_EVENT_RECORDING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), 
GTK_TYPE_INSPECTOR_EVENT_RECORDING))
+#define GTK_INSPECTOR_EVENT_RECORDING_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), 
GTK_TYPE_INSPECTOR_EVENT_RECORDING, GtkInspectorEventRecordingClass))
+
+
+typedef struct _GtkInspectorEventRecordingPrivate GtkInspectorEventRecordingPrivate;
+
+typedef struct _GtkInspectorEventRecording
+{
+  GtkInspectorRecording parent;
+
+  GdkEvent *event;
+} GtkInspectorEventRecording;
+
+typedef struct _GtkInspectorEventRecordingClass
+{
+  GtkInspectorRecordingClass parent;
+} GtkInspectorEventRecordingClass;
+
+GType           gtk_inspector_event_recording_get_type      (void);
+
+GtkInspectorRecording *
+                gtk_inspector_event_recording_new            (gint64    timestamp,
+                                                              GdkEvent *event);
+
+GdkEvent *      gtk_inspector_event_recording_get_event      (GtkInspectorEventRecording       *recording);
+
+
+G_END_DECLS
+
+#endif // _GTK_INSPECTOR_EVENT_RECORDING_H_
+
+// vim: set et sw=2 ts=2:
diff --git a/gtk/inspector/meson.build b/gtk/inspector/meson.build
index 422eab7f24..08031b2c9a 100644
--- a/gtk/inspector/meson.build
+++ b/gtk/inspector/meson.build
@@ -9,6 +9,7 @@ inspector_sources = files(
   'controllers.c',
   'css-editor.c',
   'css-node-tree.c',
+  'eventrecording.c',
   'focusoverlay.c',
   'fpsoverlay.c',
   'general.c',
diff --git a/gtk/inspector/recorder.c b/gtk/inspector/recorder.c
index b7b423f11c..2ff097b615 100644
--- a/gtk/inspector/recorder.c
+++ b/gtk/inspector/recorder.c
@@ -54,6 +54,7 @@
 #include "recording.h"
 #include "renderrecording.h"
 #include "startrecording.h"
+#include "eventrecording.h"
 
 struct _GtkInspectorRecorder
 {
@@ -73,6 +74,9 @@ 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;
@@ -427,6 +431,75 @@ bind_widget_for_render_node (GtkSignalListItemFactory *factory,
   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,
@@ -444,26 +517,42 @@ recording_selected (GtkSingleSelection   *selection,
 
   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);
     }
@@ -1170,6 +1259,192 @@ populate_render_node_properties (GtkListStore  *store,
     }
 }
 
+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;
+  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));
+
+  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)
 {
@@ -1352,6 +1627,76 @@ setup_widget_for_recording (GtkListItemFactory *factory,
   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,
@@ -1359,20 +1704,34 @@ bind_widget_for_recording (GtkListItemFactory *factory,
 {
   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
     {
@@ -1520,6 +1879,8 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
   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);
@@ -1565,6 +1926,10 @@ gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
   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 (2, G_TYPE_STRING, G_TYPE_STRING));
+  gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->event_property_tree), recorder->event_properties);
+  g_object_unref (recorder->event_properties);
 }
 
 static void
@@ -1640,6 +2005,36 @@ gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder,
   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)
diff --git a/gtk/inspector/recorder.h b/gtk/inspector/recorder.h
index 925aa3eeb5..be5260879b 100644
--- a/gtk/inspector/recorder.h
+++ b/gtk/inspector/recorder.h
@@ -44,6 +44,10 @@ void            gtk_inspector_recorder_record_render            (GtkInspectorRec
                                                                  const cairo_region_t   *region,
                                                                  GskRenderNode          *node);
 
+void            gtk_inspector_recorder_record_event             (GtkInspectorRecorder   *recorder,
+                                                                 GtkWidget              *widget,
+                                                                 GdkEvent               *event);
+
 G_END_DECLS
 
 #endif // _GTK_INSPECTOR_RECORDER_H_
diff --git a/gtk/inspector/recorder.ui b/gtk/inspector/recorder.ui
index cc2eb4a2a6..4accd7fa44 100644
--- a/gtk/inspector/recorder.ui
+++ b/gtk/inspector/recorder.ui
@@ -182,6 +182,61 @@
                     </property>
                   </object>
                 </child>
+                <child>
+                  <object class="GtkStackPage">
+                    <property name="name">event_data</property>
+                    <property name="child">
+                      <object class="GtkPaned">
+                        <property name="position">400</property>
+                        <property name="wide-handle">1</property>
+                        <child>
+                          <object class="GtkScrolledWindow">
+                            <property name="propagate-natural-width">1</property>
+                            <child>
+                              <object class="GtkTreeView" id="event_property_tree">
+                                <property name="activate-on-single-click">1</property>
+                                <child>
+                                  <object class="GtkTreeViewColumn">
+                                    <property name="title" translatable="yes">Property</property>
+                                    <child>
+                                      <object class="GtkCellRendererText">
+                                        <property name="yalign">0</property>
+                                      </object>
+                                      <attributes>
+                                        <attribute name="text">0</attribute>
+                                      </attributes>
+                                    </child>
+                                  </object>
+                                </child>
+                                <child>
+                                  <object class="GtkTreeViewColumn">
+                                    <property name="title" translatable="yes">Value</property>
+                                    <child>
+                                      <object class="GtkCellRendererText">
+                                        <property name="yalign">0</property>
+                                        <property name="wrap-mode">word</property>
+                                        <property name="max-width-chars">50</property>
+                                      </object>
+                                      <attributes>
+                                        <attribute name="text">1</attribute>
+                                      </attributes>
+                                    </child>
+                                  </object>
+                                </child>
+                              </object>
+                            </child>
+                          </object>
+                        </child>
+                        <child>
+                          <object class="GtkPicture" id="event_view">
+                            <property name="hexpand">1</property>
+                            <property name="vexpand">1</property>
+                          </object>
+                        </child>
+                      </object>
+                    </property>
+                  </object>
+                </child>
               </object>
             </child>
           </object>
@@ -190,3 +245,4 @@
     </child>
   </template>
 </interface>
+
diff --git a/gtk/inspector/window.c b/gtk/inspector/window.c
index 8b07fb3de3..a950233424 100644
--- a/gtk/inspector/window.c
+++ b/gtk/inspector/window.c
@@ -184,7 +184,7 @@ open_object_details (GtkWidget *button, GtkInspectorWindow *iw)
   GObject *selected;
 
   selected = gtk_inspector_object_tree_get_selected (GTK_INSPECTOR_OBJECT_TREE (iw->object_tree));
- 
+
   gtk_inspector_window_set_object (iw, selected, CHILD_KIND_WIDGET, 0);
 
   gtk_stack_set_visible_child_name (GTK_STACK (iw->object_stack), "object-details");
@@ -752,7 +752,7 @@ gtk_inspector_window_get (GdkDisplay *display)
   iw = GTK_WIDGET (g_object_get_data (G_OBJECT (display), "-gtk-inspector"));
 
   if (!iw)
-    iw = GTK_WIDGET (gtk_inspector_window_new (display)); 
+    iw = GTK_WIDGET (gtk_inspector_window_new (display));
 
   return iw;
 }
@@ -873,6 +873,10 @@ gtk_inspector_handle_event (GdkEvent *event)
   if (iw == NULL)
     return FALSE;
 
+  gtk_inspector_recorder_record_event (GTK_INSPECTOR_RECORDER (iw->widget_recorder),
+                                       gtk_get_event_widget (event),
+                                       event);
+
   g_signal_emit (iw, signals[EVENT], 0, event, &handled);
 
   return handled;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]