1 | /* |
2 | * Copyright (c) 2016 Red Hat, Inc. |
3 | * |
4 | * This library is free software; you can redistribute it and/or |
5 | * modify it under the terms of the GNU Lesser General Public |
6 | * License as published by the Free Software Foundation; either |
7 | * version 2 of the License, or (at your option) any later version. |
8 | * |
9 | * This library is distributed in the hope that it will be useful, |
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
12 | * Lesser General Public License for more details. |
13 | * |
14 | * You should have received a copy of the GNU Lesser General Public |
15 | * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
16 | */ |
17 | |
18 | #include "config.h" |
19 | |
20 | #include "recorder.h" |
21 | |
22 | #include <gtk/gtkbinlayout.h> |
23 | #include <gtk/gtkbox.h> |
24 | #include <gtk/gtkdragsource.h> |
25 | #include <gtk/gtkeventcontroller.h> |
26 | #include <gtk/gtkfilechooserdialog.h> |
27 | #include <gtk/gtksignallistitemfactory.h> |
28 | #include <gtk/gtklabel.h> |
29 | #include <gtk/gtklistbox.h> |
30 | #include <gtk/gtklistitem.h> |
31 | #include <gtk/gtklistview.h> |
32 | #include <gtk/gtkliststore.h> |
33 | #include <gtk/gtkmessagedialog.h> |
34 | #include <gtk/gtkpicture.h> |
35 | #include <gtk/gtkpopover.h> |
36 | #include <gtk/gtksingleselection.h> |
37 | #include <gtk/gtktogglebutton.h> |
38 | #include <gtk/gtktreeexpander.h> |
39 | #include <gtk/gtktreelistmodel.h> |
40 | #include <gtk/gtktreemodel.h> |
41 | #include <gtk/gtktreeview.h> |
42 | #include <gtk/gtkstack.h> |
43 | #include <gsk/gskrendererprivate.h> |
44 | #include <gsk/gskrendernodeprivate.h> |
45 | #include <gsk/gskroundedrectprivate.h> |
46 | #include <gsk/gsktransformprivate.h> |
47 | |
48 | #include <glib/gi18n-lib.h> |
49 | #include <gdk/gdktextureprivate.h> |
50 | #include "gtk/gtkdebug.h" |
51 | #include "gtk/gtkbuiltiniconprivate.h" |
52 | #include "gtk/gtkrendernodepaintableprivate.h" |
53 | |
54 | #include "recording.h" |
55 | #include "renderrecording.h" |
56 | #include "startrecording.h" |
57 | #include "eventrecording.h" |
58 | #include "recorderrow.h" |
59 | |
60 | struct _GtkInspectorRecorder |
61 | { |
62 | GtkWidget parent; |
63 | |
64 | GListModel *recordings; |
65 | GtkTreeListModel *render_node_model; |
66 | GListStore *render_node_root_model; |
67 | GtkSingleSelection *render_node_selection; |
68 | |
69 | GtkWidget *box; |
70 | GtkWidget *recordings_list; |
71 | GtkWidget *render_node_view; |
72 | GtkWidget *render_node_list; |
73 | GtkWidget *render_node_save_button; |
74 | GtkWidget *render_node_clip_button; |
75 | GtkWidget *node_property_tree; |
76 | GtkWidget *recording_data_stack; |
77 | GtkTreeModel *render_node_properties; |
78 | GtkTreeModel *event_properties; |
79 | GtkWidget *event_property_tree; |
80 | GtkWidget *event_view; |
81 | |
82 | GtkInspectorRecording *recording; /* start recording if recording or NULL if not */ |
83 | gint64 start_time; |
84 | |
85 | gboolean debug_nodes; |
86 | gboolean highlight_sequences; |
87 | |
88 | GdkEventSequence *selected_sequence; |
89 | }; |
90 | |
91 | typedef struct _GtkInspectorRecorderClass |
92 | { |
93 | GtkWidgetClass parent; |
94 | } GtkInspectorRecorderClass; |
95 | |
96 | |
97 | enum |
98 | { |
99 | PROP_0, |
100 | PROP_RECORDING, |
101 | PROP_DEBUG_NODES, |
102 | PROP_HIGHLIGHT_SEQUENCES, |
103 | PROP_SELECTED_SEQUENCE, |
104 | LAST_PROP |
105 | }; |
106 | |
107 | static GParamSpec *props[LAST_PROP] = { NULL, }; |
108 | |
109 | G_DEFINE_TYPE (GtkInspectorRecorder, gtk_inspector_recorder, GTK_TYPE_WIDGET) |
110 | |
111 | static GListModel * |
112 | create_render_node_list_model (GskRenderNode **nodes, |
113 | guint n_nodes) |
114 | { |
115 | GListStore *store; |
116 | guint i; |
117 | |
118 | /* can't put render nodes into list models - they're not GObjects */ |
119 | store = g_list_store_new (GDK_TYPE_PAINTABLE); |
120 | |
121 | for (i = 0; i < n_nodes; i++) |
122 | { |
123 | graphene_rect_t bounds; |
124 | |
125 | gsk_render_node_get_bounds (node: nodes[i], bounds: &bounds); |
126 | GdkPaintable *paintable = gtk_render_node_paintable_new (node: nodes[i], bounds: &bounds); |
127 | g_list_store_append (store, item: paintable); |
128 | g_object_unref (object: paintable); |
129 | } |
130 | |
131 | return G_LIST_MODEL (ptr: store); |
132 | } |
133 | |
134 | static GListModel * |
135 | create_list_model_for_render_node (GskRenderNode *node) |
136 | { |
137 | switch (gsk_render_node_get_node_type (node)) |
138 | { |
139 | default: |
140 | case GSK_NOT_A_RENDER_NODE: |
141 | g_assert_not_reached (); |
142 | return NULL; |
143 | |
144 | case GSK_CAIRO_NODE: |
145 | case GSK_TEXT_NODE: |
146 | case GSK_TEXTURE_NODE: |
147 | case GSK_COLOR_NODE: |
148 | case GSK_LINEAR_GRADIENT_NODE: |
149 | case GSK_REPEATING_LINEAR_GRADIENT_NODE: |
150 | case GSK_RADIAL_GRADIENT_NODE: |
151 | case GSK_REPEATING_RADIAL_GRADIENT_NODE: |
152 | case GSK_CONIC_GRADIENT_NODE: |
153 | case GSK_BORDER_NODE: |
154 | case GSK_INSET_SHADOW_NODE: |
155 | case GSK_OUTSET_SHADOW_NODE: |
156 | /* no children */ |
157 | return NULL; |
158 | |
159 | case GSK_TRANSFORM_NODE: |
160 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_transform_node_get_child (node) }, n_nodes: 1); |
161 | |
162 | case GSK_OPACITY_NODE: |
163 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_opacity_node_get_child (node) }, n_nodes: 1); |
164 | |
165 | case GSK_COLOR_MATRIX_NODE: |
166 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_color_matrix_node_get_child (node) }, n_nodes: 1); |
167 | |
168 | case GSK_BLUR_NODE: |
169 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_blur_node_get_child (node) }, n_nodes: 1); |
170 | |
171 | case GSK_REPEAT_NODE: |
172 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_repeat_node_get_child (node) }, n_nodes: 1); |
173 | |
174 | case GSK_CLIP_NODE: |
175 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_clip_node_get_child (node) }, n_nodes: 1); |
176 | |
177 | case GSK_ROUNDED_CLIP_NODE: |
178 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_rounded_clip_node_get_child (node) }, n_nodes: 1); |
179 | |
180 | case GSK_SHADOW_NODE: |
181 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_shadow_node_get_child (node) }, n_nodes: 1); |
182 | |
183 | case GSK_BLEND_NODE: |
184 | return create_render_node_list_model (nodes: (GskRenderNode *[2]) { gsk_blend_node_get_bottom_child (node), |
185 | gsk_blend_node_get_top_child (node) }, n_nodes: 2); |
186 | |
187 | case GSK_CROSS_FADE_NODE: |
188 | return create_render_node_list_model (nodes: (GskRenderNode *[2]) { gsk_cross_fade_node_get_start_child (node), |
189 | gsk_cross_fade_node_get_end_child (node) }, n_nodes: 2); |
190 | |
191 | case GSK_GL_SHADER_NODE: |
192 | { |
193 | GListStore *store = g_list_store_new (GDK_TYPE_PAINTABLE); |
194 | |
195 | for (guint i = 0; i < gsk_gl_shader_node_get_n_children (node); i++) |
196 | { |
197 | GskRenderNode *child = gsk_gl_shader_node_get_child (node, idx: i); |
198 | graphene_rect_t bounds; |
199 | GdkPaintable *paintable; |
200 | |
201 | gsk_render_node_get_bounds (node: child, bounds: &bounds); |
202 | paintable = gtk_render_node_paintable_new (node: child, bounds: &bounds); |
203 | g_list_store_append (store, item: paintable); |
204 | g_object_unref (object: paintable); |
205 | } |
206 | |
207 | return G_LIST_MODEL (ptr: store); |
208 | } |
209 | |
210 | case GSK_CONTAINER_NODE: |
211 | { |
212 | GListStore *store; |
213 | guint i; |
214 | |
215 | /* can't put render nodes into list models - they're not GObjects */ |
216 | store = g_list_store_new (GDK_TYPE_PAINTABLE); |
217 | |
218 | for (i = 0; i < gsk_container_node_get_n_children (node); i++) |
219 | { |
220 | GskRenderNode *child = gsk_container_node_get_child (node, idx: i); |
221 | graphene_rect_t bounds; |
222 | GdkPaintable *paintable; |
223 | |
224 | gsk_render_node_get_bounds (node: child, bounds: &bounds); |
225 | paintable = gtk_render_node_paintable_new (node: child, bounds: &bounds); |
226 | g_list_store_append (store, item: paintable); |
227 | g_object_unref (object: paintable); |
228 | } |
229 | |
230 | return G_LIST_MODEL (ptr: store); |
231 | } |
232 | |
233 | case GSK_DEBUG_NODE: |
234 | return create_render_node_list_model (nodes: (GskRenderNode *[1]) { gsk_debug_node_get_child (node) }, n_nodes: 1); |
235 | } |
236 | } |
237 | |
238 | static GListModel * |
239 | create_list_model_for_render_node_paintable (gpointer paintable, |
240 | gpointer unused) |
241 | { |
242 | GskRenderNode *node = gtk_render_node_paintable_get_render_node (self: paintable); |
243 | |
244 | return create_list_model_for_render_node (node); |
245 | } |
246 | |
247 | static void |
248 | recordings_clear_all (GtkButton *button, |
249 | GtkInspectorRecorder *recorder) |
250 | { |
251 | g_list_store_remove_all (store: G_LIST_STORE (ptr: recorder->recordings)); |
252 | } |
253 | |
254 | static const char * |
255 | node_type_name (GskRenderNodeType type) |
256 | { |
257 | switch (type) |
258 | { |
259 | case GSK_NOT_A_RENDER_NODE: |
260 | default: |
261 | g_assert_not_reached (); |
262 | return "Unknown" ; |
263 | case GSK_CONTAINER_NODE: |
264 | return "Container" ; |
265 | case GSK_DEBUG_NODE: |
266 | return "Debug" ; |
267 | case GSK_CAIRO_NODE: |
268 | return "Cairo" ; |
269 | case GSK_COLOR_NODE: |
270 | return "Color" ; |
271 | case GSK_LINEAR_GRADIENT_NODE: |
272 | return "Linear Gradient" ; |
273 | case GSK_REPEATING_LINEAR_GRADIENT_NODE: |
274 | return "Repeating Linear Gradient" ; |
275 | case GSK_RADIAL_GRADIENT_NODE: |
276 | return "Radial Gradient" ; |
277 | case GSK_REPEATING_RADIAL_GRADIENT_NODE: |
278 | return "Repeating Radial Gradient" ; |
279 | case GSK_CONIC_GRADIENT_NODE: |
280 | return "Conic Gradient" ; |
281 | case GSK_BORDER_NODE: |
282 | return "Border" ; |
283 | case GSK_TEXTURE_NODE: |
284 | return "Texture" ; |
285 | case GSK_INSET_SHADOW_NODE: |
286 | return "Inset Shadow" ; |
287 | case GSK_OUTSET_SHADOW_NODE: |
288 | return "Outset Shadow" ; |
289 | case GSK_TRANSFORM_NODE: |
290 | return "Transform" ; |
291 | case GSK_OPACITY_NODE: |
292 | return "Opacity" ; |
293 | case GSK_COLOR_MATRIX_NODE: |
294 | return "Color Matrix" ; |
295 | case GSK_REPEAT_NODE: |
296 | return "Repeat" ; |
297 | case GSK_CLIP_NODE: |
298 | return "Clip" ; |
299 | case GSK_ROUNDED_CLIP_NODE: |
300 | return "Rounded Clip" ; |
301 | case GSK_SHADOW_NODE: |
302 | return "Shadow" ; |
303 | case GSK_BLEND_NODE: |
304 | return "Blend" ; |
305 | case GSK_CROSS_FADE_NODE: |
306 | return "CrossFade" ; |
307 | case GSK_TEXT_NODE: |
308 | return "Text" ; |
309 | case GSK_BLUR_NODE: |
310 | return "Blur" ; |
311 | case GSK_GL_SHADER_NODE: |
312 | return "GL Shader" ; |
313 | } |
314 | } |
315 | |
316 | static char * |
317 | node_name (GskRenderNode *node) |
318 | { |
319 | switch (gsk_render_node_get_node_type (node)) |
320 | { |
321 | case GSK_NOT_A_RENDER_NODE: |
322 | default: |
323 | g_assert_not_reached (); |
324 | case GSK_CONTAINER_NODE: |
325 | case GSK_CAIRO_NODE: |
326 | case GSK_LINEAR_GRADIENT_NODE: |
327 | case GSK_REPEATING_LINEAR_GRADIENT_NODE: |
328 | case GSK_RADIAL_GRADIENT_NODE: |
329 | case GSK_REPEATING_RADIAL_GRADIENT_NODE: |
330 | case GSK_CONIC_GRADIENT_NODE: |
331 | case GSK_BORDER_NODE: |
332 | case GSK_INSET_SHADOW_NODE: |
333 | case GSK_OUTSET_SHADOW_NODE: |
334 | case GSK_TRANSFORM_NODE: |
335 | case GSK_OPACITY_NODE: |
336 | case GSK_COLOR_MATRIX_NODE: |
337 | case GSK_REPEAT_NODE: |
338 | case GSK_CLIP_NODE: |
339 | case GSK_ROUNDED_CLIP_NODE: |
340 | case GSK_SHADOW_NODE: |
341 | case GSK_BLEND_NODE: |
342 | case GSK_CROSS_FADE_NODE: |
343 | case GSK_TEXT_NODE: |
344 | case GSK_BLUR_NODE: |
345 | case GSK_GL_SHADER_NODE: |
346 | return g_strdup (str: node_type_name (type: gsk_render_node_get_node_type (node))); |
347 | |
348 | case GSK_DEBUG_NODE: |
349 | return g_strdup (str: gsk_debug_node_get_message (node)); |
350 | |
351 | case GSK_COLOR_NODE: |
352 | return gdk_rgba_to_string (rgba: gsk_color_node_get_color (node)); |
353 | |
354 | case GSK_TEXTURE_NODE: |
355 | { |
356 | GdkTexture *texture = gsk_texture_node_get_texture (node); |
357 | return g_strdup_printf (format: "%dx%d Texture" , gdk_texture_get_width (texture), gdk_texture_get_height (texture)); |
358 | } |
359 | } |
360 | } |
361 | |
362 | static GdkContentProvider * |
363 | prepare_render_node_drag (GtkDragSource *source, |
364 | double x, |
365 | double y, |
366 | GtkListItem *list_item) |
367 | { |
368 | GtkTreeListRow *row_item; |
369 | GdkPaintable *paintable; |
370 | GskRenderNode *node; |
371 | |
372 | row_item = gtk_list_item_get_item (self: list_item); |
373 | if (row_item == NULL) |
374 | return NULL; |
375 | |
376 | paintable = gtk_tree_list_row_get_item (self: row_item); |
377 | node = gtk_render_node_paintable_get_render_node (self: GTK_RENDER_NODE_PAINTABLE (ptr: paintable)); |
378 | |
379 | return gdk_content_provider_new_typed (GSK_TYPE_RENDER_NODE, node); |
380 | } |
381 | |
382 | static void |
383 | setup_widget_for_render_node (GtkSignalListItemFactory *factory, |
384 | GtkListItem *list_item) |
385 | { |
386 | GtkWidget *expander, *box, *child; |
387 | GtkDragSource *source; |
388 | |
389 | /* expander */ |
390 | expander = gtk_tree_expander_new (); |
391 | gtk_list_item_set_child (self: list_item, child: expander); |
392 | source = gtk_drag_source_new (); |
393 | g_signal_connect (source, "prepare" , G_CALLBACK (prepare_render_node_drag), list_item); |
394 | gtk_widget_add_controller (widget: expander, GTK_EVENT_CONTROLLER (source)); |
395 | |
396 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 3); |
397 | gtk_tree_expander_set_child (self: GTK_TREE_EXPANDER (ptr: expander), child: box); |
398 | |
399 | /* icon */ |
400 | child = gtk_image_new (); |
401 | gtk_box_append (GTK_BOX (box), child); |
402 | |
403 | /* name */ |
404 | child = gtk_label_new (NULL); |
405 | gtk_box_append (GTK_BOX (box), child); |
406 | } |
407 | |
408 | static void |
409 | bind_widget_for_render_node (GtkSignalListItemFactory *factory, |
410 | GtkListItem *list_item) |
411 | { |
412 | GdkPaintable *paintable; |
413 | GskRenderNode *node; |
414 | GtkTreeListRow *row_item; |
415 | GtkWidget *expander, *box, *child; |
416 | char *name; |
417 | |
418 | row_item = gtk_list_item_get_item (self: list_item); |
419 | paintable = gtk_tree_list_row_get_item (self: row_item); |
420 | node = gtk_render_node_paintable_get_render_node (self: GTK_RENDER_NODE_PAINTABLE (ptr: paintable)); |
421 | |
422 | /* expander */ |
423 | expander = gtk_list_item_get_child (self: list_item); |
424 | gtk_tree_expander_set_list_row (self: GTK_TREE_EXPANDER (ptr: expander), list_row: row_item); |
425 | box = gtk_tree_expander_get_child (self: GTK_TREE_EXPANDER (ptr: expander)); |
426 | |
427 | /* icon */ |
428 | child = gtk_widget_get_first_child (widget: box); |
429 | gtk_image_set_from_paintable (GTK_IMAGE (child), paintable); |
430 | |
431 | /* name */ |
432 | name = node_name (node); |
433 | child = gtk_widget_get_last_child (widget: box); |
434 | gtk_label_set_label (GTK_LABEL (child), str: name); |
435 | g_free (mem: name); |
436 | |
437 | g_object_unref (object: paintable); |
438 | } |
439 | |
440 | static void |
441 | show_render_node (GtkInspectorRecorder *recorder, |
442 | GskRenderNode *node) |
443 | { |
444 | graphene_rect_t bounds; |
445 | GdkPaintable *paintable; |
446 | |
447 | gsk_render_node_get_bounds (node, bounds: &bounds); |
448 | paintable = gtk_render_node_paintable_new (node, bounds: &bounds); |
449 | |
450 | if (strcmp (s1: gtk_stack_get_visible_child_name (GTK_STACK (recorder->recording_data_stack)), s2: "frame_data" ) == 0) |
451 | { |
452 | gtk_picture_set_paintable (self: GTK_PICTURE (ptr: recorder->render_node_view), paintable); |
453 | |
454 | g_list_store_splice (store: recorder->render_node_root_model, |
455 | position: 0, n_removals: g_list_model_get_n_items (list: G_LIST_MODEL (ptr: recorder->render_node_root_model)), |
456 | additions: (gpointer[1]) { paintable }, |
457 | n_additions: 1); |
458 | } |
459 | else |
460 | { |
461 | gtk_picture_set_paintable (self: GTK_PICTURE (ptr: recorder->event_view), paintable); |
462 | } |
463 | |
464 | g_object_unref (object: paintable); |
465 | } |
466 | |
467 | static GskRenderNode * |
468 | make_dot (double x, double y) |
469 | { |
470 | GskRenderNode *fill, *dot; |
471 | GdkRGBA red = (GdkRGBA){ 1, 0, 0, 1 }; |
472 | graphene_rect_t rect = GRAPHENE_RECT_INIT (x - 3, y - 3, 6, 6); |
473 | graphene_size_t corner = GRAPHENE_SIZE_INIT (3, 3); |
474 | GskRoundedRect clip; |
475 | |
476 | fill = gsk_color_node_new (rgba: &red, bounds: &rect); |
477 | dot = gsk_rounded_clip_node_new (child: fill, clip: gsk_rounded_rect_init (self: &clip, bounds: &rect, |
478 | top_left: &corner, top_right: &corner, bottom_right: &corner, bottom_left: &corner)); |
479 | gsk_render_node_unref (node: fill); |
480 | |
481 | return dot; |
482 | } |
483 | |
484 | static void |
485 | show_event (GtkInspectorRecorder *recorder, |
486 | GskRenderNode *node, |
487 | GdkEvent *event) |
488 | { |
489 | GskRenderNode *temp; |
490 | double x, y; |
491 | |
492 | if (gdk_event_get_position (event, x: &x, y: &y)) |
493 | { |
494 | GskRenderNode *dot = make_dot (x, y); |
495 | temp = gsk_container_node_new (children: (GskRenderNode *[]) { node, dot }, n_children: 2); |
496 | gsk_render_node_unref (node: dot); |
497 | } |
498 | else |
499 | temp = gsk_render_node_ref (node); |
500 | |
501 | show_render_node (recorder, node: temp); |
502 | |
503 | gsk_render_node_unref (node: temp); |
504 | } |
505 | |
506 | static void populate_event_properties (GtkListStore *store, |
507 | GdkEvent *event); |
508 | |
509 | static void |
510 | recording_selected (GtkSingleSelection *selection, |
511 | GParamSpec *pspec, |
512 | GtkInspectorRecorder *recorder) |
513 | { |
514 | GtkInspectorRecording *recording; |
515 | GdkEventSequence *selected_sequence = NULL; |
516 | |
517 | if (recorder->recordings == NULL) |
518 | { |
519 | gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), name: "no_data" ); |
520 | return; |
521 | } |
522 | |
523 | recording = gtk_single_selection_get_selected_item (self: selection); |
524 | |
525 | if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording)) |
526 | { |
527 | GskRenderNode *node; |
528 | |
529 | gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), name: "frame_data" ); |
530 | |
531 | node = gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (recording)); |
532 | show_render_node (recorder, node); |
533 | } |
534 | else if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording)) |
535 | { |
536 | GdkEvent *event; |
537 | |
538 | gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), name: "event_data" ); |
539 | |
540 | event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); |
541 | |
542 | for (guint pos = gtk_single_selection_get_selected (self: selection) - 1; pos > 0; pos--) |
543 | { |
544 | GtkInspectorRecording *item = g_list_model_get_item (list: G_LIST_MODEL (ptr: selection), position: pos); |
545 | |
546 | g_object_unref (object: item); |
547 | if (GTK_INSPECTOR_IS_RENDER_RECORDING (item)) |
548 | { |
549 | GskRenderNode *node; |
550 | |
551 | node = gtk_inspector_render_recording_get_node (GTK_INSPECTOR_RENDER_RECORDING (item)); |
552 | show_event (recorder, node, event); |
553 | break; |
554 | } |
555 | } |
556 | |
557 | populate_event_properties (GTK_LIST_STORE (recorder->event_properties), event); |
558 | |
559 | if (recorder->highlight_sequences) |
560 | selected_sequence = gdk_event_get_event_sequence (event); |
561 | } |
562 | else |
563 | { |
564 | gtk_stack_set_visible_child_name (GTK_STACK (recorder->recording_data_stack), name: "no_data" ); |
565 | |
566 | gtk_picture_set_paintable (self: GTK_PICTURE (ptr: recorder->render_node_view), NULL); |
567 | g_list_store_remove_all (store: recorder->render_node_root_model); |
568 | } |
569 | |
570 | gtk_inspector_recorder_set_selected_sequence (recorder, sequence: selected_sequence); |
571 | } |
572 | |
573 | static GdkTexture * |
574 | get_color_texture (const GdkRGBA *color) |
575 | { |
576 | GdkTexture *texture; |
577 | guchar pixel[4]; |
578 | guchar *data; |
579 | GBytes *bytes; |
580 | int width = 30; |
581 | int height = 30; |
582 | int i; |
583 | |
584 | pixel[0] = round (x: color->red * 255); |
585 | pixel[1] = round (x: color->green * 255); |
586 | pixel[2] = round (x: color->blue * 255); |
587 | pixel[3] = round (x: color->alpha * 255); |
588 | |
589 | data = g_malloc (n_bytes: 4 * width * height); |
590 | for (i = 0; i < width * height; i++) |
591 | { |
592 | memcpy (dest: data + 4 * i, src: pixel, n: 4); |
593 | } |
594 | |
595 | bytes = g_bytes_new_take (data, size: 4 * width * height); |
596 | texture = gdk_memory_texture_new (width, |
597 | height, |
598 | format: GDK_MEMORY_R8G8B8A8, |
599 | bytes, |
600 | stride: width * 4); |
601 | g_bytes_unref (bytes); |
602 | |
603 | return texture; |
604 | } |
605 | |
606 | static GdkTexture * |
607 | get_linear_gradient_texture (gsize n_stops, const GskColorStop *stops) |
608 | { |
609 | cairo_surface_t *surface; |
610 | cairo_t *cr; |
611 | cairo_pattern_t *pattern; |
612 | GdkTexture *texture; |
613 | int i; |
614 | |
615 | surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, width: 90, height: 30); |
616 | cr = cairo_create (target: surface); |
617 | |
618 | pattern = cairo_pattern_create_linear (x0: 0, y0: 0, x1: 90, y1: 0); |
619 | for (i = 0; i < n_stops; i++) |
620 | { |
621 | cairo_pattern_add_color_stop_rgba (pattern, |
622 | offset: stops[i].offset, |
623 | red: stops[i].color.red, |
624 | green: stops[i].color.green, |
625 | blue: stops[i].color.blue, |
626 | alpha: stops[i].color.alpha); |
627 | } |
628 | |
629 | cairo_set_source (cr, source: pattern); |
630 | cairo_pattern_destroy (pattern); |
631 | cairo_rectangle (cr, x: 0, y: 0, width: 90, height: 30); |
632 | cairo_fill (cr); |
633 | cairo_destroy (cr); |
634 | |
635 | texture = gdk_texture_new_for_surface (surface); |
636 | cairo_surface_destroy (surface); |
637 | |
638 | return texture; |
639 | } |
640 | |
641 | static void |
642 | add_text_row (GtkListStore *store, |
643 | const char *name, |
644 | const char *text) |
645 | { |
646 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
647 | 0, name, |
648 | 1, text, |
649 | 2, FALSE, |
650 | 3, NULL, |
651 | -1); |
652 | } |
653 | |
654 | static void |
655 | add_color_row (GtkListStore *store, |
656 | const char *name, |
657 | const GdkRGBA *color) |
658 | { |
659 | char *text; |
660 | GdkTexture *texture; |
661 | |
662 | text = gdk_rgba_to_string (rgba: color); |
663 | texture = get_color_texture (color); |
664 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
665 | 0, name, |
666 | 1, text, |
667 | 2, TRUE, |
668 | 3, texture, |
669 | -1); |
670 | g_free (mem: text); |
671 | g_object_unref (object: texture); |
672 | } |
673 | |
674 | static void |
675 | add_int_row (GtkListStore *store, |
676 | const char *name, |
677 | int value) |
678 | { |
679 | char *text = g_strdup_printf (format: "%d" , value); |
680 | add_text_row (store, name, text); |
681 | g_free (mem: text); |
682 | } |
683 | |
684 | static void |
685 | add_uint_row (GtkListStore *store, |
686 | const char *name, |
687 | guint value) |
688 | { |
689 | char *text = g_strdup_printf (format: "%u" , value); |
690 | add_text_row (store, name, text); |
691 | g_free (mem: text); |
692 | } |
693 | |
694 | static void |
695 | add_boolean_row (GtkListStore *store, |
696 | const char *name, |
697 | gboolean value) |
698 | { |
699 | add_text_row (store, name, text: value ? "TRUE" : "FALSE" ); |
700 | } |
701 | |
702 | static void |
703 | add_float_row (GtkListStore *store, |
704 | const char *name, |
705 | float value) |
706 | { |
707 | char *text = g_strdup_printf (format: "%.2f" , value); |
708 | add_text_row (store, name, text); |
709 | g_free (mem: text); |
710 | } |
711 | |
712 | static void |
713 | populate_render_node_properties (GtkListStore *store, |
714 | GskRenderNode *node) |
715 | { |
716 | graphene_rect_t bounds; |
717 | char *tmp; |
718 | |
719 | gtk_list_store_clear (list_store: store); |
720 | |
721 | gsk_render_node_get_bounds (node, bounds: &bounds); |
722 | |
723 | add_text_row (store, name: "Type" , text: node_type_name (type: gsk_render_node_get_node_type (node))); |
724 | |
725 | tmp = g_strdup_printf (format: "%.2f x %.2f + %.2f + %.2f" , |
726 | bounds.size.width, |
727 | bounds.size.height, |
728 | bounds.origin.x, |
729 | bounds.origin.y); |
730 | add_text_row (store, name: "Bounds" , text: tmp); |
731 | g_free (mem: tmp); |
732 | |
733 | switch (gsk_render_node_get_node_type (node)) |
734 | { |
735 | case GSK_CAIRO_NODE: |
736 | { |
737 | GdkTexture *texture; |
738 | cairo_surface_t *drawn_surface; |
739 | cairo_t *cr; |
740 | gboolean show_inline; |
741 | |
742 | drawn_surface = cairo_image_surface_create (format: CAIRO_FORMAT_ARGB32, |
743 | width: ceilf (x: node->bounds.size.width), |
744 | height: ceilf (x: node->bounds.size.height)); |
745 | cr = cairo_create (target: drawn_surface); |
746 | cairo_save (cr); |
747 | cairo_translate (cr, tx: -node->bounds.origin.x, ty: -node->bounds.origin.y); |
748 | gsk_render_node_draw (node, cr); |
749 | cairo_restore (cr); |
750 | |
751 | cairo_destroy (cr); |
752 | |
753 | texture = gdk_texture_new_for_surface (surface: drawn_surface); |
754 | cairo_surface_destroy (surface: drawn_surface); |
755 | |
756 | show_inline = gdk_texture_get_height (texture) <= 40 && |
757 | gdk_texture_get_width (texture) <= 100; |
758 | |
759 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
760 | 0, "Surface" , |
761 | 1, show_inline ? "" : "Yes (click to show)" , |
762 | 2, show_inline, |
763 | 3, texture, |
764 | -1); |
765 | } |
766 | break; |
767 | |
768 | case GSK_TEXTURE_NODE: |
769 | { |
770 | GdkTexture *texture = g_object_ref (gsk_texture_node_get_texture (node)); |
771 | gboolean show_inline; |
772 | |
773 | show_inline = gdk_texture_get_height (texture) <= 40 && |
774 | gdk_texture_get_width (texture) <= 100; |
775 | |
776 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
777 | 0, "Texture" , |
778 | 1, show_inline ? "" : "Yes (click to show)" , |
779 | 2, show_inline, |
780 | 3, texture, |
781 | -1); |
782 | } |
783 | break; |
784 | |
785 | case GSK_COLOR_NODE: |
786 | add_color_row (store, name: "Color" , color: gsk_color_node_get_color (node)); |
787 | break; |
788 | |
789 | case GSK_LINEAR_GRADIENT_NODE: |
790 | case GSK_REPEATING_LINEAR_GRADIENT_NODE: |
791 | { |
792 | const graphene_point_t *start = gsk_linear_gradient_node_get_start (node); |
793 | const graphene_point_t *end = gsk_linear_gradient_node_get_end (node); |
794 | const gsize n_stops = gsk_linear_gradient_node_get_n_color_stops (node); |
795 | const GskColorStop *stops = gsk_linear_gradient_node_get_color_stops (node, NULL); |
796 | int i; |
797 | GString *s; |
798 | GdkTexture *texture; |
799 | |
800 | tmp = g_strdup_printf (format: "%.2f %.2f ⟶ %.2f %.2f" , start->x, start->y, end->x, end->y); |
801 | add_text_row (store, name: "Direction" , text: tmp); |
802 | g_free (mem: tmp); |
803 | |
804 | s = g_string_new (init: "" ); |
805 | for (i = 0; i < n_stops; i++) |
806 | { |
807 | tmp = gdk_rgba_to_string (rgba: &stops[i].color); |
808 | g_string_append_printf (string: s, format: "%.2f, %s\n" , stops[i].offset, tmp); |
809 | g_free (mem: tmp); |
810 | } |
811 | |
812 | texture = get_linear_gradient_texture (n_stops, stops); |
813 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
814 | 0, "Color Stops" , |
815 | 1, s->str, |
816 | 2, TRUE, |
817 | 3, texture, |
818 | -1); |
819 | g_string_free (string: s, TRUE); |
820 | g_object_unref (object: texture); |
821 | } |
822 | break; |
823 | |
824 | case GSK_RADIAL_GRADIENT_NODE: |
825 | case GSK_REPEATING_RADIAL_GRADIENT_NODE: |
826 | { |
827 | const graphene_point_t *center = gsk_radial_gradient_node_get_center (node); |
828 | const float start = gsk_radial_gradient_node_get_start (node); |
829 | const float end = gsk_radial_gradient_node_get_end (node); |
830 | const float hradius = gsk_radial_gradient_node_get_hradius (node); |
831 | const float vradius = gsk_radial_gradient_node_get_vradius (node); |
832 | const gsize n_stops = gsk_radial_gradient_node_get_n_color_stops (node); |
833 | const GskColorStop *stops = gsk_radial_gradient_node_get_color_stops (node, NULL); |
834 | int i; |
835 | GString *s; |
836 | GdkTexture *texture; |
837 | |
838 | tmp = g_strdup_printf (format: "%.2f, %.2f" , center->x, center->y); |
839 | add_text_row (store, name: "Center" , text: tmp); |
840 | g_free (mem: tmp); |
841 | |
842 | tmp = g_strdup_printf (format: "%.2f ⟶ %.2f" , start, end); |
843 | add_text_row (store, name: "Direction" , text: tmp); |
844 | g_free (mem: tmp); |
845 | |
846 | tmp = g_strdup_printf (format: "%.2f, %.2f" , hradius, vradius); |
847 | add_text_row (store, name: "Radius" , text: tmp); |
848 | g_free (mem: tmp); |
849 | |
850 | s = g_string_new (init: "" ); |
851 | for (i = 0; i < n_stops; i++) |
852 | { |
853 | tmp = gdk_rgba_to_string (rgba: &stops[i].color); |
854 | g_string_append_printf (string: s, format: "%.2f, %s\n" , stops[i].offset, tmp); |
855 | g_free (mem: tmp); |
856 | } |
857 | |
858 | texture = get_linear_gradient_texture (n_stops, stops); |
859 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
860 | 0, "Color Stops" , |
861 | 1, s->str, |
862 | 2, TRUE, |
863 | 3, texture, |
864 | -1); |
865 | g_string_free (string: s, TRUE); |
866 | g_object_unref (object: texture); |
867 | } |
868 | break; |
869 | |
870 | case GSK_CONIC_GRADIENT_NODE: |
871 | { |
872 | const graphene_point_t *center = gsk_conic_gradient_node_get_center (node); |
873 | const float rotation = gsk_conic_gradient_node_get_rotation (node); |
874 | const gsize n_stops = gsk_conic_gradient_node_get_n_color_stops (node); |
875 | const GskColorStop *stops = gsk_conic_gradient_node_get_color_stops (node, NULL); |
876 | gsize i; |
877 | GString *s; |
878 | GdkTexture *texture; |
879 | |
880 | tmp = g_strdup_printf (format: "%.2f, %.2f" , center->x, center->y); |
881 | add_text_row (store, name: "Center" , text: tmp); |
882 | g_free (mem: tmp); |
883 | |
884 | tmp = g_strdup_printf (format: "%.2f" , rotation); |
885 | add_text_row (store, name: "Rotation" , text: tmp); |
886 | g_free (mem: tmp); |
887 | |
888 | s = g_string_new (init: "" ); |
889 | for (i = 0; i < n_stops; i++) |
890 | { |
891 | tmp = gdk_rgba_to_string (rgba: &stops[i].color); |
892 | g_string_append_printf (string: s, format: "%.2f, %s\n" , stops[i].offset, tmp); |
893 | g_free (mem: tmp); |
894 | } |
895 | |
896 | texture = get_linear_gradient_texture (n_stops, stops); |
897 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
898 | 0, "Color Stops" , |
899 | 1, s->str, |
900 | 2, TRUE, |
901 | 3, texture, |
902 | -1); |
903 | g_string_free (string: s, TRUE); |
904 | g_object_unref (object: texture); |
905 | } |
906 | break; |
907 | |
908 | case GSK_TEXT_NODE: |
909 | { |
910 | const PangoFont *font = gsk_text_node_get_font (node); |
911 | const GdkRGBA *color = gsk_text_node_get_color (node); |
912 | const graphene_point_t *offset = gsk_text_node_get_offset (node); |
913 | PangoFontDescription *desc; |
914 | GString *s; |
915 | |
916 | desc = pango_font_describe (font: (PangoFont *)font); |
917 | tmp = pango_font_description_to_string (desc); |
918 | add_text_row (store, name: "Font" , text: tmp); |
919 | g_free (mem: tmp); |
920 | pango_font_description_free (desc); |
921 | |
922 | s = g_string_sized_new (dfl_size: 0); |
923 | gsk_text_node_serialize_glyphs (self: node, str: s); |
924 | add_text_row (store, name: "Glyphs" , text: s->str); |
925 | g_string_free (string: s, TRUE); |
926 | |
927 | tmp = g_strdup_printf (format: "%.2f %.2f" , offset->x, offset->y); |
928 | add_text_row (store, name: "Position" , text: tmp); |
929 | g_free (mem: tmp); |
930 | |
931 | add_color_row (store, name: "Color" , color); |
932 | } |
933 | break; |
934 | |
935 | case GSK_BORDER_NODE: |
936 | { |
937 | const char *name[4] = { "Top" , "Right" , "Bottom" , "Left" }; |
938 | const float *widths = gsk_border_node_get_widths (node); |
939 | const GdkRGBA *colors = gsk_border_node_get_colors (node); |
940 | int i; |
941 | |
942 | for (i = 0; i < 4; i++) |
943 | { |
944 | GdkTexture *texture; |
945 | char *text; |
946 | |
947 | texture = get_color_texture (color: &colors[i]); |
948 | text = gdk_rgba_to_string (rgba: &colors[i]); |
949 | tmp = g_strdup_printf (format: "%.2f, %s" , widths[i], text); |
950 | gtk_list_store_insert_with_values (list_store: store, NULL, position: -1, |
951 | 0, name[i], |
952 | 1, tmp, |
953 | 2, TRUE, |
954 | 3, texture, |
955 | -1); |
956 | g_free (mem: text); |
957 | g_free (mem: tmp); |
958 | g_object_unref (object: texture); |
959 | } |
960 | } |
961 | break; |
962 | |
963 | case GSK_OPACITY_NODE: |
964 | add_float_row (store, name: "Opacity" , value: gsk_opacity_node_get_opacity (node)); |
965 | break; |
966 | |
967 | case GSK_CROSS_FADE_NODE: |
968 | add_float_row (store, name: "Progress" , value: gsk_cross_fade_node_get_progress (node)); |
969 | break; |
970 | |
971 | case GSK_BLEND_NODE: |
972 | { |
973 | GskBlendMode mode = gsk_blend_node_get_blend_mode (node); |
974 | tmp = g_enum_to_string (g_enum_type: GSK_TYPE_BLEND_MODE, value: mode); |
975 | add_text_row (store, name: "Blendmode" , text: tmp); |
976 | g_free (mem: tmp); |
977 | } |
978 | break; |
979 | |
980 | case GSK_BLUR_NODE: |
981 | add_float_row (store, name: "Radius" , value: gsk_blur_node_get_radius (node)); |
982 | break; |
983 | |
984 | case GSK_GL_SHADER_NODE: |
985 | { |
986 | GskGLShader *shader = gsk_gl_shader_node_get_shader (node); |
987 | GBytes *args = gsk_gl_shader_node_get_args (node); |
988 | int i; |
989 | |
990 | add_int_row (store, name: "Required textures" , value: gsk_gl_shader_get_n_textures (shader)); |
991 | for (i = 0; i < gsk_gl_shader_get_n_uniforms (shader); i++) |
992 | { |
993 | const char *name; |
994 | char *title; |
995 | |
996 | name = gsk_gl_shader_get_uniform_name (shader, idx: i); |
997 | title = g_strdup_printf (format: "Uniform %s" , name); |
998 | |
999 | switch (gsk_gl_shader_get_uniform_type (shader, idx: i)) |
1000 | { |
1001 | case GSK_GL_UNIFORM_TYPE_NONE: |
1002 | default: |
1003 | g_assert_not_reached (); |
1004 | break; |
1005 | |
1006 | case GSK_GL_UNIFORM_TYPE_FLOAT: |
1007 | add_float_row (store, name: title, |
1008 | value: gsk_gl_shader_get_arg_float (shader, args, idx: i)); |
1009 | break; |
1010 | |
1011 | case GSK_GL_UNIFORM_TYPE_INT: |
1012 | add_int_row (store, name: title, |
1013 | value: gsk_gl_shader_get_arg_int (shader, args, idx: i)); |
1014 | break; |
1015 | |
1016 | case GSK_GL_UNIFORM_TYPE_UINT: |
1017 | add_uint_row (store, name: title, |
1018 | value: gsk_gl_shader_get_arg_uint (shader, args, idx: i)); |
1019 | break; |
1020 | |
1021 | case GSK_GL_UNIFORM_TYPE_BOOL: |
1022 | add_boolean_row (store, name: title, |
1023 | value: gsk_gl_shader_get_arg_bool (shader, args, idx: i)); |
1024 | break; |
1025 | |
1026 | case GSK_GL_UNIFORM_TYPE_VEC2: |
1027 | { |
1028 | graphene_vec2_t v; |
1029 | gsk_gl_shader_get_arg_vec2 (shader, args, idx: i, out_value: &v); |
1030 | float x = graphene_vec2_get_x (v: &v); |
1031 | float y = graphene_vec2_get_x (v: &v); |
1032 | char *s = g_strdup_printf (format: "%.2f %.2f" , x, y); |
1033 | add_text_row (store, name: title, text: s); |
1034 | g_free (mem: s); |
1035 | } |
1036 | break; |
1037 | |
1038 | case GSK_GL_UNIFORM_TYPE_VEC3: |
1039 | { |
1040 | graphene_vec3_t v; |
1041 | gsk_gl_shader_get_arg_vec3 (shader, args, idx: i, out_value: &v); |
1042 | float x = graphene_vec3_get_x (v: &v); |
1043 | float y = graphene_vec3_get_y (v: &v); |
1044 | float z = graphene_vec3_get_z (v: &v); |
1045 | char *s = g_strdup_printf (format: "%.2f %.2f %.2f" , x, y, z); |
1046 | add_text_row (store, name: title, text: s); |
1047 | g_free (mem: s); |
1048 | } |
1049 | break; |
1050 | |
1051 | case GSK_GL_UNIFORM_TYPE_VEC4: |
1052 | { |
1053 | graphene_vec4_t v; |
1054 | gsk_gl_shader_get_arg_vec4 (shader, args, idx: i, out_value: &v); |
1055 | float x = graphene_vec4_get_x (v: &v); |
1056 | float y = graphene_vec4_get_y (v: &v); |
1057 | float z = graphene_vec4_get_z (v: &v); |
1058 | float w = graphene_vec4_get_w (v: &v); |
1059 | char *s = g_strdup_printf (format: "%.2f %.2f %.2f %.2f" , x, y, z, w); |
1060 | add_text_row (store, name: title, text: s); |
1061 | g_free (mem: s); |
1062 | } |
1063 | break; |
1064 | } |
1065 | g_free (mem: title); |
1066 | } |
1067 | } |
1068 | break; |
1069 | |
1070 | case GSK_INSET_SHADOW_NODE: |
1071 | { |
1072 | const GdkRGBA *color = gsk_inset_shadow_node_get_color (node); |
1073 | float dx = gsk_inset_shadow_node_get_dx (node); |
1074 | float dy = gsk_inset_shadow_node_get_dy (node); |
1075 | float spread = gsk_inset_shadow_node_get_spread (node); |
1076 | float radius = gsk_inset_shadow_node_get_blur_radius (node); |
1077 | |
1078 | add_color_row (store, name: "Color" , color); |
1079 | |
1080 | tmp = g_strdup_printf (format: "%.2f %.2f" , dx, dy); |
1081 | add_text_row (store, name: "Offset" , text: tmp); |
1082 | g_free (mem: tmp); |
1083 | |
1084 | add_float_row (store, name: "Spread" , value: spread); |
1085 | add_float_row (store, name: "Radius" , value: radius); |
1086 | } |
1087 | break; |
1088 | |
1089 | case GSK_OUTSET_SHADOW_NODE: |
1090 | { |
1091 | const GskRoundedRect *outline = gsk_outset_shadow_node_get_outline (node); |
1092 | const GdkRGBA *color = gsk_outset_shadow_node_get_color (node); |
1093 | float dx = gsk_outset_shadow_node_get_dx (node); |
1094 | float dy = gsk_outset_shadow_node_get_dy (node); |
1095 | float spread = gsk_outset_shadow_node_get_spread (node); |
1096 | float radius = gsk_outset_shadow_node_get_blur_radius (node); |
1097 | float rect[12]; |
1098 | |
1099 | gsk_rounded_rect_to_float (self: outline, rect); |
1100 | tmp = g_strdup_printf (format: "%.2f x %.2f + %.2f + %.2f" , |
1101 | rect[2], rect[3], rect[0], rect[1]); |
1102 | add_text_row (store, name: "Outline" , text: tmp); |
1103 | g_free (mem: tmp); |
1104 | |
1105 | add_color_row (store, name: "Color" , color); |
1106 | |
1107 | tmp = g_strdup_printf (format: "%.2f %.2f" , dx, dy); |
1108 | add_text_row (store, name: "Offset" , text: tmp); |
1109 | g_free (mem: tmp); |
1110 | |
1111 | add_float_row (store, name: "Spread" , value: spread); |
1112 | add_float_row (store, name: "Radius" , value: radius); |
1113 | } |
1114 | break; |
1115 | |
1116 | case GSK_REPEAT_NODE: |
1117 | { |
1118 | const graphene_rect_t *child_bounds = gsk_repeat_node_get_child_bounds (node); |
1119 | |
1120 | tmp = g_strdup_printf (format: "%.2f x %.2f + %.2f + %.2f" , |
1121 | child_bounds->size.width, |
1122 | child_bounds->size.height, |
1123 | child_bounds->origin.x, |
1124 | child_bounds->origin.y); |
1125 | add_text_row (store, name: "Child Bounds" , text: tmp); |
1126 | g_free (mem: tmp); |
1127 | } |
1128 | break; |
1129 | |
1130 | case GSK_COLOR_MATRIX_NODE: |
1131 | { |
1132 | const graphene_matrix_t *matrix = gsk_color_matrix_node_get_color_matrix (node); |
1133 | const graphene_vec4_t *offset = gsk_color_matrix_node_get_color_offset (node); |
1134 | |
1135 | tmp = g_strdup_printf (format: "% .2f % .2f % .2f % .2f\n" |
1136 | "% .2f % .2f % .2f % .2f\n" |
1137 | "% .2f % .2f % .2f % .2f\n" |
1138 | "% .2f % .2f % .2f % .2f" , |
1139 | graphene_matrix_get_value (m: matrix, row: 0, col: 0), |
1140 | graphene_matrix_get_value (m: matrix, row: 0, col: 1), |
1141 | graphene_matrix_get_value (m: matrix, row: 0, col: 2), |
1142 | graphene_matrix_get_value (m: matrix, row: 0, col: 3), |
1143 | graphene_matrix_get_value (m: matrix, row: 1, col: 0), |
1144 | graphene_matrix_get_value (m: matrix, row: 1, col: 1), |
1145 | graphene_matrix_get_value (m: matrix, row: 1, col: 2), |
1146 | graphene_matrix_get_value (m: matrix, row: 1, col: 3), |
1147 | graphene_matrix_get_value (m: matrix, row: 2, col: 0), |
1148 | graphene_matrix_get_value (m: matrix, row: 2, col: 1), |
1149 | graphene_matrix_get_value (m: matrix, row: 2, col: 2), |
1150 | graphene_matrix_get_value (m: matrix, row: 2, col: 3), |
1151 | graphene_matrix_get_value (m: matrix, row: 3, col: 0), |
1152 | graphene_matrix_get_value (m: matrix, row: 3, col: 1), |
1153 | graphene_matrix_get_value (m: matrix, row: 3, col: 2), |
1154 | graphene_matrix_get_value (m: matrix, row: 3, col: 3)); |
1155 | add_text_row (store, name: "Matrix" , text: tmp); |
1156 | g_free (mem: tmp); |
1157 | tmp = g_strdup_printf (format: "%.2f %.2f %.2f %.2f" , |
1158 | graphene_vec4_get_x (v: offset), |
1159 | graphene_vec4_get_y (v: offset), |
1160 | graphene_vec4_get_z (v: offset), |
1161 | graphene_vec4_get_w (v: offset)); |
1162 | add_text_row (store, name: "Offset" , text: tmp); |
1163 | g_free (mem: tmp); |
1164 | } |
1165 | break; |
1166 | |
1167 | case GSK_CLIP_NODE: |
1168 | { |
1169 | const graphene_rect_t *clip = gsk_clip_node_get_clip (node); |
1170 | tmp = g_strdup_printf (format: "%.2f x %.2f + %.2f + %.2f" , |
1171 | clip->size.width, |
1172 | clip->size.height, |
1173 | clip->origin.x, |
1174 | clip->origin.y); |
1175 | add_text_row (store, name: "Clip" , text: tmp); |
1176 | g_free (mem: tmp); |
1177 | } |
1178 | break; |
1179 | |
1180 | case GSK_ROUNDED_CLIP_NODE: |
1181 | { |
1182 | const GskRoundedRect *clip = gsk_rounded_clip_node_get_clip (node); |
1183 | tmp = g_strdup_printf (format: "%.2f x %.2f + %.2f + %.2f" , |
1184 | clip->bounds.size.width, |
1185 | clip->bounds.size.height, |
1186 | clip->bounds.origin.x, |
1187 | clip->bounds.origin.y); |
1188 | add_text_row (store, name: "Clip" , text: tmp); |
1189 | g_free (mem: tmp); |
1190 | |
1191 | tmp = g_strdup_printf (format: "%.2f x %.2f" , clip->corner[0].width, clip->corner[0].height); |
1192 | add_text_row (store, name: "Top Left Corner Size" , text: tmp); |
1193 | g_free (mem: tmp); |
1194 | |
1195 | tmp = g_strdup_printf (format: "%.2f x %.2f" , clip->corner[1].width, clip->corner[1].height); |
1196 | add_text_row (store, name: "Top Right Corner Size" , text: tmp); |
1197 | g_free (mem: tmp); |
1198 | |
1199 | tmp = g_strdup_printf (format: "%.2f x %.2f" , clip->corner[2].width, clip->corner[2].height); |
1200 | add_text_row (store, name: "Bottom Right Corner Size" , text: tmp); |
1201 | g_free (mem: tmp); |
1202 | |
1203 | tmp = g_strdup_printf (format: "%.2f x %.2f" , clip->corner[3].width, clip->corner[3].height); |
1204 | add_text_row (store, name: "Bottom Left Corner Size" , text: tmp); |
1205 | g_free (mem: tmp); |
1206 | } |
1207 | break; |
1208 | |
1209 | case GSK_CONTAINER_NODE: |
1210 | tmp = g_strdup_printf (format: "%d" , gsk_container_node_get_n_children (node)); |
1211 | add_text_row (store, name: "Children" , text: tmp); |
1212 | g_free (mem: tmp); |
1213 | break; |
1214 | |
1215 | case GSK_DEBUG_NODE: |
1216 | add_text_row (store, name: "Message" , text: gsk_debug_node_get_message (node)); |
1217 | break; |
1218 | |
1219 | case GSK_SHADOW_NODE: |
1220 | { |
1221 | int i; |
1222 | |
1223 | for (i = 0; i < gsk_shadow_node_get_n_shadows (node); i++) |
1224 | { |
1225 | char *label; |
1226 | char *value; |
1227 | const GskShadow *shadow = gsk_shadow_node_get_shadow (node, i); |
1228 | |
1229 | label = g_strdup_printf (format: "Color %d" , i); |
1230 | add_color_row (store, name: label, color: &shadow->color); |
1231 | g_free (mem: label); |
1232 | |
1233 | label = g_strdup_printf (format: "Offset %d" , i); |
1234 | value = g_strdup_printf (format: "%.2f %.2f" , shadow->dx, shadow->dy); |
1235 | add_text_row (store, name: label, text: value); |
1236 | g_free (mem: value); |
1237 | g_free (mem: label); |
1238 | |
1239 | label = g_strdup_printf (format: "Radius %d" , i); |
1240 | add_float_row (store, name: label, value: shadow->radius); |
1241 | g_free (mem: label); |
1242 | } |
1243 | } |
1244 | break; |
1245 | |
1246 | case GSK_TRANSFORM_NODE: |
1247 | { |
1248 | static const char * category_names[] = { |
1249 | [GSK_TRANSFORM_CATEGORY_UNKNOWN] = "unknown" , |
1250 | [GSK_TRANSFORM_CATEGORY_ANY] = "any" , |
1251 | [GSK_TRANSFORM_CATEGORY_3D] = "3D" , |
1252 | [GSK_TRANSFORM_CATEGORY_2D] = "2D" , |
1253 | [GSK_TRANSFORM_CATEGORY_2D_AFFINE] = "2D affine" , |
1254 | [GSK_TRANSFORM_CATEGORY_2D_TRANSLATE] = "2D translate" , |
1255 | [GSK_TRANSFORM_CATEGORY_IDENTITY] = "identity" |
1256 | }; |
1257 | GskTransform *transform; |
1258 | char *s; |
1259 | |
1260 | transform = gsk_transform_node_get_transform (node); |
1261 | s = gsk_transform_to_string (self: transform); |
1262 | add_text_row (store, name: "Matrix" , text: s); |
1263 | g_free (mem: s); |
1264 | add_text_row (store, name: "Category" , text: category_names[gsk_transform_get_category (transform)]); |
1265 | } |
1266 | break; |
1267 | |
1268 | case GSK_NOT_A_RENDER_NODE: |
1269 | default: |
1270 | break; |
1271 | } |
1272 | } |
1273 | |
1274 | static const char * |
1275 | event_type_name (GdkEventType type) |
1276 | { |
1277 | const char *event_name[] = { |
1278 | "Delete" , |
1279 | "Motion" , |
1280 | "Button Press" , |
1281 | "Button Release" , |
1282 | "Key Press" , |
1283 | "Key Release" , |
1284 | "Enter" , |
1285 | "Leave" , |
1286 | "Focus" , |
1287 | "Proximity In" , |
1288 | "Proximity Out" , |
1289 | "Drag Enter" , |
1290 | "Drag Leave" , |
1291 | "Drag Motion" , |
1292 | "Drop Start" , |
1293 | "Scroll" , |
1294 | "Grab Broken" , |
1295 | "Touch Begin" , |
1296 | "Touch Update" , |
1297 | "Touch End" , |
1298 | "Touch Cancel" , |
1299 | "Touchpad Swipe" , |
1300 | "Touchpad Pinch" , |
1301 | "Pad Button Press" , |
1302 | "Pad Button Release" , |
1303 | "Pad Rind" , |
1304 | "Pad Strip" , |
1305 | "Pad Group Mode" |
1306 | }; |
1307 | |
1308 | return event_name[type]; |
1309 | } |
1310 | |
1311 | static const char * |
1312 | scroll_direction_name (GdkScrollDirection dir) |
1313 | { |
1314 | const char *scroll_dir[] = { |
1315 | "Up" , "Down" , "Left" , "Right" , "Smooth" |
1316 | }; |
1317 | return scroll_dir[dir]; |
1318 | } |
1319 | |
1320 | static char * |
1321 | modifier_names (GdkModifierType state) |
1322 | { |
1323 | struct { |
1324 | const char *name; |
1325 | int mask; |
1326 | } mods[] = { |
1327 | { "Shift" , GDK_SHIFT_MASK }, |
1328 | { "Lock" , GDK_LOCK_MASK }, |
1329 | { "Control" , GDK_CONTROL_MASK }, |
1330 | { "Alt" , GDK_ALT_MASK }, |
1331 | { "Button1" , GDK_BUTTON1_MASK }, |
1332 | { "Button2" , GDK_BUTTON2_MASK }, |
1333 | { "Button3" , GDK_BUTTON3_MASK }, |
1334 | { "Button4" , GDK_BUTTON4_MASK }, |
1335 | { "Button5" , GDK_BUTTON5_MASK }, |
1336 | { "Super" , GDK_SUPER_MASK }, |
1337 | { "Hyper" , GDK_HYPER_MASK }, |
1338 | { "Meta" , GDK_META_MASK }, |
1339 | }; |
1340 | GString *s; |
1341 | |
1342 | s = g_string_new (init: "" ); |
1343 | |
1344 | for (int i = 0; i < G_N_ELEMENTS (mods); i++) |
1345 | { |
1346 | if (state & mods[i].mask) |
1347 | { |
1348 | if (s->len > 0) |
1349 | g_string_append (string: s, val: " " ); |
1350 | g_string_append (string: s, val: mods[i].name); |
1351 | } |
1352 | } |
1353 | |
1354 | return g_string_free (string: s, FALSE); |
1355 | } |
1356 | |
1357 | static char * |
1358 | key_event_string (GdkEvent *event) |
1359 | { |
1360 | guint keyval; |
1361 | gunichar c; |
1362 | char buf[5] = { 0, }; |
1363 | |
1364 | keyval = gdk_key_event_get_keyval (event); |
1365 | c = gdk_keyval_to_unicode (keyval); |
1366 | if (c) |
1367 | { |
1368 | g_unichar_to_utf8 (c, outbuf: buf); |
1369 | return g_strdup (str: buf); |
1370 | } |
1371 | |
1372 | return g_strdup (str: gdk_keyval_name (keyval)); |
1373 | } |
1374 | |
1375 | static const char * |
1376 | device_tool_name (GdkDeviceTool *tool) |
1377 | { |
1378 | const char *name[] = { |
1379 | "Unknown" , |
1380 | "Pen" , |
1381 | "Eraser" , |
1382 | "Brush" , |
1383 | "Pencil" , |
1384 | "Airbrush" , |
1385 | "Mouse" , |
1386 | "Lens" |
1387 | }; |
1388 | |
1389 | return name[gdk_device_tool_get_tool_type (tool)]; |
1390 | } |
1391 | |
1392 | static const char * |
1393 | axis_name (GdkAxisUse axis) |
1394 | { |
1395 | const char *name[] = { |
1396 | "" , |
1397 | "X" , |
1398 | "Y" , |
1399 | "Delta X" , |
1400 | "Delta Y" , |
1401 | "Pressure" , |
1402 | "X Tilt" , |
1403 | "Y Tilt" , |
1404 | "Wheel" , |
1405 | "Distance" , |
1406 | "Rotation" , |
1407 | "Slider" |
1408 | }; |
1409 | |
1410 | return name[axis]; |
1411 | } |
1412 | |
1413 | static const char * |
1414 | gesture_phase_name (GdkTouchpadGesturePhase phase) |
1415 | { |
1416 | const char *name[] = { |
1417 | "Begin" , |
1418 | "Update" , |
1419 | "End" , |
1420 | "Cancel" |
1421 | }; |
1422 | |
1423 | return name[phase]; |
1424 | } |
1425 | |
1426 | static void |
1427 | populate_event_properties (GtkListStore *store, |
1428 | GdkEvent *event) |
1429 | { |
1430 | GdkEventType type; |
1431 | GdkDevice *device; |
1432 | GdkDeviceTool *tool; |
1433 | double x, y; |
1434 | double dx, dy; |
1435 | char *tmp; |
1436 | GdkModifierType state; |
1437 | |
1438 | gtk_list_store_clear (list_store: store); |
1439 | |
1440 | type = gdk_event_get_event_type (event); |
1441 | |
1442 | add_text_row (store, name: "Type" , text: event_type_name (type)); |
1443 | if (gdk_event_get_event_sequence (event) != NULL) |
1444 | { |
1445 | tmp = g_strdup_printf (format: "%p" , gdk_event_get_event_sequence (event)); |
1446 | add_text_row (store, name: "Sequence" , text: tmp); |
1447 | g_free (mem: tmp); |
1448 | } |
1449 | add_int_row (store, name: "Timestamp" , value: gdk_event_get_time (event)); |
1450 | |
1451 | device = gdk_event_get_device (event); |
1452 | if (device) |
1453 | add_text_row (store, name: "Device" , text: gdk_device_get_name (device)); |
1454 | |
1455 | tool = gdk_event_get_device_tool (event); |
1456 | if (tool) |
1457 | add_text_row (store, name: "Device Tool" , text: device_tool_name (tool)); |
1458 | |
1459 | if (gdk_event_get_position (event, x: &x, y: &y)) |
1460 | { |
1461 | tmp = g_strdup_printf (format: "%.2f %.2f" , x, y); |
1462 | add_text_row (store, name: "Position" , text: tmp); |
1463 | g_free (mem: tmp); |
1464 | } |
1465 | |
1466 | if (tool) |
1467 | { |
1468 | GdkAxisFlags axes = gdk_device_tool_get_axes (tool); |
1469 | |
1470 | /* We report position and scroll delta separately, so skip them here */ |
1471 | axes &= ~(GDK_AXIS_FLAG_X|GDK_AXIS_FLAG_Y|GDK_AXIS_FLAG_DELTA_X|GDK_AXIS_FLAG_DELTA_Y); |
1472 | |
1473 | for (int i = 1; i < GDK_AXIS_LAST; i++) |
1474 | { |
1475 | if (axes & (1 << i)) |
1476 | { |
1477 | double val; |
1478 | gdk_event_get_axis (event, axis_use: i, value: &val); |
1479 | tmp = g_strdup_printf (format: "%.2f" , val); |
1480 | add_text_row (store, name: axis_name (axis: i), text: tmp); |
1481 | g_free (mem: tmp); |
1482 | } |
1483 | } |
1484 | } |
1485 | |
1486 | state = gdk_event_get_modifier_state (event); |
1487 | if (state != 0) |
1488 | { |
1489 | tmp = modifier_names (state); |
1490 | add_text_row (store, name: "State" , text: tmp); |
1491 | g_free (mem: tmp); |
1492 | } |
1493 | |
1494 | switch ((int)type) |
1495 | { |
1496 | case GDK_BUTTON_PRESS: |
1497 | case GDK_BUTTON_RELEASE: |
1498 | add_int_row (store, name: "Button" , value: gdk_button_event_get_button (event)); |
1499 | break; |
1500 | |
1501 | case GDK_KEY_PRESS: |
1502 | case GDK_KEY_RELEASE: |
1503 | add_int_row (store, name: "Keycode" , value: gdk_key_event_get_keycode (event)); |
1504 | add_int_row (store, name: "Keyval" , value: gdk_key_event_get_keyval (event)); |
1505 | tmp = key_event_string (event); |
1506 | add_text_row (store, name: "Key" , text: tmp); |
1507 | g_free (mem: tmp); |
1508 | add_int_row (store, name: "Layout" , value: gdk_key_event_get_layout (event)); |
1509 | add_int_row (store, name: "Level" , value: gdk_key_event_get_level (event)); |
1510 | add_boolean_row (store, name: "Is Modifier" , value: gdk_key_event_is_modifier (event)); |
1511 | break; |
1512 | |
1513 | case GDK_SCROLL: |
1514 | if (gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH) |
1515 | { |
1516 | gdk_scroll_event_get_deltas (event, delta_x: &x, delta_y: &y); |
1517 | tmp = g_strdup_printf (format: "%.2f %.2f" , x, y); |
1518 | add_text_row (store, name: "Delta" , text: tmp); |
1519 | g_free (mem: tmp); |
1520 | } |
1521 | else |
1522 | { |
1523 | add_text_row (store, name: "Direction" , text: scroll_direction_name (dir: gdk_scroll_event_get_direction (event))); |
1524 | } |
1525 | add_boolean_row (store, name: "Is Stop" , value: gdk_scroll_event_is_stop (event)); |
1526 | break; |
1527 | |
1528 | case GDK_FOCUS_CHANGE: |
1529 | add_text_row (store, name: "Direction" , text: gdk_focus_event_get_in (event) ? "In" : "Out" ); |
1530 | break; |
1531 | |
1532 | case GDK_ENTER_NOTIFY: |
1533 | case GDK_LEAVE_NOTIFY: |
1534 | add_int_row (store, name: "Mode" , value: gdk_crossing_event_get_mode (event)); |
1535 | add_int_row (store, name: "Detail" , value: gdk_crossing_event_get_detail (event)); |
1536 | add_boolean_row (store, name: "Is Focus" , value: gdk_crossing_event_get_focus (event)); |
1537 | break; |
1538 | |
1539 | case GDK_GRAB_BROKEN: |
1540 | add_boolean_row (store, name: "Implicit" , value: gdk_grab_broken_event_get_implicit (event)); |
1541 | break; |
1542 | |
1543 | case GDK_TOUCHPAD_SWIPE: |
1544 | case GDK_TOUCHPAD_PINCH: |
1545 | add_text_row (store, name: "Phase" , text: gesture_phase_name (phase: gdk_touchpad_event_get_gesture_phase (event))); |
1546 | add_int_row (store, name: "Fingers" , value: gdk_touchpad_event_get_n_fingers (event)); |
1547 | gdk_touchpad_event_get_deltas (event, dx: &dx, dy: &dy); |
1548 | tmp = g_strdup_printf (format: "%.2f %.f2" , dx, dy); |
1549 | add_text_row (store, name: "Delta" , text: tmp); |
1550 | g_free (mem: tmp); |
1551 | if (type == GDK_TOUCHPAD_PINCH) |
1552 | { |
1553 | tmp = g_strdup_printf (format: "%.2f" , gdk_touchpad_event_get_pinch_angle_delta (event)); |
1554 | add_text_row (store, name: "Angle Delta" , text: tmp); |
1555 | g_free (mem: tmp); |
1556 | tmp = g_strdup_printf (format: "%.2f" , gdk_touchpad_event_get_pinch_scale (event)); |
1557 | add_text_row (store, name: "Scale" , text: tmp); |
1558 | g_free (mem: tmp); |
1559 | } |
1560 | break; |
1561 | |
1562 | default: |
1563 | /* FIXME */ |
1564 | ; |
1565 | } |
1566 | |
1567 | if (type == GDK_MOTION_NOTIFY || type == GDK_SCROLL) |
1568 | { |
1569 | GdkTimeCoord *history; |
1570 | guint n_coords; |
1571 | |
1572 | history = gdk_event_get_history (event, out_n_coords: &n_coords); |
1573 | if (history) |
1574 | { |
1575 | GString *s = g_string_new (init: "" ); |
1576 | |
1577 | for (int i = 0; i < n_coords; i++) |
1578 | { |
1579 | if (i > 0) |
1580 | g_string_append (string: s, val: "\n" ); |
1581 | |
1582 | g_string_append_printf (string: s, format: "%d" , history[i].time); |
1583 | |
1584 | if (history[i].flags & (GDK_AXIS_FLAG_X|GDK_AXIS_FLAG_Y)) |
1585 | g_string_append_printf (string: s, format: " Position %.2f %.2f" , history[i].axes[GDK_AXIS_X], history[i].axes[GDK_AXIS_Y]); |
1586 | |
1587 | if (history[i].flags & (GDK_AXIS_FLAG_DELTA_X|GDK_AXIS_FLAG_DELTA_Y)) |
1588 | g_string_append_printf (string: s, format: " Delta %.2f %.2f" , history[i].axes[GDK_AXIS_DELTA_X], history[i].axes[GDK_AXIS_DELTA_Y]); |
1589 | |
1590 | for (int j = GDK_AXIS_PRESSURE; j < GDK_AXIS_LAST; j++) |
1591 | { |
1592 | if (history[i].flags & (1 << j)) |
1593 | g_string_append_printf (string: s, format: " %s %.2f" , axis_name (axis: j), history[i].axes[j]); |
1594 | } |
1595 | } |
1596 | |
1597 | add_text_row (store, name: "History" , text: s->str); |
1598 | |
1599 | g_string_free (string: s, TRUE); |
1600 | g_free (mem: history); |
1601 | } |
1602 | } |
1603 | } |
1604 | |
1605 | static GskRenderNode * |
1606 | get_selected_node (GtkInspectorRecorder *recorder) |
1607 | { |
1608 | GtkTreeListRow *row_item; |
1609 | GdkPaintable *paintable; |
1610 | GskRenderNode *node; |
1611 | |
1612 | row_item = gtk_single_selection_get_selected_item (self: recorder->render_node_selection); |
1613 | if (row_item == NULL) |
1614 | return NULL; |
1615 | |
1616 | paintable = gtk_tree_list_row_get_item (self: row_item); |
1617 | node = gtk_render_node_paintable_get_render_node (self: GTK_RENDER_NODE_PAINTABLE (ptr: paintable)); |
1618 | g_object_unref (object: paintable); |
1619 | |
1620 | return node; |
1621 | } |
1622 | |
1623 | static void |
1624 | render_node_list_selection_changed (GtkListBox *list, |
1625 | GtkListBoxRow *row, |
1626 | GtkInspectorRecorder *recorder) |
1627 | { |
1628 | GskRenderNode *node; |
1629 | GdkPaintable *paintable; |
1630 | GtkTreeListRow *row_item; |
1631 | |
1632 | row_item = gtk_single_selection_get_selected_item (self: recorder->render_node_selection); |
1633 | |
1634 | gtk_widget_set_sensitive (widget: recorder->render_node_save_button, sensitive: row_item != NULL); |
1635 | gtk_widget_set_sensitive (widget: recorder->render_node_clip_button, sensitive: row_item != NULL); |
1636 | |
1637 | if (row_item == NULL) |
1638 | return; |
1639 | |
1640 | paintable = gtk_tree_list_row_get_item (self: row_item); |
1641 | |
1642 | gtk_picture_set_paintable (self: GTK_PICTURE (ptr: recorder->render_node_view), paintable); |
1643 | node = gtk_render_node_paintable_get_render_node (self: GTK_RENDER_NODE_PAINTABLE (ptr: paintable)); |
1644 | populate_render_node_properties (GTK_LIST_STORE (recorder->render_node_properties), node); |
1645 | |
1646 | g_object_unref (object: paintable); |
1647 | } |
1648 | |
1649 | static void |
1650 | render_node_save_response (GtkWidget *dialog, |
1651 | int response, |
1652 | GskRenderNode *node) |
1653 | { |
1654 | gtk_widget_hide (widget: dialog); |
1655 | |
1656 | if (response == GTK_RESPONSE_ACCEPT) |
1657 | { |
1658 | GBytes *bytes = gsk_render_node_serialize (node); |
1659 | GError *error = NULL; |
1660 | |
1661 | if (!g_file_replace_contents (file: gtk_file_chooser_get_file (GTK_FILE_CHOOSER (dialog)), |
1662 | contents: g_bytes_get_data (bytes, NULL), |
1663 | length: g_bytes_get_size (bytes), |
1664 | NULL, |
1665 | FALSE, |
1666 | flags: 0, |
1667 | NULL, |
1668 | NULL, |
1669 | error: &error)) |
1670 | { |
1671 | GtkWidget *message_dialog; |
1672 | |
1673 | message_dialog = gtk_message_dialog_new (GTK_WINDOW (gtk_window_get_transient_for (GTK_WINDOW (dialog))), |
1674 | flags: GTK_DIALOG_MODAL|GTK_DIALOG_DESTROY_WITH_PARENT, |
1675 | type: GTK_MESSAGE_INFO, |
1676 | buttons: GTK_BUTTONS_OK, |
1677 | _("Saving RenderNode failed" )); |
1678 | gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (message_dialog), |
1679 | message_format: "%s" , error->message); |
1680 | g_signal_connect (message_dialog, "response" , G_CALLBACK (gtk_window_destroy), NULL); |
1681 | gtk_widget_show (widget: message_dialog); |
1682 | g_error_free (error); |
1683 | } |
1684 | |
1685 | g_bytes_unref (bytes); |
1686 | } |
1687 | |
1688 | gtk_window_destroy (GTK_WINDOW (dialog)); |
1689 | } |
1690 | |
1691 | static void |
1692 | render_node_save (GtkButton *button, |
1693 | GtkInspectorRecorder *recorder) |
1694 | { |
1695 | GskRenderNode *node; |
1696 | GtkWidget *dialog; |
1697 | char *filename, *nodename; |
1698 | |
1699 | node = get_selected_node (recorder); |
1700 | if (node == NULL) |
1701 | return; |
1702 | |
1703 | dialog = gtk_file_chooser_dialog_new (title: "" , |
1704 | GTK_WINDOW (gtk_widget_get_root (GTK_WIDGET (recorder))), |
1705 | action: GTK_FILE_CHOOSER_ACTION_SAVE, |
1706 | _("_Cancel" ), GTK_RESPONSE_CANCEL, |
1707 | _("_Save" ), GTK_RESPONSE_ACCEPT, |
1708 | NULL); |
1709 | nodename = node_name (node); |
1710 | filename = g_strdup_printf (format: "%s.node" , nodename); |
1711 | g_free (mem: nodename); |
1712 | gtk_file_chooser_set_current_name (GTK_FILE_CHOOSER (dialog), name: filename); |
1713 | g_free (mem: filename); |
1714 | gtk_dialog_set_default_response (GTK_DIALOG (dialog), response_id: GTK_RESPONSE_ACCEPT); |
1715 | gtk_window_set_modal (GTK_WINDOW (dialog), TRUE); |
1716 | g_signal_connect (dialog, "response" , G_CALLBACK (render_node_save_response), node); |
1717 | gtk_widget_show (widget: dialog); |
1718 | } |
1719 | |
1720 | static void |
1721 | render_node_clip (GtkButton *button, |
1722 | GtkInspectorRecorder *recorder) |
1723 | { |
1724 | GskRenderNode *node; |
1725 | GdkClipboard *clipboard; |
1726 | GBytes *bytes; |
1727 | GdkContentProvider *content; |
1728 | |
1729 | node = get_selected_node (recorder); |
1730 | if (node == NULL) |
1731 | return; |
1732 | |
1733 | bytes = gsk_render_node_serialize (node); |
1734 | content = gdk_content_provider_new_for_bytes (mime_type: "text/plain;charset=utf-8" , bytes); |
1735 | |
1736 | clipboard = gtk_widget_get_clipboard (GTK_WIDGET (recorder)); |
1737 | |
1738 | gdk_clipboard_set_content (clipboard, provider: content); |
1739 | |
1740 | g_object_unref (object: content); |
1741 | g_bytes_unref (bytes); |
1742 | } |
1743 | |
1744 | static void |
1745 | toggle_dark_mode (GtkToggleButton *button, |
1746 | GParamSpec *pspec, |
1747 | gpointer data) |
1748 | { |
1749 | GtkWidget *picture = data; |
1750 | |
1751 | if (gtk_toggle_button_get_active (toggle_button: button)) |
1752 | { |
1753 | gtk_widget_add_css_class (widget: picture, css_class: "dark" ); |
1754 | gtk_widget_remove_css_class (widget: picture, css_class: "light" ); |
1755 | } |
1756 | else |
1757 | { |
1758 | gtk_widget_remove_css_class (widget: picture, css_class: "dark" ); |
1759 | gtk_widget_add_css_class (widget: picture, css_class: "light" ); |
1760 | } |
1761 | } |
1762 | |
1763 | static void |
1764 | setup_widget_for_recording (GtkListItemFactory *factory, |
1765 | GtkListItem *item, |
1766 | gpointer data) |
1767 | { |
1768 | GtkWidget *row, *box, *label; |
1769 | |
1770 | row = g_object_new (GTK_TYPE_INSPECTOR_RECORDER_ROW, NULL); |
1771 | |
1772 | box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 6); |
1773 | label = gtk_label_new (str: "" ); |
1774 | gtk_label_set_xalign (GTK_LABEL (label), xalign: 0.0f); |
1775 | gtk_widget_set_hexpand (widget: label, TRUE); |
1776 | gtk_box_append (GTK_BOX (box), child: label); |
1777 | |
1778 | label = gtk_label_new (str: "" ); |
1779 | gtk_box_append (GTK_BOX (box), child: label); |
1780 | |
1781 | gtk_widget_set_margin_start (widget: box, margin: 6); |
1782 | gtk_widget_set_margin_end (widget: box, margin: 6); |
1783 | gtk_widget_set_margin_top (widget: box, margin: 6); |
1784 | gtk_widget_set_margin_bottom (widget: box, margin: 6); |
1785 | |
1786 | gtk_widget_set_parent (widget: box, parent: row); |
1787 | |
1788 | gtk_list_item_set_child (self: item, child: row); |
1789 | } |
1790 | |
1791 | static char * |
1792 | get_event_summary (GdkEvent *event) |
1793 | { |
1794 | double x, y; |
1795 | GdkEventType type; |
1796 | const char *name; |
1797 | |
1798 | gdk_event_get_position (event, x: &x, y: &y); |
1799 | type = gdk_event_get_event_type (event); |
1800 | name = event_type_name (type); |
1801 | |
1802 | switch (type) |
1803 | { |
1804 | case GDK_ENTER_NOTIFY: |
1805 | case GDK_LEAVE_NOTIFY: |
1806 | case GDK_MOTION_NOTIFY: |
1807 | case GDK_DRAG_ENTER: |
1808 | case GDK_DRAG_LEAVE: |
1809 | case GDK_DRAG_MOTION: |
1810 | case GDK_DROP_START: |
1811 | case GDK_TOUCH_BEGIN: |
1812 | case GDK_TOUCH_UPDATE: |
1813 | case GDK_TOUCH_END: |
1814 | case GDK_TOUCH_CANCEL: |
1815 | case GDK_TOUCHPAD_SWIPE: |
1816 | case GDK_TOUCHPAD_PINCH: |
1817 | case GDK_TOUCHPAD_HOLD: |
1818 | case GDK_BUTTON_PRESS: |
1819 | case GDK_BUTTON_RELEASE: |
1820 | return g_strdup_printf (format: "%s (%.2f %.2f)" , name, x, y); |
1821 | |
1822 | case GDK_KEY_PRESS: |
1823 | case GDK_KEY_RELEASE: |
1824 | { |
1825 | char *tmp, *ret; |
1826 | tmp = key_event_string (event); |
1827 | ret = g_strdup_printf (format: "%s %s\n" , name, tmp); |
1828 | g_free (mem: tmp); |
1829 | return ret; |
1830 | } |
1831 | |
1832 | case GDK_FOCUS_CHANGE: |
1833 | return g_strdup_printf (format: "%s %s" , name, gdk_focus_event_get_in (event) ? "In" : "Out" ); |
1834 | |
1835 | case GDK_GRAB_BROKEN: |
1836 | case GDK_PROXIMITY_IN: |
1837 | case GDK_PROXIMITY_OUT: |
1838 | case GDK_PAD_BUTTON_PRESS: |
1839 | case GDK_PAD_BUTTON_RELEASE: |
1840 | case GDK_PAD_RING: |
1841 | case GDK_PAD_STRIP: |
1842 | case GDK_PAD_GROUP_MODE: |
1843 | case GDK_DELETE: |
1844 | return g_strdup_printf (format: "%s" , name); |
1845 | |
1846 | case GDK_SCROLL: |
1847 | if (gdk_scroll_event_get_direction (event) == GDK_SCROLL_SMOOTH) |
1848 | { |
1849 | gdk_scroll_event_get_deltas (event, delta_x: &x, delta_y: &y); |
1850 | return g_strdup_printf (format: "%s %.2f %.2f" , name, x, y); |
1851 | } |
1852 | else |
1853 | { |
1854 | return g_strdup_printf (format: "%s %s" , name, scroll_direction_name (dir: gdk_scroll_event_get_direction (event))); |
1855 | } |
1856 | break; |
1857 | |
1858 | case GDK_EVENT_LAST: |
1859 | default: |
1860 | g_assert_not_reached (); |
1861 | } |
1862 | } |
1863 | |
1864 | static void |
1865 | bind_widget_for_recording (GtkListItemFactory *factory, |
1866 | GtkListItem *item, |
1867 | gpointer data) |
1868 | { |
1869 | GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (data); |
1870 | GtkInspectorRecording *recording = gtk_list_item_get_item (self: item); |
1871 | GtkWidget *row, *box, *label, *label2; |
1872 | char *text; |
1873 | |
1874 | row = gtk_list_item_get_child (self: item); |
1875 | box = gtk_widget_get_first_child (widget: row); |
1876 | label = gtk_widget_get_first_child (widget: box); |
1877 | label2 = gtk_widget_get_next_sibling (widget: label); |
1878 | |
1879 | g_object_set (object: row, first_property_name: "sequence" , NULL, NULL); |
1880 | g_object_bind_property (source: recorder, source_property: "selected-sequence" , target: row, target_property: "match-sequence" , flags: G_BINDING_SYNC_CREATE); |
1881 | |
1882 | gtk_label_set_use_markup (GTK_LABEL (label), FALSE); |
1883 | |
1884 | if (GTK_INSPECTOR_IS_RENDER_RECORDING (recording)) |
1885 | { |
1886 | gtk_label_set_label (GTK_LABEL (label), str: "Frame" ); |
1887 | gtk_label_set_use_markup (GTK_LABEL (label), FALSE); |
1888 | |
1889 | text = g_strdup_printf (format: "%.3f" , gtk_inspector_recording_get_timestamp (recording) / 1000.0); |
1890 | gtk_label_set_label (GTK_LABEL (label2), str: text); |
1891 | g_free (mem: text); |
1892 | } |
1893 | else if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording)) |
1894 | { |
1895 | GdkEvent *event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); |
1896 | |
1897 | g_object_set (object: row, first_property_name: "sequence" , gdk_event_get_event_sequence (event), NULL); |
1898 | |
1899 | text = get_event_summary (event); |
1900 | gtk_label_set_label (GTK_LABEL (label), str: text); |
1901 | g_free (mem: text); |
1902 | |
1903 | text = g_strdup_printf (format: "%.3f" , gtk_inspector_recording_get_timestamp (recording) / 1000.0); |
1904 | gtk_label_set_label (GTK_LABEL (label2), str: text); |
1905 | g_free (mem: text); |
1906 | } |
1907 | else |
1908 | { |
1909 | gtk_label_set_label (GTK_LABEL (label), str: "<b>Start of Recording</b>" ); |
1910 | gtk_label_set_use_markup (GTK_LABEL (label), TRUE); |
1911 | gtk_label_set_label (GTK_LABEL (label2), str: "" ); |
1912 | } |
1913 | } |
1914 | |
1915 | static void |
1916 | node_property_activated (GtkTreeView *tv, |
1917 | GtkTreePath *path, |
1918 | GtkTreeViewColumn *col, |
1919 | GtkInspectorRecorder *recorder) |
1920 | { |
1921 | GtkTreeIter iter; |
1922 | GdkRectangle rect; |
1923 | GdkTexture *texture; |
1924 | gboolean visible; |
1925 | GtkWidget *popover; |
1926 | GtkWidget *image; |
1927 | |
1928 | gtk_tree_model_get_iter (GTK_TREE_MODEL (recorder->render_node_properties), iter: &iter, path); |
1929 | gtk_tree_model_get (GTK_TREE_MODEL (recorder->render_node_properties), iter: &iter, |
1930 | 2, &visible, |
1931 | 3, &texture, |
1932 | -1); |
1933 | gtk_tree_view_get_cell_area (tree_view: tv, path, column: col, rect: &rect); |
1934 | gtk_tree_view_convert_bin_window_to_widget_coords (tree_view: tv, bx: rect.x, by: rect.y, wx: &rect.x, wy: &rect.y); |
1935 | |
1936 | if (texture == NULL || visible) |
1937 | return; |
1938 | |
1939 | popover = gtk_popover_new (); |
1940 | gtk_widget_set_parent (widget: popover, GTK_WIDGET (tv)); |
1941 | gtk_popover_set_pointing_to (GTK_POPOVER (popover), rect: &rect); |
1942 | |
1943 | image = gtk_image_new_from_paintable (paintable: GDK_PAINTABLE (ptr: texture)); |
1944 | gtk_widget_set_margin_start (widget: image, margin: 20); |
1945 | gtk_widget_set_margin_end (widget: image, margin: 20); |
1946 | gtk_widget_set_margin_top (widget: image, margin: 20); |
1947 | gtk_widget_set_margin_bottom (widget: image, margin: 20); |
1948 | gtk_popover_set_child (GTK_POPOVER (popover), child: image); |
1949 | gtk_popover_popup (GTK_POPOVER (popover)); |
1950 | |
1951 | g_signal_connect (popover, "unmap" , G_CALLBACK (gtk_widget_unparent), NULL); |
1952 | |
1953 | g_object_unref (object: texture); |
1954 | } |
1955 | |
1956 | static void |
1957 | gtk_inspector_recorder_get_property (GObject *object, |
1958 | guint param_id, |
1959 | GValue *value, |
1960 | GParamSpec *pspec) |
1961 | { |
1962 | GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object); |
1963 | |
1964 | switch (param_id) |
1965 | { |
1966 | case PROP_RECORDING: |
1967 | g_value_set_boolean (value, v_boolean: recorder->recording != NULL); |
1968 | break; |
1969 | |
1970 | case PROP_DEBUG_NODES: |
1971 | g_value_set_boolean (value, v_boolean: recorder->debug_nodes); |
1972 | break; |
1973 | |
1974 | case PROP_HIGHLIGHT_SEQUENCES: |
1975 | g_value_set_boolean (value, v_boolean: recorder->highlight_sequences); |
1976 | break; |
1977 | |
1978 | case PROP_SELECTED_SEQUENCE: |
1979 | g_value_set_pointer (value, v_pointer: recorder->selected_sequence); |
1980 | break; |
1981 | |
1982 | default: |
1983 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); |
1984 | break; |
1985 | } |
1986 | } |
1987 | |
1988 | static void |
1989 | gtk_inspector_recorder_set_property (GObject *object, |
1990 | guint param_id, |
1991 | const GValue *value, |
1992 | GParamSpec *pspec) |
1993 | { |
1994 | GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object); |
1995 | |
1996 | switch (param_id) |
1997 | { |
1998 | case PROP_RECORDING: |
1999 | gtk_inspector_recorder_set_recording (recorder, record: g_value_get_boolean (value)); |
2000 | break; |
2001 | |
2002 | case PROP_DEBUG_NODES: |
2003 | gtk_inspector_recorder_set_debug_nodes (recorder, debug_nodes: g_value_get_boolean (value)); |
2004 | break; |
2005 | |
2006 | case PROP_HIGHLIGHT_SEQUENCES: |
2007 | gtk_inspector_recorder_set_highlight_sequences (recorder, highlight_sequences: g_value_get_boolean (value)); |
2008 | break; |
2009 | |
2010 | case PROP_SELECTED_SEQUENCE: |
2011 | recorder->selected_sequence = g_value_get_pointer (value); |
2012 | break; |
2013 | |
2014 | default: |
2015 | G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec); |
2016 | break; |
2017 | } |
2018 | } |
2019 | |
2020 | static void |
2021 | gtk_inspector_recorder_dispose (GObject *object) |
2022 | { |
2023 | GtkInspectorRecorder *recorder = GTK_INSPECTOR_RECORDER (object); |
2024 | |
2025 | g_clear_pointer (&recorder->box, gtk_widget_unparent); |
2026 | g_clear_object (&recorder->render_node_model); |
2027 | g_clear_object (&recorder->render_node_root_model); |
2028 | g_clear_object (&recorder->render_node_selection); |
2029 | |
2030 | G_OBJECT_CLASS (gtk_inspector_recorder_parent_class)->dispose (object); |
2031 | } |
2032 | |
2033 | static void |
2034 | gtk_inspector_recorder_class_init (GtkInspectorRecorderClass *klass) |
2035 | { |
2036 | GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); |
2037 | GObjectClass *object_class = G_OBJECT_CLASS (klass); |
2038 | |
2039 | object_class->get_property = gtk_inspector_recorder_get_property; |
2040 | object_class->set_property = gtk_inspector_recorder_set_property; |
2041 | object_class->dispose = gtk_inspector_recorder_dispose; |
2042 | |
2043 | props[PROP_RECORDING] = |
2044 | g_param_spec_boolean (name: "recording" , |
2045 | nick: "Recording" , |
2046 | blurb: "Whether the recorder is currently recording" , |
2047 | FALSE, |
2048 | flags: G_PARAM_READWRITE); |
2049 | props[PROP_DEBUG_NODES] = |
2050 | g_param_spec_boolean (name: "debug-nodes" , |
2051 | nick: "Debug nodes" , |
2052 | blurb: "Whether to insert extra debug nodes in the tree" , |
2053 | FALSE, |
2054 | flags: G_PARAM_READWRITE); |
2055 | |
2056 | props[PROP_HIGHLIGHT_SEQUENCES] = g_param_spec_boolean (name: "highlight-sequences" , nick: "" , blurb: "" , FALSE, flags: G_PARAM_READWRITE); |
2057 | props[PROP_SELECTED_SEQUENCE] = g_param_spec_pointer (name: "selected-sequence" , nick: "" , blurb: "" , flags: G_PARAM_READWRITE); |
2058 | |
2059 | g_object_class_install_properties (oclass: object_class, n_pspecs: LAST_PROP, pspecs: props); |
2060 | |
2061 | gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/org/gtk/libgtk/inspector/recorder.ui" ); |
2062 | |
2063 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, box); |
2064 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recordings); |
2065 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recordings_list); |
2066 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_view); |
2067 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_list); |
2068 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_save_button); |
2069 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, render_node_clip_button); |
2070 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, node_property_tree); |
2071 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, recording_data_stack); |
2072 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, event_view); |
2073 | gtk_widget_class_bind_template_child (widget_class, GtkInspectorRecorder, event_property_tree); |
2074 | |
2075 | gtk_widget_class_bind_template_callback (widget_class, recordings_clear_all); |
2076 | gtk_widget_class_bind_template_callback (widget_class, recording_selected); |
2077 | gtk_widget_class_bind_template_callback (widget_class, render_node_save); |
2078 | gtk_widget_class_bind_template_callback (widget_class, render_node_clip); |
2079 | gtk_widget_class_bind_template_callback (widget_class, node_property_activated); |
2080 | gtk_widget_class_bind_template_callback (widget_class, toggle_dark_mode); |
2081 | |
2082 | gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT); |
2083 | } |
2084 | |
2085 | static void |
2086 | gtk_inspector_recorder_init (GtkInspectorRecorder *recorder) |
2087 | { |
2088 | GtkListItemFactory *factory; |
2089 | |
2090 | gtk_widget_init_template (GTK_WIDGET (recorder)); |
2091 | |
2092 | factory = gtk_signal_list_item_factory_new (); |
2093 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_widget_for_recording), recorder); |
2094 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_widget_for_recording), recorder); |
2095 | gtk_list_view_set_factory (GTK_LIST_VIEW (recorder->recordings_list), factory); |
2096 | g_object_unref (object: factory); |
2097 | |
2098 | recorder->render_node_root_model = g_list_store_new (GDK_TYPE_PAINTABLE); |
2099 | recorder->render_node_model = gtk_tree_list_model_new (g_object_ref (G_LIST_MODEL (recorder->render_node_root_model)), |
2100 | FALSE, |
2101 | TRUE, |
2102 | create_func: create_list_model_for_render_node_paintable, |
2103 | NULL, NULL); |
2104 | recorder->render_node_selection = gtk_single_selection_new (g_object_ref (G_LIST_MODEL (recorder->render_node_model))); |
2105 | g_signal_connect (recorder->render_node_selection, "notify::selected-item" , G_CALLBACK (render_node_list_selection_changed), recorder); |
2106 | |
2107 | factory = gtk_signal_list_item_factory_new (); |
2108 | g_signal_connect (factory, "setup" , G_CALLBACK (setup_widget_for_render_node), NULL); |
2109 | g_signal_connect (factory, "bind" , G_CALLBACK (bind_widget_for_render_node), NULL); |
2110 | |
2111 | gtk_list_view_set_factory (GTK_LIST_VIEW (recorder->render_node_list), factory); |
2112 | g_object_unref (object: factory); |
2113 | gtk_list_view_set_model (GTK_LIST_VIEW (recorder->render_node_list), |
2114 | model: GTK_SELECTION_MODEL (ptr: recorder->render_node_selection)); |
2115 | |
2116 | recorder->render_node_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE)); |
2117 | gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->node_property_tree), model: recorder->render_node_properties); |
2118 | g_object_unref (object: recorder->render_node_properties); |
2119 | |
2120 | recorder->event_properties = GTK_TREE_MODEL (gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_BOOLEAN, GDK_TYPE_TEXTURE)); |
2121 | gtk_tree_view_set_model (GTK_TREE_VIEW (recorder->event_property_tree), model: recorder->event_properties); |
2122 | g_object_unref (object: recorder->event_properties); |
2123 | } |
2124 | |
2125 | static void |
2126 | gtk_inspector_recorder_add_recording (GtkInspectorRecorder *recorder, |
2127 | GtkInspectorRecording *recording) |
2128 | { |
2129 | g_list_store_append (store: G_LIST_STORE (ptr: recorder->recordings), item: recording); |
2130 | } |
2131 | |
2132 | void |
2133 | gtk_inspector_recorder_set_recording (GtkInspectorRecorder *recorder, |
2134 | gboolean recording) |
2135 | { |
2136 | if (gtk_inspector_recorder_is_recording (recorder) == recording) |
2137 | return; |
2138 | |
2139 | if (recording) |
2140 | { |
2141 | recorder->recording = gtk_inspector_start_recording_new (); |
2142 | recorder->start_time = 0; |
2143 | gtk_inspector_recorder_add_recording (recorder, recording: recorder->recording); |
2144 | } |
2145 | else |
2146 | { |
2147 | g_clear_object (&recorder->recording); |
2148 | } |
2149 | |
2150 | g_object_notify_by_pspec (G_OBJECT (recorder), pspec: props[PROP_RECORDING]); |
2151 | } |
2152 | |
2153 | gboolean |
2154 | gtk_inspector_recorder_is_recording (GtkInspectorRecorder *recorder) |
2155 | { |
2156 | return recorder->recording != NULL; |
2157 | } |
2158 | |
2159 | void |
2160 | gtk_inspector_recorder_record_render (GtkInspectorRecorder *recorder, |
2161 | GtkWidget *widget, |
2162 | GskRenderer *renderer, |
2163 | GdkSurface *surface, |
2164 | const cairo_region_t *region, |
2165 | GskRenderNode *node) |
2166 | { |
2167 | GtkInspectorRecording *recording; |
2168 | GdkFrameClock *frame_clock; |
2169 | gint64 frame_time; |
2170 | |
2171 | if (!gtk_inspector_recorder_is_recording (recorder)) |
2172 | return; |
2173 | |
2174 | frame_clock = gtk_widget_get_frame_clock (widget); |
2175 | frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
2176 | |
2177 | if (recorder->start_time == 0) |
2178 | { |
2179 | recorder->start_time = frame_time; |
2180 | frame_time = 0; |
2181 | } |
2182 | else |
2183 | { |
2184 | frame_time = frame_time - recorder->start_time; |
2185 | } |
2186 | |
2187 | recording = gtk_inspector_render_recording_new (timestamp: frame_time, |
2188 | profiler: gsk_renderer_get_profiler (renderer), |
2189 | area: &(GdkRectangle) { 0, 0, |
2190 | gdk_surface_get_width (surface), |
2191 | gdk_surface_get_height (surface) }, |
2192 | clip_region: region, |
2193 | node); |
2194 | gtk_inspector_recorder_add_recording (recorder, recording); |
2195 | g_object_unref (object: recording); |
2196 | } |
2197 | |
2198 | void |
2199 | gtk_inspector_recorder_record_event (GtkInspectorRecorder *recorder, |
2200 | GtkWidget *widget, |
2201 | GdkEvent *event) |
2202 | { |
2203 | GtkInspectorRecording *recording; |
2204 | GdkFrameClock *frame_clock; |
2205 | gint64 frame_time; |
2206 | |
2207 | if (!gtk_inspector_recorder_is_recording (recorder)) |
2208 | return; |
2209 | |
2210 | frame_clock = gtk_widget_get_frame_clock (widget); |
2211 | frame_time = gdk_frame_clock_get_frame_time (frame_clock); |
2212 | |
2213 | if (recorder->start_time == 0) |
2214 | { |
2215 | recorder->start_time = frame_time; |
2216 | frame_time = 0; |
2217 | } |
2218 | else |
2219 | { |
2220 | frame_time = frame_time - recorder->start_time; |
2221 | } |
2222 | |
2223 | recording = gtk_inspector_event_recording_new (timestamp: frame_time, event); |
2224 | gtk_inspector_recorder_add_recording (recorder, recording); |
2225 | g_object_unref (object: recording); |
2226 | } |
2227 | |
2228 | void |
2229 | gtk_inspector_recorder_set_debug_nodes (GtkInspectorRecorder *recorder, |
2230 | gboolean debug_nodes) |
2231 | { |
2232 | guint flags; |
2233 | |
2234 | if (recorder->debug_nodes == debug_nodes) |
2235 | return; |
2236 | |
2237 | recorder->debug_nodes = debug_nodes; |
2238 | |
2239 | flags = gtk_get_debug_flags (); |
2240 | |
2241 | if (debug_nodes) |
2242 | flags |= GTK_DEBUG_SNAPSHOT; |
2243 | else |
2244 | flags &= ~GTK_DEBUG_SNAPSHOT; |
2245 | |
2246 | gtk_set_debug_flags (flags); |
2247 | |
2248 | g_object_notify_by_pspec (G_OBJECT (recorder), pspec: props[PROP_DEBUG_NODES]); |
2249 | } |
2250 | |
2251 | void |
2252 | gtk_inspector_recorder_set_highlight_sequences (GtkInspectorRecorder *recorder, |
2253 | gboolean highlight_sequences) |
2254 | { |
2255 | GdkEventSequence *sequence = NULL; |
2256 | |
2257 | if (recorder->highlight_sequences == highlight_sequences) |
2258 | return; |
2259 | |
2260 | recorder->highlight_sequences = highlight_sequences; |
2261 | |
2262 | if (highlight_sequences) |
2263 | { |
2264 | GtkSingleSelection *selection; |
2265 | GtkInspectorRecording *recording; |
2266 | GdkEvent *event; |
2267 | |
2268 | selection = GTK_SINGLE_SELECTION (ptr: gtk_list_view_get_model (GTK_LIST_VIEW (recorder->recordings_list))); |
2269 | recording = gtk_single_selection_get_selected_item (self: selection); |
2270 | |
2271 | if (GTK_INSPECTOR_IS_EVENT_RECORDING (recording)) |
2272 | { |
2273 | event = gtk_inspector_event_recording_get_event (GTK_INSPECTOR_EVENT_RECORDING (recording)); |
2274 | sequence = gdk_event_get_event_sequence (event); |
2275 | } |
2276 | } |
2277 | |
2278 | gtk_inspector_recorder_set_selected_sequence (recorder, sequence); |
2279 | |
2280 | g_object_notify_by_pspec (G_OBJECT (recorder), pspec: props[PROP_HIGHLIGHT_SEQUENCES]); |
2281 | } |
2282 | |
2283 | void |
2284 | gtk_inspector_recorder_set_selected_sequence (GtkInspectorRecorder *recorder, |
2285 | GdkEventSequence *sequence) |
2286 | { |
2287 | if (recorder->selected_sequence == sequence) |
2288 | return; |
2289 | |
2290 | recorder->selected_sequence = sequence; |
2291 | |
2292 | g_object_notify_by_pspec (G_OBJECT (recorder), pspec: props[PROP_SELECTED_SEQUENCE]); |
2293 | } |
2294 | |
2295 | |
2296 | |