1#include <gtk/gtk.h>
2
3static char *write_to_filename = NULL;
4static gboolean compare_node;
5
6static GOptionEntry options[] = {
7 { "write", 'o', 0, G_OPTION_ARG_STRING, &write_to_filename, "Write PNG file", NULL },
8 { "compare", 'c', 0, G_OPTION_ARG_NONE, &compare_node, "Compare render to render_texture", NULL },
9 { NULL }
10};
11
12
13
14typedef struct _GtkNodeView GtkNodeView;
15typedef struct _GtkNodeViewClass GtkNodeViewClass;
16
17#define GTK_TYPE_NODE_VIEW (gtk_node_view_get_type ())
18#define GTK_NODE_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST(obj, GTK_TYPE_NODE_VIEW, GtkNodeView))
19#define GTK_NODE_VIEW_CLASS(cls) (G_TYPE_CHECK_CLASS_CAST(cls, GTK_TYPE_NODE_VIEW, GtkNodeViewClass))
20struct _GtkNodeView
21{
22 GtkWidget parent_instance;
23
24 GskRenderNode *node;
25 GFileMonitor *file_monitor;
26};
27
28struct _GtkNodeViewClass
29{
30 GtkWidgetClass parent_class;
31};
32
33GType gtk_node_view_get_type (void) G_GNUC_CONST;
34
35
36G_DEFINE_TYPE(GtkNodeView, gtk_node_view, GTK_TYPE_WIDGET)
37
38static void
39deserialize_error_func (const GskParseLocation *start,
40 const GskParseLocation *end,
41 const GError *error,
42 gpointer user_data)
43{
44 GString *string = g_string_new (init: "<data>");
45
46 g_string_append_printf (string, format: ":%zu:%zu",
47 start->lines + 1, start->line_chars + 1);
48 if (start->lines != end->lines || start->line_chars != end->line_chars)
49 {
50 g_string_append (string, val: "-");
51 if (start->lines != end->lines)
52 g_string_append_printf (string, format: "%zu:", end->lines + 1);
53 g_string_append_printf (string, format: "%zu", end->line_chars + 1);
54 }
55
56 g_warning ("Error at %s: %s", string->str, error->message);
57
58 g_string_free (string, TRUE);
59}
60
61static void
62load_file_contents (GtkNodeView *self,
63 GFile *file)
64{
65 GBytes *bytes;
66 GError *error = NULL;
67
68 bytes = g_file_load_bytes (file, NULL, NULL, NULL);
69 if (bytes == NULL)
70 return;
71
72 if (!g_utf8_validate (str: g_bytes_get_data (bytes, NULL), max_len: g_bytes_get_size (bytes), NULL))
73 {
74 g_bytes_unref (bytes);
75 return;
76 }
77
78 self->node = gsk_render_node_deserialize (bytes, error_func: deserialize_error_func, user_data: &error);
79
80 if (error)
81 {
82 g_critical ("Invalid node file: %s", error->message);
83 g_clear_error (err: &error);
84 return;
85 }
86
87 gtk_widget_queue_draw (GTK_WIDGET (self));
88
89 g_bytes_unref (bytes);
90}
91
92static void
93file_changed_cb (GFileMonitor *monitor,
94 GFile *file,
95 GFile *other_file,
96 GFileMonitorEvent event_type,
97 gpointer user_data)
98{
99 GtkNodeView *self = user_data;
100
101 if (event_type == G_FILE_MONITOR_EVENT_CHANGED)
102 load_file_contents (self, file);
103}
104
105static void
106gtk_node_view_measure (GtkWidget *widget,
107 GtkOrientation orientation,
108 int for_size,
109 int *minimum,
110 int *natural,
111 int *minimum_baseline,
112 int *natural_baseline)
113{
114 GtkNodeView *self = GTK_NODE_VIEW (widget);
115 graphene_rect_t bounds;
116
117
118 if (self->node == NULL)
119 return;
120
121 gsk_render_node_get_bounds (node: self->node, bounds: &bounds);
122
123 if (orientation == GTK_ORIENTATION_HORIZONTAL)
124 {
125 *minimum = *natural = bounds.origin.x + bounds.size.width;
126 }
127 else /* VERTICAL */
128 {
129 *minimum = *natural = bounds.origin.y + bounds.size.height;
130 }
131}
132
133static void
134gtk_node_view_snapshot (GtkWidget *widget,
135 GtkSnapshot *snapshot)
136{
137 GtkNodeView *self = GTK_NODE_VIEW (widget);
138
139 if (self->node != NULL)
140 gtk_snapshot_append_node (snapshot, node: self->node);
141}
142
143static void
144gtk_node_view_finalize (GObject *object)
145{
146 GtkNodeView *self = GTK_NODE_VIEW (object);
147
148 if (self->node)
149 gsk_render_node_unref (node: self->node);
150
151 G_OBJECT_CLASS (gtk_node_view_parent_class)->finalize (object);
152}
153
154static void
155gtk_node_view_init (GtkNodeView *self)
156{
157 gtk_widget_set_overflow (GTK_WIDGET (self), overflow: GTK_OVERFLOW_HIDDEN);
158}
159
160static void
161gtk_node_view_class_init (GtkNodeViewClass *klass)
162{
163 GObjectClass *object_class = G_OBJECT_CLASS (klass);
164 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
165
166 object_class->finalize = gtk_node_view_finalize;
167
168 widget_class->measure = gtk_node_view_measure;
169 widget_class->snapshot = gtk_node_view_snapshot;
170}
171
172static void
173quit_cb (GtkWidget *widget,
174 gpointer data)
175{
176 gboolean *done = data;
177
178 *done = TRUE;
179
180 g_main_context_wakeup (NULL);
181}
182
183int
184main (int argc, char **argv)
185{
186 GtkWidget *window;
187 GtkWidget *nodeview;
188 graphene_rect_t node_bounds;
189 GOptionContext *option_context;
190 GError *error = NULL;
191 gboolean done = FALSE;
192 GFile *file;
193
194 option_context = g_option_context_new (parameter_string: "NODE-FILE [-o OUTPUT] [--compare]");
195 g_option_context_add_main_entries (context: option_context, entries: options, NULL);
196
197 if (argc < 2)
198 {
199 printf (format: "Usage: showrendernode NODEFILE [-o OUTPUT] [--compare]\n");
200 return 0;
201 }
202
203 if (!g_option_context_parse (context: option_context, argc: &argc, argv: &argv, error: &error))
204 {
205 g_printerr (format: "Option parsing failed: %s\n", error->message);
206 return 1;
207 }
208
209 g_option_context_free (context: option_context);
210 option_context = NULL;
211
212 g_message ("Compare: %d, write to filename: %s", compare_node, write_to_filename);
213
214 gtk_init ();
215
216 window = gtk_window_new ();
217 nodeview = g_object_new (GTK_TYPE_NODE_VIEW, NULL);
218
219 gtk_window_set_decorated (GTK_WINDOW (window), FALSE);
220
221 file = g_file_new_for_path (path: argv[1]);
222 load_file_contents (GTK_NODE_VIEW (nodeview), file);
223 GTK_NODE_VIEW (nodeview)->file_monitor = g_file_monitor_file (file, flags: G_FILE_MONITOR_NONE, NULL, error: &error);
224 g_object_unref (object: file);
225
226 if (error)
227 {
228 g_warning ("%s", error->message);
229 return -1;
230 }
231
232 g_signal_connect (GTK_NODE_VIEW (nodeview)->file_monitor,
233 "changed", G_CALLBACK (file_changed_cb), nodeview);
234
235 if (write_to_filename != NULL)
236 {
237 GdkSurface *surface = gdk_surface_new_toplevel (display: gdk_display_get_default());
238 GskRenderer *renderer = gsk_renderer_new_for_surface (surface);
239 GdkTexture *texture = gsk_renderer_render_texture (renderer, GTK_NODE_VIEW (nodeview)->node, NULL);
240
241 g_message ("Writing .node file to .png using %s", G_OBJECT_TYPE_NAME (renderer));
242
243 g_assert (texture != NULL);
244
245 gdk_texture_save_to_png (texture, filename: write_to_filename);
246
247 gsk_renderer_unrealize (renderer);
248
249 g_object_unref (object: texture);
250 g_object_unref (object: renderer);
251 g_object_unref (object: surface);
252 }
253
254 if (compare_node)
255 {
256 GtkWidget *box = gtk_box_new (orientation: GTK_ORIENTATION_VERTICAL, spacing: 12);
257 GdkSurface *gdk_surface = gdk_surface_new_toplevel (display: gdk_display_get_default());
258 GskRenderer *renderer = gsk_renderer_new_for_surface (surface: gdk_surface);
259 GdkTexture *texture = gsk_renderer_render_texture (renderer, GTK_NODE_VIEW (nodeview)->node, NULL);
260 GtkWidget *image = gtk_image_new_from_paintable (paintable: GDK_PAINTABLE (ptr: texture));
261
262 gtk_widget_set_size_request (widget: image,
263 width: gdk_texture_get_width (texture),
264 height: gdk_texture_get_height (texture));
265
266 gtk_box_append (GTK_BOX (box), child: nodeview);
267 gtk_box_append (GTK_BOX (box), child: image);
268 gtk_window_set_child (GTK_WINDOW (window), child: box);
269
270 gsk_renderer_unrealize (renderer);
271 g_object_unref (object: texture);
272 g_object_unref (object: renderer);
273 g_object_unref (object: gdk_surface);
274 }
275 else
276 {
277 gtk_window_set_child (GTK_WINDOW (window), child: nodeview);
278 }
279
280 gsk_render_node_get_bounds (GTK_NODE_VIEW (nodeview)->node, bounds: &node_bounds);
281 gtk_window_set_default_size (GTK_WINDOW (window),
282 MAX (600, node_bounds.size.width),
283 MAX (500, node_bounds.size.height));
284
285 g_signal_connect (window, "destroy", G_CALLBACK (quit_cb), &done);
286 gtk_widget_show (widget: window);
287
288 while (!done)
289 g_main_context_iteration (NULL, TRUE);
290
291 return 0;
292}
293

source code of gtk/tests/showrendernode.c