Merge branch 'event-recorder' into 'main'

inspector: Tweaks to the recorder

See merge request GNOME/gtk!4256
This commit is contained in:
Matthias Clasen 2021-12-15 05:51:28 +00:00
commit 6ab1aff531
10 changed files with 832 additions and 130 deletions

View File

@ -19,7 +19,7 @@
* Modified by the GTK+ Team and others 1997-2000. See the AUTHORS
* file for a list of people on the GTK+ Team. See the ChangeLog
* files for a list of changes. These files are distributed with
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
#include "config.h"
@ -522,7 +522,8 @@ _gdk_event_queue_find_first (GdkDisplay *display)
if (pending_motion)
return pending_motion;
if (event->event_type == GDK_MOTION_NOTIFY && (event->flags & GDK_EVENT_FLUSHED) == 0)
if ((event->event_type == GDK_MOTION_NOTIFY || event->event_type == GDK_SCROLL) &&
(event->flags & GDK_EVENT_FLUSHED) == 0)
pending_motion = tmp_list;
else
return tmp_list;
@ -596,6 +597,9 @@ _gdk_event_unqueue (GdkDisplay *display)
/*
* If the last N events in the event queue are smooth scroll events
* for the same surface and device, combine them into one.
*
* We give the remaining event a history with N items, and deltas
* that are the sum over the history entries.
*/
void
gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
@ -605,7 +609,6 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
GdkDevice *device = NULL;
GdkEvent *last_event = NULL;
GList *scrolls = NULL;
double delta_x, delta_y;
GArray *history = NULL;
GdkTimeCoord hist;
@ -640,35 +643,42 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
l = l->prev;
}
delta_x = delta_y = 0;
while (scrolls && scrolls->next != NULL)
{
GdkEvent *event = scrolls->data;
GList *next = scrolls->next;
double dx, dy;
gboolean inherited = FALSE;
if (!history && ((GdkScrollEvent *)event)->history)
{
history = ((GdkScrollEvent *)event)->history;
((GdkScrollEvent *)event)->history = NULL;
inherited = TRUE;
}
if (!history)
history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
gdk_scroll_event_get_deltas (event, &dx, &dy);
delta_x += dx;
delta_y += dy;
if (!inherited)
{
gdk_scroll_event_get_deltas (event, &dx, &dy);
memset (&hist, 0, sizeof (GdkTimeCoord));
hist.time = gdk_event_get_time (event);
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
hist.axes[GDK_AXIS_DELTA_X] = dx;
hist.axes[GDK_AXIS_DELTA_Y] = dy;
memset (&hist, 0, sizeof (GdkTimeCoord));
hist.time = gdk_event_get_time (event);
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
hist.axes[GDK_AXIS_DELTA_X] = dx;
hist.axes[GDK_AXIS_DELTA_Y] = dy;
g_array_append_val (history, hist);
g_array_append_val (history, hist);
}
gdk_event_unref (event);
g_queue_delete_link (&display->queued_events, scrolls);
scrolls = next;
}
if (scrolls)
if (scrolls && history)
{
GdkEvent *old_event, *event;
double dx, dy;
@ -676,13 +686,29 @@ gdk_event_queue_handle_scroll_compression (GdkDisplay *display)
old_event = scrolls->data;
gdk_scroll_event_get_deltas (old_event, &dx, &dy);
memset (&hist, 0, sizeof (GdkTimeCoord));
hist.time = gdk_event_get_time (old_event);
hist.flags = GDK_AXIS_FLAG_DELTA_X | GDK_AXIS_FLAG_DELTA_Y;
hist.axes[GDK_AXIS_DELTA_X] = dx;
hist.axes[GDK_AXIS_DELTA_Y] = dy;
g_array_append_val (history, hist);
dx = dy = 0;
for (int i = 0; i < history->len; i++)
{
GdkTimeCoord *val = &g_array_index (history, GdkTimeCoord, i);
dx += val->axes[GDK_AXIS_DELTA_X];
dy += val->axes[GDK_AXIS_DELTA_Y];
}
event = gdk_scroll_event_new (surface,
device,
gdk_event_get_device_tool (old_event),
gdk_event_get_time (old_event),
gdk_event_get_modifier_state (old_event),
delta_x + dx,
delta_y + dy,
dx,
dy,
gdk_scroll_event_is_stop (old_event));
((GdkScrollEvent *)event)->history = history;
@ -714,24 +740,41 @@ gdk_motion_event_push_history (GdkEvent *event,
g_assert (GDK_IS_EVENT_TYPE (event, GDK_MOTION_NOTIFY));
g_assert (GDK_IS_EVENT_TYPE (history_event, GDK_MOTION_NOTIFY));
if (!self->tool)
return;
if (G_UNLIKELY (!self->history))
self->history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
if (((GdkMotionEvent *)history_event)->history)
{
GArray *history = ((GdkMotionEvent *)history_event)->history;
g_array_append_vals (self->history, history->data, history->len);
}
tool = gdk_event_get_device_tool (history_event);
memset (&hist, 0, sizeof (GdkTimeCoord));
hist.time = gdk_event_get_time (history_event);
hist.flags = gdk_device_tool_get_axes (tool);
for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
gdk_event_get_axis (history_event, i, &hist.axes[i]);
if (G_UNLIKELY (!self->history))
self->history = g_array_new (FALSE, TRUE, sizeof (GdkTimeCoord));
if (tool)
{
hist.flags = gdk_device_tool_get_axes (tool);
for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
gdk_event_get_axis (history_event, i, &hist.axes[i]);
}
else
{
hist.flags = GDK_AXIS_FLAG_X | GDK_AXIS_FLAG_Y;
gdk_event_get_position (history_event, &hist.axes[GDK_AXIS_X], &hist.axes[GDK_AXIS_Y]);
}
g_array_append_val (self->history, hist);
}
/* If the last N events in the event queue are motion notify
* events for the same surface, drop all but the last.
*
* If a button is held down or the device has a tool, then
* we give the remaining events a history containing the N-1
* dropped events.
*/
void
_gdk_event_queue_handle_motion_compression (GdkDisplay *display)
{
@ -741,9 +784,6 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
GdkDevice *pending_motion_device = NULL;
GdkEvent *last_motion = NULL;
/* If the last N events in the event queue are motion notify
* events for the same surface, drop all but the last */
tmp_list = g_queue_peek_tail_link (&display->queued_events);
while (tmp_list)
@ -780,12 +820,11 @@ _gdk_event_queue_handle_motion_compression (GdkDisplay *display)
if (last_motion != NULL)
{
GdkModifierType state = gdk_event_get_modifier_state (last_motion);
if (state &
(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK |
GDK_BUTTON4_MASK | GDK_BUTTON5_MASK))
gdk_motion_event_push_history (last_motion, pending_motions->data);
if ((gdk_event_get_modifier_state (last_motion) &
(GDK_BUTTON1_MASK | GDK_BUTTON2_MASK | GDK_BUTTON3_MASK |
GDK_BUTTON4_MASK | GDK_BUTTON5_MASK)) ||
gdk_event_get_device_tool (last_motion) != NULL)
gdk_motion_event_push_history (last_motion, pending_motions->data);
}
gdk_event_unref (pending_motions->data);
@ -2907,7 +2946,8 @@ gdk_motion_event_new (GdkSurface *surface,
* to the application because they occurred in the same frame as @event.
*
* Note that only motion and scroll events record history, and motion
* events do it only if one of the mouse buttons is down.
* events do it only if one of the mouse buttons is down, or the device
* has a tool.
*
* Returns: (transfer container) (array length=out_n_coords) (nullable): an
* array of time and coordinates

View File

@ -209,7 +209,7 @@ struct _GdkTouchEvent
* @pointer_emulated: whether the scroll event was the result of
* a pointer emulation
* @tool: a `GdkDeviceTool`
* @history: (element-type GdkScrollHistory): array of times and deltas
* @history: (element-type GdkTimeCoord): array of times and deltas
* for other scroll events that were compressed before delivering the
* current event
*
@ -233,7 +233,7 @@ struct _GdkScrollEvent
gboolean pointer_emulated;
gboolean is_stop;
GdkDeviceTool *tool;
GArray *history; /* <GdkScrollHistory> */
GArray *history; /* <GdkTimeCoord> */
};
/*

View File

@ -181,7 +181,7 @@ init_formats (GtkInspectorClipboard *self,
gtk_list_box_remove (list, GTK_WIDGET (row));
formats = gdk_clipboard_get_formats (clipboard);
gtypes = gdk_content_formats_get_gtypes (formats, &n);
for (i = 0; i < n; i++)
add_content_type_row (self, list, g_type_name (gtypes[i]), clipboard, G_CALLBACK (load_gtype), GSIZE_TO_POINTER (gtypes[i]));
@ -235,7 +235,6 @@ primary_notify (GdkClipboard *clipboard,
init_formats (self, GTK_LIST_BOX (self->primary_formats), clipboard);
}
g_print ("%s: %s\n", pspec->name, gdk_content_formats_to_string (gdk_clipboard_get_formats (clipboard)));
init_info (self, GTK_LABEL (self->primary_info), clipboard);
}

View File

@ -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:

View File

@ -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:

View File

@ -9,6 +9,7 @@ inspector_sources = files(
'controllers.c',
'css-editor.c',
'css-node-tree.c',
'eventrecording.c',
'focusoverlay.c',
'fpsoverlay.c',
'general.c',

View File

@ -39,6 +39,7 @@
#include <gtk/gtktreelistmodel.h>
#include <gtk/gtktreemodel.h>
#include <gtk/gtktreeview.h>
#include <gtk/gtkstack.h>
#include <gsk/gskrendererprivate.h>
#include <gsk/gskrendernodeprivate.h>
#include <gsk/gskroundedrectprivate.h>
@ -53,6 +54,7 @@
#include "recording.h"
#include "renderrecording.h"
#include "startrecording.h"
#include "eventrecording.h"
struct _GtkInspectorRecorder
{
@ -70,9 +72,14 @@ struct _GtkInspectorRecorder
GtkWidget *render_node_save_button;
GtkWidget *render_node_clip_button;
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;
gboolean debug_nodes;
};
@ -424,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,
@ -432,29 +508,51 @@ recording_selected (GtkSingleSelection *selection,
GtkInspectorRecording *recording;
if (recorder->recordings == NULL)
return;
{
gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), "no_data");
return;
}
recording = gtk_single_selection_get_selected_item (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);
}
@ -1161,6 +1259,227 @@ 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;
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));
add_int_row (store, "Timestamp", gdk_event_get_time (event));
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 */
;
}
if (type == GDK_MOTION_NOTIFY || type == GDK_SCROLL)
{
GdkTimeCoord *history;
guint n_coords;
history = gdk_event_get_history (event, &n_coords);
if (history)
{
GString *s;
s = g_string_new ("");
for (int i = 0; i < n_coords; i++)
{
if (i > 0)
g_string_append (s, "\n");
if ((history[i].flags & (GDK_AXIS_FLAG_X|GDK_AXIS_FLAG_Y)) == (GDK_AXIS_FLAG_X|GDK_AXIS_FLAG_Y))
g_string_append_printf (s, "%d: %.2f %.2f", history[i].time, history[i].axes[GDK_AXIS_X], history[i].axes[GDK_AXIS_Y]);
if ((history[i].flags & (GDK_AXIS_FLAG_DELTA_X|GDK_AXIS_FLAG_DELTA_Y)) == (GDK_AXIS_FLAG_DELTA_X|GDK_AXIS_FLAG_DELTA_Y))
g_string_append_printf (s, "%d: %.2f %.2f", history[i].time, history[i].axes[GDK_AXIS_DELTA_X], history[i].axes[GDK_AXIS_DELTA_Y]);
}
add_text_row (store, "History", s->str);
g_string_free (s, TRUE);
g_free (history);
}
}
}
static GskRenderNode *
get_selected_node (GtkInspectorRecorder *recorder)
{
@ -1324,27 +1643,16 @@ setup_widget_for_recording (GtkListItemFactory *factory,
GtkListItem *item,
gpointer data)
{
GtkWidget *widget, *hbox, *label, *button;
GtkWidget *widget, *label;
widget = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
hbox = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
gtk_box_append (GTK_BOX (widget), hbox);
widget = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 6);
label = gtk_label_new ("");
gtk_label_set_xalign (GTK_LABEL (label), 0.0f);
gtk_widget_set_hexpand (label, TRUE);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_box_append (GTK_BOX (hbox), label);
button = gtk_toggle_button_new ();
gtk_button_set_has_frame (GTK_BUTTON (button), FALSE);
gtk_button_set_icon_name (GTK_BUTTON (button), "view-more-symbolic");
gtk_box_append (GTK_BOX (hbox), button);
label = gtk_label_new ("");
gtk_widget_hide (label);
gtk_box_append (GTK_BOX (widget), label);
g_object_bind_property (button, "active", label, "visible", 0);
label = gtk_label_new ("");
gtk_box_append (GTK_BOX (widget), label);
gtk_widget_set_margin_start (widget, 6);
gtk_widget_set_margin_end (widget, 6);
@ -1354,32 +1662,116 @@ 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,
gpointer data)
{
GtkInspectorRecording *recording = gtk_list_item_get_item (item);
GtkWidget *widget, *hbox, *label, *button, *label2;
GtkWidget *widget, *label, *label2;
char *text;
widget = gtk_list_item_get_child (item);
hbox = gtk_widget_get_first_child (widget);
label = gtk_widget_get_first_child (hbox);
button = gtk_widget_get_next_sibling (label);
label2 = gtk_widget_get_next_sibling (hbox);
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))
{
gtk_label_set_label (GTK_LABEL (label), "<b>Frame</b>");
gtk_widget_show (button);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
gtk_label_set_label (GTK_LABEL (label2), gtk_inspector_render_recording_get_profiler_info (GTK_INSPECTOR_RENDER_RECORDING (recording)));
gtk_label_set_label (GTK_LABEL (label), "Frame");
gtk_label_set_use_markup (GTK_LABEL (label), FALSE);
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_label_set_label (GTK_LABEL (label), "<b>Start of Recording</b>");
gtk_widget_hide (button);
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (button), FALSE);
gtk_label_set_use_markup (GTK_LABEL (label), TRUE);
gtk_label_set_label (GTK_LABEL (label2), "");
}
}
@ -1521,6 +1913,9 @@ gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass)
gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_save_button);
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);
@ -1540,8 +1935,8 @@ gtk_inspector_recorder_init (GtkInspectorRecorder *recorder)
gtk_widget_init_template (GTK_WIDGET (recorder));
factory = gtk_signal_list_item_factory_new ();
g_signal_connect (factory, "setup", G_CALLBACK (setup_widget_for_recording), NULL);
g_signal_connect (factory, "bind", G_CALLBACK (bind_widget_for_recording), NULL);
g_signal_connect (factory, "setup", G_CALLBACK (setup_widget_for_recording), recorder);
g_signal_connect (factory, "bind", G_CALLBACK (bind_widget_for_recording), recorder);
gtk_list_view_set_factory (GTK_LIST_VIEW (recorder->recordings_list), factory);
g_object_unref (factory);
@ -1566,13 +1961,16 @@ 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 (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
gtk_inspector_recorder_add_recording (GtkInspectorRecorder *recorder,
GtkInspectorRecording *recording)
{
g_print ("appending %s\n", G_OBJECT_TYPE_NAME (recording));
g_list_store_append (G_LIST_STORE (recorder->recordings), recording);
}
@ -1586,6 +1984,7 @@ gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder,
if (recording)
{
recorder->recording = gtk_inspector_start_recording_new ();
recorder->start_time = 0;
gtk_inspector_recorder_add_recording (recorder, recorder->recording);
}
else
@ -1612,13 +2011,25 @@ gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder,
{
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);
recording = gtk_inspector_render_recording_new (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_render_recording_new (frame_time,
gsk_renderer_get_profiler (renderer),
&(GdkRectangle) { 0, 0,
gdk_surface_get_width (surface),
@ -1629,6 +2040,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)

View File

@ -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_

View File

@ -86,76 +86,155 @@
<object class="GtkSeparator"/>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="hscrollbar-policy">never</property>
<property name="propagate-natural-width">1</property>
<object class="GtkStack" id="recording_data_stack">
<child>
<object class="GtkListView" id="render_node_list">
<property name="vexpand">1</property>
<property name="hexpand">1</property>
<object class="GtkStackPage">
<property name="name">no_data</property>
<property name="child">
<object class="GtkLabel">
<property name="label">No data.</property>
<property name="halign">center</property>
<property name="valign">center</property>
</object>
</property>
</object>
</child>
</object>
</child>
<child>
<object class="GtkSeparator"/>
</child>
<child>
<object class="GtkPaned" id="render_paned">
<property name="orientation">vertical</property>
<property name="position">300</property>
<property name="wide-handle">1</property>
<child>
<object class="GtkScrolledWindow">
<child>
<object class="GtkTreeView" id="node_property_tree">
<property name="activate-on-single-click">1</property>
<signal name="row-activated" handler="node_property_activated"/>
<object class="GtkStackPage">
<property name="name">frame_data</property>
<property name="child">
<object class="GtkPaned">
<property name="position">400</property>
<property name="wide-handle">1</property>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Property</property>
<object class="GtkScrolledWindow">
<property name="propagate-natural-width">1</property>
<child>
<object class="GtkCellRendererText">
<property name="yalign">0</property>
<object class="GtkListView" id="render_node_list">
<property name="vexpand">1</property>
<property name="hexpand">1</property>
</object>
<attributes>
<attribute name="text">0</attribute>
</attributes>
</child>
</object>
</child>
<child>
<object class="GtkTreeViewColumn">
<property name="title" translatable="yes">Value</property>
<object class="GtkPaned" id="render_paned">
<property name="orientation">vertical</property>
<property name="position">200</property>
<property name="wide-handle">1</property>
<child>
<object class="GtkCellRendererText">
<property name="yalign">0</property>
<property name="wrap-mode">word</property>
<property name="max-width-chars">50</property>
<object class="GtkScrolledWindow">
<property name="propagate-natural-width">1</property>
<child>
<object class="GtkTreeView" id="node_property_tree">
<property name="activate-on-single-click">1</property>
<signal name="row-activated" handler="node_property_activated"/>
<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>
<child>
<object class="GtkCellRendererPixbuf">
<property name="xalign">0</property>
</object>
<attributes>
<attribute name="visible">2</attribute>
<attribute name="texture">3</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</object>
<attributes>
<attribute name="text">1</attribute>
</attributes>
</child>
<child>
<object class="GtkCellRendererPixbuf">
<property name="xalign">0</property>
<object class="GtkPicture" id="render_node_view">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
</object>
<attributes>
<attribute name="visible">2</attribute>
<attribute name="texture">3</attribute>
</attributes>
</child>
</object>
</child>
</object>
</child>
</property>
</object>
</child>
<child>
<object class="GtkPicture" id="render_node_view">
<property name="hexpand">1</property>
<property name="vexpand">1</property>
<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>
@ -166,3 +245,4 @@
</child>
</template>
</interface>

View File

@ -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;