1 /*
2  * Copyright (c) 2014 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 #include <glib/gi18n-lib.h>
20 
21 #include "visual.h"
22 
23 #include "gtkadjustment.h"
24 #include "gtkbox.h"
25 #include "gtkcomboboxtext.h"
26 #include "gtkdebug.h"
27 #include "gtkprivate.h"
28 #include "gtksettings.h"
29 #include "gtkswitch.h"
30 #include "gtkscale.h"
31 #include "gtkwindow.h"
32 #include "gtkcssproviderprivate.h"
33 #include "gtkversion.h"
34 
35 #include "fallback-c89.c"
36 
37 #ifdef GDK_WINDOWING_X11
38 #include "x11/gdkx.h"
39 #endif
40 #ifdef GDK_WINDOWING_WAYLAND
41 #include "wayland/gdkwayland.h"
42 #endif
43 
44 #include "gdk/gdk-private.h"
45 
46 #define EPSILON               1e-10
47 
48 struct _GtkInspectorVisualPrivate
49 {
50   GtkWidget *visual_box;
51   GtkWidget *theme_combo;
52   GtkWidget *dark_switch;
53   GtkWidget *icon_combo;
54   GtkWidget *cursor_combo;
55   GtkWidget *cursor_size_spin;
56   GtkWidget *direction_combo;
57   GtkWidget *font_button;
58   GtkWidget *hidpi_spin;
59   GtkWidget *animation_switch;
60   GtkWidget *font_scale_entry;
61   GtkAdjustment *font_scale_adjustment;
62   GtkAdjustment *scale_adjustment;
63   GtkAdjustment *slowdown_adjustment;
64   GtkWidget *slowdown_entry;
65   GtkAdjustment *cursor_size_adjustment;
66 
67   GtkWidget *debug_box;
68   GtkWidget *rendering_mode_combo;
69   GtkWidget *updates_switch;
70   GtkWidget *baselines_switch;
71   GtkWidget *layout_switch;
72   GtkWidget *touchscreen_switch;
73 
74   GtkWidget *gl_box;
75   GtkWidget *gl_combo;
76   GtkWidget *software_gl_switch;
77   GtkWidget *software_surface_switch;
78   GtkWidget *texture_rectangle_switch;
79 
80   GtkAdjustment *focus_adjustment;
81 };
82 
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorVisual,gtk_inspector_visual,GTK_TYPE_SCROLLED_WINDOW)83 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorVisual, gtk_inspector_visual, GTK_TYPE_SCROLLED_WINDOW)
84 
85 static void
86 fix_direction_recurse (GtkWidget *widget,
87                        gpointer   data)
88 {
89   GtkTextDirection dir = GPOINTER_TO_INT (data);
90 
91   g_object_ref (widget);
92 
93   gtk_widget_set_direction (widget, dir);
94   if (GTK_IS_CONTAINER (widget))
95     gtk_container_forall (GTK_CONTAINER (widget), fix_direction_recurse, data);
96 
97   g_object_unref (widget);
98 }
99 
100 static GtkTextDirection initial_direction;
101 
102 static void
fix_direction(GtkWidget * iw)103 fix_direction (GtkWidget *iw)
104 {
105   fix_direction_recurse (iw, GINT_TO_POINTER (initial_direction));
106 }
107 
108 static void
direction_changed(GtkComboBox * combo)109 direction_changed (GtkComboBox *combo)
110 {
111   GtkWidget *iw;
112   const gchar *direction;
113 
114   iw = gtk_widget_get_toplevel (GTK_WIDGET (combo));
115   fix_direction (iw);
116 
117   direction = gtk_combo_box_get_active_id (combo);
118   if (g_strcmp0 (direction, "ltr") == 0)
119     gtk_widget_set_default_direction (GTK_TEXT_DIR_LTR);
120   else
121     gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL);
122 }
123 
124 static void
init_direction(GtkInspectorVisual * vis)125 init_direction (GtkInspectorVisual *vis)
126 {
127   const gchar *direction;
128 
129   initial_direction = gtk_widget_get_default_direction ();
130   if (initial_direction == GTK_TEXT_DIR_LTR)
131     direction = "ltr";
132   else
133     direction = "rtl";
134   gtk_combo_box_set_active_id (GTK_COMBO_BOX (vis->priv->direction_combo), direction);
135 }
136 
137 static void
redraw_everything(void)138 redraw_everything (void)
139 {
140   GList *toplevels;
141   toplevels = gtk_window_list_toplevels ();
142   g_list_foreach (toplevels, (GFunc) gtk_widget_queue_draw, NULL);
143   g_list_free (toplevels);
144 }
145 
146 static double
get_font_scale(GtkInspectorVisual * vis)147 get_font_scale (GtkInspectorVisual *vis)
148 {
149 #ifdef GDK_WINDOWING_X11
150   if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
151     {
152       int dpi_int;
153 
154       g_object_get (gtk_settings_get_default (),
155                     "gtk-xft-dpi", &dpi_int,
156                     NULL);
157 
158       return dpi_int / (96.0 * 1024.0);
159     }
160 #endif
161 #ifdef GDK_WINDOWING_WAYLAND
162   if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
163     {
164       int dpi_int;
165 
166       g_object_get (gtk_settings_get_default (),
167                     "gtk-xft-dpi", &dpi_int,
168                     NULL);
169 
170       return dpi_int / (96.0 * 1024.0);
171     }
172 #endif
173 
174   return 1.0;
175 }
176 
177 static void
update_font_scale(GtkInspectorVisual * vis,gdouble factor,gboolean update_adjustment,gboolean update_entry)178 update_font_scale (GtkInspectorVisual *vis,
179                    gdouble             factor,
180                    gboolean            update_adjustment,
181                    gboolean            update_entry)
182 {
183   g_object_set (gtk_settings_get_default (),
184                 "gtk-xft-dpi", (gint)(factor * 96 * 1024),
185                 NULL);
186 
187   if (update_adjustment)
188     gtk_adjustment_set_value (vis->priv->font_scale_adjustment, factor);
189 
190   if (update_entry)
191     {
192       gchar *str = g_strdup_printf ("%0.2f", factor);
193 
194       gtk_entry_set_text (GTK_ENTRY (vis->priv->font_scale_entry), str);
195       g_free (str);
196     }
197 }
198 
199 static void
font_scale_adjustment_changed(GtkAdjustment * adjustment,GtkInspectorVisual * vis)200 font_scale_adjustment_changed (GtkAdjustment      *adjustment,
201                                GtkInspectorVisual *vis)
202 {
203   gdouble factor;
204 
205   factor = gtk_adjustment_get_value (adjustment);
206   update_font_scale (vis, factor, FALSE, TRUE);
207 }
208 
209 static void
font_scale_entry_activated(GtkEntry * entry,GtkInspectorVisual * vis)210 font_scale_entry_activated (GtkEntry           *entry,
211                             GtkInspectorVisual *vis)
212 {
213   gdouble factor;
214   gchar *err = NULL;
215 
216   factor = g_strtod (gtk_entry_get_text (entry), &err);
217   if (err != NULL)
218     update_font_scale (vis, factor, TRUE, FALSE);
219 }
220 
221 static void
updates_activate(GtkSwitch * sw)222 updates_activate (GtkSwitch *sw)
223 {
224   gboolean updates;
225 
226   updates = gtk_switch_get_active (sw);
227   GDK_PRIVATE_CALL (gdk_display_set_debug_updates) (gdk_display_get_default (), updates);
228   redraw_everything ();
229 }
230 
231 static void
init_updates(GtkInspectorVisual * vis)232 init_updates (GtkInspectorVisual *vis)
233 {
234   gboolean updates;
235 
236   updates = GDK_PRIVATE_CALL (gdk_display_get_debug_updates) (gdk_display_get_default ());
237   gtk_switch_set_active (GTK_SWITCH (vis->priv->updates_switch), updates);
238 }
239 
240 static void
baselines_activate(GtkSwitch * sw)241 baselines_activate (GtkSwitch *sw)
242 {
243   guint flags;
244 
245   flags = gtk_get_debug_flags ();
246 
247   if (gtk_switch_get_active (sw))
248     flags |= GTK_DEBUG_BASELINES;
249   else
250     flags &= ~GTK_DEBUG_BASELINES;
251 
252   gtk_set_debug_flags (flags);
253   redraw_everything ();
254 }
255 
256 static void
layout_activate(GtkSwitch * sw)257 layout_activate (GtkSwitch *sw)
258 {
259   guint flags;
260 
261   flags = gtk_get_debug_flags ();
262 
263   if (gtk_switch_get_active (sw))
264     flags |= GTK_DEBUG_LAYOUT;
265   else
266     flags &= ~GTK_DEBUG_LAYOUT;
267 
268   gtk_set_debug_flags (flags);
269   redraw_everything ();
270 }
271 
272 static void
pixelcache_activate(GtkSwitch * sw)273 pixelcache_activate (GtkSwitch *sw)
274 {
275   guint flags;
276 
277   flags = gtk_get_debug_flags ();
278 
279   if (gtk_switch_get_active (sw))
280     flags |= GTK_DEBUG_PIXEL_CACHE;
281   else
282     flags &= ~GTK_DEBUG_PIXEL_CACHE;
283 
284   gtk_set_debug_flags (flags);
285   /* FIXME: this doesn't work, because it is redrawing
286    * _from_ the cache. We need to recurse over the tree
287    * and invalidate the pixel cache of every widget that
288    * has one.
289    */
290   redraw_everything ();
291 }
292 
293 static void
widget_resize_activate(GtkSwitch * sw)294 widget_resize_activate (GtkSwitch *sw)
295 {
296   guint flags = gtk_get_debug_flags ();
297 
298   if (gtk_switch_get_active (sw))
299     flags |= GTK_DEBUG_RESIZE;
300   else
301     flags &= ~GTK_DEBUG_RESIZE;
302 
303   gtk_set_debug_flags (flags);
304 }
305 
306 static void
fill_gtk(const gchar * path,GHashTable * t)307 fill_gtk (const gchar *path,
308           GHashTable  *t)
309 {
310   const gchar *dir_entry;
311   GDir *dir = g_dir_open (path, 0, NULL);
312 
313   if (!dir)
314     return;
315 
316 #if (GTK_MINOR_VERSION % 2)
317 #define MINOR (GTK_MINOR_VERSION + 1)
318 #else
319 #define MINOR GTK_MINOR_VERSION
320 #endif
321 
322   /* Keep this in sync with _gtk_css_find_theme_dir() in gtkcssprovider.c */
323   while ((dir_entry = g_dir_read_name (dir)))
324     {
325       gint i;
326       gboolean found = FALSE;
327 
328       for (i = MINOR; !found && i >= 0; i = i - 2)
329         {
330           gchar *filename, *subsubdir;
331 
332           if (i < 14)
333             i = 0;
334 
335           subsubdir = g_strdup_printf ("gtk-3.%d", i);
336           filename = g_build_filename (path, dir_entry, subsubdir, "gtk.css", NULL);
337           g_free (subsubdir);
338 
339           if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) &&
340               !g_hash_table_contains (t, dir_entry))
341             {
342               found = TRUE;
343               g_hash_table_add (t, g_strdup (dir_entry));
344             }
345 
346           g_free (filename);
347         }
348     }
349 
350 #undef MINOR
351 
352   g_dir_close (dir);
353 }
354 
355 static gchar*
get_data_path(const gchar * subdir)356 get_data_path (const gchar *subdir)
357 {
358   gchar *base_datadir, *full_datadir;
359 #if defined (GDK_WINDOWING_WIN32) || defined (GDK_WINDOWING_QUARTZ)
360   base_datadir = g_strdup (_gtk_get_datadir ());
361 #else
362   base_datadir = g_strdup (GTK_DATADIR);
363 #endif
364   full_datadir = g_build_filename (base_datadir, subdir, NULL);
365   g_free (base_datadir);
366   return full_datadir;
367 }
368 
369 static void
init_theme(GtkInspectorVisual * vis)370 init_theme (GtkInspectorVisual *vis)
371 {
372   GHashTable *t;
373   GHashTableIter iter;
374   gchar *theme, *path;
375   gchar **builtin_themes;
376   GList *list, *l;
377   guint i;
378   const gchar * const *dirs;
379 
380   t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
381   /* Builtin themes */
382   builtin_themes = g_resources_enumerate_children ("/org/gtk/libgtk/theme", 0, NULL);
383   for (i = 0; builtin_themes[i] != NULL; i++)
384     {
385       if (g_str_has_suffix (builtin_themes[i], "/"))
386         g_hash_table_add (t, g_strndup (builtin_themes[i], strlen (builtin_themes[i]) - 1));
387     }
388   g_strfreev (builtin_themes);
389 
390   path = _gtk_get_theme_dir ();
391   fill_gtk (path, t);
392   g_free (path);
393 
394   path = g_build_filename (g_get_user_data_dir (), "themes", NULL);
395   fill_gtk (path, t);
396   g_free (path);
397 
398   path = g_build_filename (g_get_home_dir (), ".themes", NULL);
399   fill_gtk (path, t);
400   g_free (path);
401 
402   dirs = g_get_system_data_dirs ();
403   for (i = 0; dirs[i]; i++)
404     {
405       path = g_build_filename (dirs[i], "themes", NULL);
406       fill_gtk (path, t);
407       g_free (path);
408     }
409 
410   list = NULL;
411   g_hash_table_iter_init (&iter, t);
412   while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))
413     list = g_list_insert_sorted (list, theme, (GCompareFunc)strcmp);
414 
415   for (l = list; l; l = l->next)
416     {
417       theme = l->data;
418       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (vis->priv->theme_combo), theme, theme);
419     }
420 
421   g_list_free (list);
422   g_hash_table_destroy (t);
423 
424   g_object_bind_property (gtk_settings_get_default (), "gtk-theme-name",
425                           vis->priv->theme_combo, "active-id",
426                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
427 
428   if (g_getenv ("GTK_THEME") != NULL)
429     {
430       /* theme is hardcoded, nothing we can do */
431       gtk_widget_set_sensitive (vis->priv->theme_combo, FALSE);
432       gtk_widget_set_tooltip_text (vis->priv->theme_combo , _("Theme is hardcoded by GTK_THEME"));
433     }
434 }
435 
436 static void
init_dark(GtkInspectorVisual * vis)437 init_dark (GtkInspectorVisual *vis)
438 {
439   g_object_bind_property (gtk_settings_get_default (), "gtk-application-prefer-dark-theme",
440                           vis->priv->dark_switch, "active",
441                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
442 
443   if (g_getenv ("GTK_THEME") != NULL)
444     {
445       /* theme is hardcoded, nothing we can do */
446       gtk_widget_set_sensitive (vis->priv->dark_switch, FALSE);
447       gtk_widget_set_tooltip_text (vis->priv->dark_switch, _("Theme is hardcoded by GTK_THEME"));
448     }
449 }
450 
451 static void
fill_icons(const gchar * path,GHashTable * t)452 fill_icons (const gchar *path,
453             GHashTable  *t)
454 {
455   const gchar *dir_entry;
456   GDir *dir;
457 
458   dir = g_dir_open (path, 0, NULL);
459   if (!dir)
460     return;
461 
462   while ((dir_entry = g_dir_read_name (dir)))
463     {
464       gchar *filename = g_build_filename (path, dir_entry, "index.theme", NULL);
465 
466       if (g_file_test (filename, G_FILE_TEST_IS_REGULAR) &&
467           g_strcmp0 (dir_entry, "hicolor") != 0 &&
468           !g_hash_table_contains (t, dir_entry))
469         g_hash_table_add (t, g_strdup (dir_entry));
470 
471       g_free (filename);
472     }
473 
474   g_dir_close (dir);
475 }
476 
477 static void
init_icons(GtkInspectorVisual * vis)478 init_icons (GtkInspectorVisual *vis)
479 {
480   GHashTable *t;
481   GHashTableIter iter;
482   gchar *theme, *path;
483   GList *list, *l;
484 
485   t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
486 
487   path = get_data_path ("icons");
488   fill_icons (path, t);
489   g_free (path);
490 
491   path = g_build_filename (g_get_user_data_dir (), "icons", NULL);
492   fill_icons (path, t);
493   g_free (path);
494 
495   list = NULL;
496   g_hash_table_iter_init (&iter, t);
497   while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))
498     list = g_list_insert_sorted (list, theme, (GCompareFunc)strcmp);
499 
500   for (l = list; l; l = l->next)
501     {
502       theme = l->data;
503       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (vis->priv->icon_combo), theme, theme);
504     }
505 
506   g_hash_table_destroy (t);
507   g_list_free (list);
508 
509   g_object_bind_property (gtk_settings_get_default (), "gtk-icon-theme-name",
510                           vis->priv->icon_combo, "active-id",
511                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
512 }
513 
514 static void
fill_cursors(const gchar * path,GHashTable * t)515 fill_cursors (const gchar *path,
516               GHashTable  *t)
517 {
518   const gchar *dir_entry;
519   GDir *dir;
520 
521   dir = g_dir_open (path, 0, NULL);
522   if (!dir)
523     return;
524 
525   while ((dir_entry = g_dir_read_name (dir)))
526     {
527       gchar *filename = g_build_filename (path, dir_entry, "cursors", NULL);
528 
529       if (g_file_test (filename, G_FILE_TEST_IS_DIR) &&
530           !g_hash_table_contains (t, dir_entry))
531         g_hash_table_add (t, g_strdup (dir_entry));
532 
533       g_free (filename);
534     }
535 
536   g_dir_close (dir);
537 }
538 
539 static void
init_cursors(GtkInspectorVisual * vis)540 init_cursors (GtkInspectorVisual *vis)
541 {
542   GHashTable *t;
543   GHashTableIter iter;
544   gchar *theme, *path;
545   GList *list, *l;
546 
547   t = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
548 
549   path = get_data_path ("icons");
550   fill_cursors (path, t);
551   g_free (path);
552 
553   path = g_build_filename (g_get_user_data_dir (), "icons", NULL);
554   fill_cursors (path, t);
555   g_free (path);
556 
557   list = NULL;
558   g_hash_table_iter_init (&iter, t);
559   while (g_hash_table_iter_next (&iter, (gpointer *)&theme, NULL))
560     list = g_list_insert_sorted (list, theme, (GCompareFunc)strcmp);
561 
562   for (l = list; l; l = l->next)
563     {
564       theme = l->data;
565       gtk_combo_box_text_append (GTK_COMBO_BOX_TEXT (vis->priv->cursor_combo), theme, theme);
566     }
567 
568   g_hash_table_destroy (t);
569   g_list_free (list);
570 
571   g_object_bind_property (gtk_settings_get_default (), "gtk-cursor-theme-name",
572                           vis->priv->cursor_combo, "active-id",
573                           G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
574 }
575 
576 static void
cursor_size_changed(GtkAdjustment * adjustment,GtkInspectorVisual * vis)577 cursor_size_changed (GtkAdjustment *adjustment, GtkInspectorVisual *vis)
578 {
579   gint size;
580 
581   size = gtk_adjustment_get_value (adjustment);
582   g_object_set (gtk_settings_get_default (), "gtk-cursor-theme-size", size, NULL);
583 }
584 
585 static void
init_cursor_size(GtkInspectorVisual * vis)586 init_cursor_size (GtkInspectorVisual *vis)
587 {
588   gint size;
589 
590   g_object_get (gtk_settings_get_default (), "gtk-cursor-theme-size", &size, NULL);
591   if (size == 0)
592     size = gdk_display_get_default_cursor_size (gdk_display_get_default ());
593 
594   gtk_adjustment_set_value (vis->priv->scale_adjustment, (gdouble)size);
595   g_signal_connect (vis->priv->cursor_size_adjustment, "value-changed",
596                     G_CALLBACK (cursor_size_changed), vis);
597 }
598 
599 static void
init_font(GtkInspectorVisual * vis)600 init_font (GtkInspectorVisual *vis)
601 {
602   g_object_bind_property (gtk_settings_get_default (), "gtk-font-name",
603                           vis->priv->font_button, "font-name",
604                           G_BINDING_BIDIRECTIONAL|G_BINDING_SYNC_CREATE);
605 }
606 
607 static void
init_font_scale(GtkInspectorVisual * vis)608 init_font_scale (GtkInspectorVisual *vis)
609 {
610   double scale;
611 
612   scale = get_font_scale (vis);
613   update_font_scale (vis, scale, TRUE, TRUE);
614   g_signal_connect (vis->priv->font_scale_adjustment, "value-changed",
615                     G_CALLBACK (font_scale_adjustment_changed), vis);
616   g_signal_connect (vis->priv->font_scale_entry, "activate",
617                     G_CALLBACK (font_scale_entry_activated), vis);
618 }
619 
620 #if defined (GDK_WINDOWING_X11)
621 static void
scale_changed(GtkAdjustment * adjustment,GtkInspectorVisual * vis)622 scale_changed (GtkAdjustment *adjustment, GtkInspectorVisual *vis)
623 {
624   GdkDisplay *display;
625   gint scale;
626 
627   scale = gtk_adjustment_get_value (adjustment);
628   display = gdk_display_get_default ();
629   gdk_x11_display_set_window_scale (display, scale);
630 }
631 #endif
632 
633 static void
init_scale(GtkInspectorVisual * vis)634 init_scale (GtkInspectorVisual *vis)
635 {
636 #if defined (GDK_WINDOWING_X11)
637   GdkScreen *screen;
638 
639   screen = gdk_screen_get_default ();
640   if (GDK_IS_X11_SCREEN (screen))
641     {
642       gdouble scale;
643 
644 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
645       scale = gdk_screen_get_monitor_scale_factor (screen, 0);
646 G_GNUC_END_IGNORE_DEPRECATIONS
647       gtk_adjustment_set_value (vis->priv->scale_adjustment, scale);
648       g_signal_connect (vis->priv->scale_adjustment, "value-changed",
649                         G_CALLBACK (scale_changed), vis);
650     }
651   else
652 #endif
653     {
654       gtk_adjustment_set_value (vis->priv->scale_adjustment, 1);
655       gtk_widget_set_sensitive (vis->priv->hidpi_spin, FALSE);
656       gtk_widget_set_tooltip_text (vis->priv->hidpi_spin,
657                                    _("Backend does not support window scaling"));
658     }
659 }
660 
661 static void
init_animation(GtkInspectorVisual * vis)662 init_animation (GtkInspectorVisual *vis)
663 {
664   g_object_bind_property (gtk_settings_get_default (), "gtk-enable-animations",
665                           vis->priv->animation_switch, "active",
666                           G_BINDING_BIDIRECTIONAL|G_BINDING_SYNC_CREATE);
667 }
668 
669 static void
update_slowdown(GtkInspectorVisual * vis,gdouble slowdown,gboolean update_adjustment,gboolean update_entry)670 update_slowdown (GtkInspectorVisual *vis,
671                  gdouble slowdown,
672                  gboolean update_adjustment,
673                  gboolean update_entry)
674 {
675   _gtk_set_slowdown (slowdown);
676 
677   if (update_adjustment)
678     gtk_adjustment_set_value (vis->priv->slowdown_adjustment,
679                               log2 (slowdown));
680 
681   if (update_entry)
682     {
683       gchar *str = g_strdup_printf ("%0.*f", 2, slowdown);
684 
685       gtk_entry_set_text (GTK_ENTRY (vis->priv->slowdown_entry), str);
686       g_free (str);
687     }
688 }
689 
690 static void
slowdown_adjustment_changed(GtkAdjustment * adjustment,GtkInspectorVisual * vis)691 slowdown_adjustment_changed (GtkAdjustment *adjustment,
692                              GtkInspectorVisual *vis)
693 {
694   gdouble value = gtk_adjustment_get_value (adjustment);
695   gdouble previous = CLAMP (log2 (_gtk_get_slowdown ()),
696                             gtk_adjustment_get_lower (adjustment),
697                             gtk_adjustment_get_upper (adjustment));
698 
699   if (fabs (value - previous) > EPSILON)
700     update_slowdown (vis, exp2 (value), FALSE, TRUE);
701 }
702 
703 static void
slowdown_entry_activated(GtkEntry * entry,GtkInspectorVisual * vis)704 slowdown_entry_activated (GtkEntry *entry,
705                           GtkInspectorVisual *vis)
706 {
707   gdouble slowdown;
708   gchar *err = NULL;
709 
710   slowdown = g_strtod (gtk_entry_get_text (entry), &err);
711   if (err != NULL)
712     update_slowdown (vis, slowdown, TRUE, FALSE);
713 }
714 
715 static void
init_slowdown(GtkInspectorVisual * vis)716 init_slowdown (GtkInspectorVisual *vis)
717 {
718   update_slowdown (vis, _gtk_get_slowdown (), TRUE, TRUE);
719   g_signal_connect (vis->priv->slowdown_adjustment, "value-changed",
720                     G_CALLBACK (slowdown_adjustment_changed), vis);
721   g_signal_connect (vis->priv->slowdown_entry, "activate",
722                     G_CALLBACK (slowdown_entry_activated), vis);
723 }
724 
725 static void
update_touchscreen(GtkSwitch * sw)726 update_touchscreen (GtkSwitch *sw)
727 {
728   GtkDebugFlag flags;
729 
730   flags = gtk_get_debug_flags ();
731 
732   if (gtk_switch_get_active (sw))
733     flags |= GTK_DEBUG_TOUCHSCREEN;
734   else
735     flags &= ~GTK_DEBUG_TOUCHSCREEN;
736 
737   gtk_set_debug_flags (flags);
738 }
739 
740 static void
init_touchscreen(GtkInspectorVisual * vis)741 init_touchscreen (GtkInspectorVisual *vis)
742 {
743   gtk_switch_set_active (GTK_SWITCH (vis->priv->touchscreen_switch), (gtk_get_debug_flags () & GTK_DEBUG_TOUCHSCREEN) != 0);
744   g_signal_connect (vis->priv->touchscreen_switch, "notify::active",
745                     G_CALLBACK (update_touchscreen), NULL);
746 
747   if (g_getenv ("GTK_TEST_TOUCHSCREEN") != 0)
748     {
749       /* hardcoded, nothing we can do */
750       gtk_switch_set_active (GTK_SWITCH (vis->priv->touchscreen_switch), TRUE);
751       gtk_widget_set_sensitive (vis->priv->touchscreen_switch, FALSE);
752       gtk_widget_set_tooltip_text (vis->priv->touchscreen_switch, _("Setting is hardcoded by GTK_TEST_TOUCHSCREEN"));
753     }
754 }
755 
756 static gboolean
keynav_failed(GtkWidget * widget,GtkDirectionType direction,GtkInspectorVisual * vis)757 keynav_failed (GtkWidget *widget, GtkDirectionType direction, GtkInspectorVisual *vis)
758 {
759   GtkWidget *next;
760   gdouble value, lower, upper, page;
761 
762   if (direction == GTK_DIR_DOWN &&
763       widget == vis->priv->visual_box)
764     next = vis->priv->debug_box;
765   else if (direction == GTK_DIR_DOWN &&
766       widget == vis->priv->debug_box)
767     next = vis->priv->gl_box;
768   else if (direction == GTK_DIR_UP &&
769            widget == vis->priv->debug_box)
770     next = vis->priv->visual_box;
771   else if (direction == GTK_DIR_UP &&
772            widget == vis->priv->gl_box)
773     next = vis->priv->debug_box;
774   else
775     next = NULL;
776 
777   if (next)
778     {
779       gtk_widget_child_focus (next, direction);
780       return TRUE;
781     }
782 
783   value = gtk_adjustment_get_value (vis->priv->focus_adjustment);
784   lower = gtk_adjustment_get_lower (vis->priv->focus_adjustment);
785   upper = gtk_adjustment_get_upper (vis->priv->focus_adjustment);
786   page  = gtk_adjustment_get_page_size (vis->priv->focus_adjustment);
787 
788   if (direction == GTK_DIR_UP && value > lower)
789     {
790       gtk_adjustment_set_value (vis->priv->focus_adjustment, lower);
791       return TRUE;
792     }
793   else if (direction == GTK_DIR_DOWN && value < upper - page)
794     {
795       gtk_adjustment_set_value (vis->priv->focus_adjustment, upper - page);
796       return TRUE;
797     }
798 
799   return FALSE;
800 }
801 
802 static void
init_gl(GtkInspectorVisual * vis)803 init_gl (GtkInspectorVisual *vis)
804 {
805   GdkGLFlags flags;
806 
807   flags = GDK_PRIVATE_CALL (gdk_gl_get_flags) ();
808 
809   if (flags & GDK_GL_ALWAYS)
810     gtk_combo_box_set_active_id (GTK_COMBO_BOX (vis->priv->gl_combo), "always");
811   else if (flags & GDK_GL_DISABLE)
812     gtk_combo_box_set_active_id (GTK_COMBO_BOX (vis->priv->gl_combo), "disable");
813   else
814     gtk_combo_box_set_active_id (GTK_COMBO_BOX (vis->priv->gl_combo), "maybe");
815   gtk_widget_set_sensitive (vis->priv->gl_combo, FALSE);
816   gtk_widget_set_tooltip_text (vis->priv->gl_combo,
817                                _("Not settable at runtime.\nUse GDK_GL=always or GDK_GL=disable instead"));
818 
819   gtk_switch_set_active (GTK_SWITCH (vis->priv->software_gl_switch),
820                          flags & GDK_GL_SOFTWARE_DRAW_GL);
821   gtk_switch_set_active (GTK_SWITCH (vis->priv->software_surface_switch),
822                          flags & GDK_GL_SOFTWARE_DRAW_SURFACE);
823   gtk_switch_set_active (GTK_SWITCH (vis->priv->texture_rectangle_switch),
824                          flags & GDK_GL_TEXTURE_RECTANGLE);
825 
826   if (flags & GDK_GL_DISABLE)
827     {
828       gtk_widget_set_sensitive (vis->priv->software_gl_switch, FALSE);
829       gtk_widget_set_sensitive (vis->priv->software_surface_switch, FALSE);
830       gtk_widget_set_sensitive (vis->priv->texture_rectangle_switch, FALSE);
831       gtk_widget_set_tooltip_text (vis->priv->software_gl_switch, _("GL rendering is disabled"));
832       gtk_widget_set_tooltip_text (vis->priv->software_surface_switch, _("GL rendering is disabled"));
833       gtk_widget_set_tooltip_text (vis->priv->texture_rectangle_switch, _("GL rendering is disabled"));
834     }
835 }
836 
837 static void
init_rendering_mode(GtkInspectorVisual * vis)838 init_rendering_mode (GtkInspectorVisual *vis)
839 {
840   GdkRenderingMode mode;
841 
842   mode = GDK_PRIVATE_CALL (gdk_display_get_rendering_mode) (gdk_display_get_default ());
843   gtk_combo_box_set_active (GTK_COMBO_BOX (vis->priv->rendering_mode_combo), mode);
844 }
845 
846 static void
rendering_mode_changed(GtkComboBox * c,GtkInspectorVisual * vis)847 rendering_mode_changed (GtkComboBox        *c,
848                         GtkInspectorVisual *vis)
849 {
850   GdkRenderingMode mode;
851 
852   mode = gtk_combo_box_get_active (c);
853   GDK_PRIVATE_CALL (gdk_display_set_rendering_mode) (gdk_display_get_default (), mode);
854 }
855 
856 static void
update_gl_flag(GtkSwitch * sw,GdkGLFlags flag)857 update_gl_flag (GtkSwitch  *sw,
858                 GdkGLFlags  flag)
859 {
860   GdkGLFlags flags;
861 
862   flags = GDK_PRIVATE_CALL (gdk_gl_get_flags) ();
863 
864   if (gtk_switch_get_active (sw))
865     flags |= flag;
866   else
867     flags &= ~flag;
868 
869   GDK_PRIVATE_CALL (gdk_gl_set_flags) (flags);
870 }
871 
872 static void
software_gl_activate(GtkSwitch * sw)873 software_gl_activate (GtkSwitch *sw)
874 {
875   update_gl_flag (sw, GDK_GL_SOFTWARE_DRAW_GL);
876 }
877 
878 static void
software_surface_activate(GtkSwitch * sw)879 software_surface_activate (GtkSwitch *sw)
880 {
881   update_gl_flag (sw, GDK_GL_SOFTWARE_DRAW_SURFACE);
882 }
883 
884 static void
texture_rectangle_activate(GtkSwitch * sw)885 texture_rectangle_activate (GtkSwitch *sw)
886 {
887   update_gl_flag (sw, GDK_GL_TEXTURE_RECTANGLE);
888 }
889 
890 static void
gtk_inspector_visual_init(GtkInspectorVisual * vis)891 gtk_inspector_visual_init (GtkInspectorVisual *vis)
892 {
893   vis->priv = gtk_inspector_visual_get_instance_private (vis);
894   gtk_widget_init_template (GTK_WIDGET (vis));
895   init_direction (vis);
896   init_theme (vis);
897   init_dark (vis);
898   init_icons (vis);
899   init_cursors (vis);
900   init_cursor_size (vis);
901   init_font (vis);
902   init_font_scale (vis);
903   init_scale (vis);
904   init_rendering_mode (vis);
905   init_updates (vis);
906   init_animation (vis);
907   init_slowdown (vis);
908   init_touchscreen (vis);
909   init_gl (vis);
910 }
911 
912 static void
gtk_inspector_visual_constructed(GObject * object)913 gtk_inspector_visual_constructed (GObject *object)
914 {
915   GtkInspectorVisual *vis = GTK_INSPECTOR_VISUAL (object);
916 
917   G_OBJECT_CLASS (gtk_inspector_visual_parent_class)->constructed (object);
918 
919   vis->priv->focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (vis));
920   gtk_container_set_focus_vadjustment (GTK_CONTAINER (gtk_bin_get_child (GTK_BIN (vis))),
921                                        vis->priv->focus_adjustment);
922 
923    g_signal_connect (vis->priv->visual_box, "keynav-failed", G_CALLBACK (keynav_failed), vis);
924    g_signal_connect (vis->priv->debug_box, "keynav-failed", G_CALLBACK (keynav_failed), vis);
925    g_signal_connect (vis->priv->gl_box, "keynav-failed", G_CALLBACK (keynav_failed), vis);
926 }
927 
928 static void
gtk_inspector_visual_class_init(GtkInspectorVisualClass * klass)929 gtk_inspector_visual_class_init (GtkInspectorVisualClass *klass)
930 {
931   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
932   GObjectClass *object_class = G_OBJECT_CLASS (klass);
933 
934   object_class->constructed = gtk_inspector_visual_constructed;
935 
936   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/visual.ui");
937   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, rendering_mode_combo);
938   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, updates_switch);
939   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, direction_combo);
940   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, baselines_switch);
941   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, layout_switch);
942   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, theme_combo);
943   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, dark_switch);
944   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, cursor_combo);
945   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, cursor_size_spin);
946   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, cursor_size_adjustment);
947   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, icon_combo);
948   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, hidpi_spin);
949   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, scale_adjustment);
950   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, animation_switch);
951   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, slowdown_adjustment);
952   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, slowdown_entry);
953   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, touchscreen_switch);
954   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, visual_box);
955   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, debug_box);
956   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_button);
957   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, gl_box);
958   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, gl_combo);
959   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, software_gl_switch);
960   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, software_surface_switch);
961   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, texture_rectangle_switch);
962   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_entry);
963   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorVisual, font_scale_adjustment);
964 
965   gtk_widget_class_bind_template_callback (widget_class, updates_activate);
966   gtk_widget_class_bind_template_callback (widget_class, direction_changed);
967   gtk_widget_class_bind_template_callback (widget_class, rendering_mode_changed);
968   gtk_widget_class_bind_template_callback (widget_class, baselines_activate);
969   gtk_widget_class_bind_template_callback (widget_class, layout_activate);
970   gtk_widget_class_bind_template_callback (widget_class, pixelcache_activate);
971   gtk_widget_class_bind_template_callback (widget_class, widget_resize_activate);
972   gtk_widget_class_bind_template_callback (widget_class, software_gl_activate);
973   gtk_widget_class_bind_template_callback (widget_class, software_surface_activate);
974   gtk_widget_class_bind_template_callback (widget_class, texture_rectangle_activate);
975 }
976 
977 // vim: set et sw=2 ts=2:
978