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 "general.h"
22 
23 #include "gtkdebug.h"
24 #include "gtklabel.h"
25 #include "gtkscale.h"
26 #include "gtkswitch.h"
27 #include "gtklistbox.h"
28 #include "gtkprivate.h"
29 #include "gtksizegroup.h"
30 #include "gtkimage.h"
31 #include "gtkadjustment.h"
32 #include "gtkbox.h"
33 
34 #ifdef GDK_WINDOWING_X11
35 #include "x11/gdkx.h"
36 #include <epoxy/glx.h>
37 #endif
38 
39 #ifdef GDK_WINDOWING_WIN32
40 #include "win32/gdkwin32.h"
41 #endif
42 
43 #ifdef GDK_WINDOWING_QUARTZ
44 #include "quartz/gdkquartz.h"
45 #endif
46 
47 #ifdef GDK_WINDOWING_WAYLAND
48 #include "wayland/gdkwayland.h"
49 #include <epoxy/egl.h>
50 #endif
51 
52 #ifdef GDK_WINDOWING_BROADWAY
53 #include "broadway/gdkbroadway.h"
54 #endif
55 
56 struct _GtkInspectorGeneralPrivate
57 {
58   GtkWidget *version_box;
59   GtkWidget *env_box;
60   GtkWidget *display_box;
61   GtkWidget *gl_box;
62   GtkWidget *device_box;
63   GtkWidget *gtk_version;
64   GtkWidget *gdk_backend;
65   GtkWidget *gl_version;
66   GtkWidget *gl_vendor;
67   GtkWidget *prefix;
68   GtkWidget *xdg_data_home;
69   GtkWidget *xdg_data_dirs;
70   GtkWidget *gtk_path;
71   GtkWidget *gtk_exe_prefix;
72   GtkWidget *gtk_data_prefix;
73   GtkWidget *gsettings_schema_dir;
74   GtkWidget *display_name;
75   GtkWidget *display_rgba;
76   GtkWidget *display_composited;
77   GtkSizeGroup *labels;
78   GtkAdjustment *focus_adjustment;
79 };
80 
G_DEFINE_TYPE_WITH_PRIVATE(GtkInspectorGeneral,gtk_inspector_general,GTK_TYPE_SCROLLED_WINDOW)81 G_DEFINE_TYPE_WITH_PRIVATE (GtkInspectorGeneral, gtk_inspector_general, GTK_TYPE_SCROLLED_WINDOW)
82 
83 static void
84 init_version (GtkInspectorGeneral *gen)
85 {
86   const gchar *backend;
87   GdkDisplay *display;
88 
89   display = gdk_display_get_default ();
90 
91 #ifdef GDK_WINDOWING_X11
92   if (GDK_IS_X11_DISPLAY (display))
93     backend = "X11";
94   else
95 #endif
96 #ifdef GDK_WINDOWING_WAYLAND
97   if (GDK_IS_WAYLAND_DISPLAY (display))
98     backend = "Wayland";
99   else
100 #endif
101 #ifdef GDK_WINDOWING_BROADWAY
102   if (GDK_IS_BROADWAY_DISPLAY (display))
103     backend = "Broadway";
104   else
105 #endif
106 #ifdef GDK_WINDOWING_WIN32
107   if (GDK_IS_WIN32_DISPLAY (display))
108     backend = "Windows";
109   else
110 #endif
111 #ifdef GDK_WINDOWING_QUARTZ
112   if (GDK_IS_QUARTZ_DISPLAY (display))
113     backend = "Quartz";
114   else
115 #endif
116     backend = "Unknown";
117 
118   gtk_label_set_text (GTK_LABEL (gen->priv->gtk_version), GTK_VERSION);
119   gtk_label_set_text (GTK_LABEL (gen->priv->gdk_backend), backend);
120 }
121 
122 static G_GNUC_UNUSED void
add_check_row(GtkInspectorGeneral * gen,GtkListBox * list,const gchar * name,gboolean value,gint indent)123 add_check_row (GtkInspectorGeneral *gen,
124                GtkListBox          *list,
125                const gchar         *name,
126                gboolean             value,
127                gint                 indent)
128 {
129   GtkWidget *row, *box, *label, *check;
130 
131   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40);
132   g_object_set (box,
133                 "margin", 10,
134                 "margin-start", 10 + indent,
135                 NULL);
136 
137   label = gtk_label_new (name);
138   gtk_widget_set_halign (label, GTK_ALIGN_START);
139   gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
140   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
141   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
142 
143   check = gtk_image_new_from_icon_name ("object-select-symbolic", GTK_ICON_SIZE_MENU);
144   gtk_widget_set_halign (check, GTK_ALIGN_END);
145   gtk_widget_set_valign (check, GTK_ALIGN_BASELINE);
146   gtk_widget_set_opacity (check, value ? 1.0 : 0.0);
147   gtk_box_pack_start (GTK_BOX (box), check, TRUE, TRUE, 0);
148 
149   row = gtk_list_box_row_new ();
150   gtk_container_add (GTK_CONTAINER (row), box);
151   gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
152   gtk_widget_show_all (row);
153 
154   gtk_list_box_insert (list, row, -1);
155 
156   gtk_size_group_add_widget (GTK_SIZE_GROUP (gen->priv->labels), label);
157 }
158 
159 static void
add_label_row(GtkInspectorGeneral * gen,GtkListBox * list,const char * name,const char * value,gint indent)160 add_label_row (GtkInspectorGeneral *gen,
161                GtkListBox          *list,
162                const char          *name,
163                const char          *value,
164                gint                 indent)
165 {
166   GtkWidget *box;
167   GtkWidget *label;
168   GtkWidget *row;
169 
170   box = gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 40);
171   g_object_set (box,
172                 "margin", 10,
173                 "margin-start", 10 + indent,
174                 NULL);
175 
176   label = gtk_label_new (name);
177   gtk_widget_set_halign (label, GTK_ALIGN_START);
178   gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
179   gtk_label_set_xalign (GTK_LABEL (label), 0.0);
180   gtk_box_pack_start (GTK_BOX (box), label, FALSE, FALSE, 0);
181 
182   label = gtk_label_new (value);
183   gtk_label_set_selectable (GTK_LABEL (label), TRUE);
184   gtk_widget_set_halign (label, GTK_ALIGN_END);
185   gtk_widget_set_valign (label, GTK_ALIGN_BASELINE);
186   gtk_label_set_xalign (GTK_LABEL (label), 1.0);
187   gtk_box_pack_start (GTK_BOX (box), label, TRUE, TRUE, 0);
188 
189   row = gtk_list_box_row_new ();
190   gtk_container_add (GTK_CONTAINER (row), box);
191   gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
192   gtk_widget_show_all (row);
193 
194   gtk_list_box_insert (GTK_LIST_BOX (list), row, -1);
195 
196   gtk_size_group_add_widget (GTK_SIZE_GROUP (gen->priv->labels), label);
197 }
198 
199 #ifdef GDK_WINDOWING_X11
200 static void
append_glx_extension_row(GtkInspectorGeneral * gen,Display * dpy,const gchar * ext)201 append_glx_extension_row (GtkInspectorGeneral *gen,
202                           Display             *dpy,
203                           const gchar         *ext)
204 {
205   add_check_row (gen, GTK_LIST_BOX (gen->priv->gl_box), ext, epoxy_has_glx_extension (dpy, 0, ext), 0);
206 }
207 #endif
208 
209 #ifdef GDK_WINDOWING_WAYLAND
210 static void
append_egl_extension_row(GtkInspectorGeneral * gen,EGLDisplay dpy,const gchar * ext)211 append_egl_extension_row (GtkInspectorGeneral *gen,
212                           EGLDisplay          dpy,
213                           const gchar         *ext)
214 {
215   add_check_row (gen, GTK_LIST_BOX (gen->priv->gl_box), ext, epoxy_has_egl_extension (dpy, ext), 0);
216 }
217 
218 static EGLDisplay
wayland_get_display(struct wl_display * wl_display)219 wayland_get_display (struct wl_display *wl_display)
220 {
221   EGLDisplay dpy = NULL;
222 
223   if (epoxy_has_egl_extension (NULL, "EGL_KHR_platform_base"))
224     {
225       PFNEGLGETPLATFORMDISPLAYPROC getPlatformDisplay =
226         (void *) eglGetProcAddress ("eglGetPlatformDisplay");
227 
228       if (getPlatformDisplay)
229         dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
230                                   wl_display,
231                                   NULL);
232       if (dpy)
233         return dpy;
234     }
235 
236   if (epoxy_has_egl_extension (NULL, "EGL_EXT_platform_base"))
237     {
238       PFNEGLGETPLATFORMDISPLAYEXTPROC getPlatformDisplay =
239         (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT");
240 
241       if (getPlatformDisplay)
242         dpy = getPlatformDisplay (EGL_PLATFORM_WAYLAND_EXT,
243                                   wl_display,
244                                   NULL);
245       if (dpy)
246         return dpy;
247     }
248 
249   return eglGetDisplay ((EGLNativeDisplayType)wl_display);
250 }
251 #endif
252 
253 
254 static void
init_gl(GtkInspectorGeneral * gen)255 init_gl (GtkInspectorGeneral *gen)
256 {
257 #ifdef GDK_WINDOWING_X11
258   if (GDK_IS_X11_DISPLAY (gdk_display_get_default ()))
259     {
260       GdkDisplay *display = gdk_display_get_default ();
261       Display *dpy = GDK_DISPLAY_XDISPLAY (display);
262       int error_base, event_base;
263       gchar *version;
264       if (!glXQueryExtension (dpy, &error_base, &event_base))
265         return;
266 
267       version = g_strconcat ("GLX ", glXGetClientString (dpy, GLX_VERSION), NULL);
268       gtk_label_set_text (GTK_LABEL (gen->priv->gl_version), version);
269       g_free (version);
270       gtk_label_set_text (GTK_LABEL (gen->priv->gl_vendor), glXGetClientString (dpy, GLX_VENDOR));
271 
272       append_glx_extension_row (gen, dpy, "GLX_ARB_create_context_profile");
273       append_glx_extension_row (gen, dpy, "GLX_SGI_swap_control");
274       append_glx_extension_row (gen, dpy, "GLX_EXT_texture_from_pixmap");
275       append_glx_extension_row (gen, dpy, "GLX_SGI_video_sync");
276       append_glx_extension_row (gen, dpy, "GLX_EXT_buffer_age");
277       append_glx_extension_row (gen, dpy, "GLX_OML_sync_control");
278       append_glx_extension_row (gen, dpy, "GLX_ARB_multisample");
279       append_glx_extension_row (gen, dpy, "GLX_EXT_visual_rating");
280     }
281   else
282 #endif
283 #ifdef GDK_WINDOWING_WAYLAND
284   if (GDK_IS_WAYLAND_DISPLAY (gdk_display_get_default ()))
285     {
286       GdkDisplay *display = gdk_display_get_default ();
287       EGLDisplay dpy;
288       EGLint major, minor;
289       gchar *version;
290 
291       dpy = wayland_get_display (gdk_wayland_display_get_wl_display (display));
292 
293       if (!eglInitialize (dpy, &major, &minor))
294         return;
295 
296       version = g_strconcat ("EGL ", eglQueryString (dpy, EGL_VERSION), NULL);
297       gtk_label_set_text (GTK_LABEL (gen->priv->gl_version), version);
298       g_free (version);
299       gtk_label_set_text (GTK_LABEL (gen->priv->gl_vendor), eglQueryString (dpy, EGL_VENDOR));
300 
301       append_egl_extension_row (gen, dpy, "EGL_KHR_create_context");
302       append_egl_extension_row (gen, dpy, "EGL_EXT_buffer_age");
303       append_egl_extension_row (gen, dpy, "EGL_EXT_swap_buffers_with_damage");
304       append_egl_extension_row (gen, dpy, "EGL_KHR_surfaceless_context");
305     }
306   else
307 #endif
308     {
309       gtk_label_set_text (GTK_LABEL (gen->priv->gl_version), C_("GL version", "None"));
310       gtk_label_set_text (GTK_LABEL (gen->priv->gl_vendor), C_("GL vendor", "None"));
311     }
312 }
313 
314 static void
set_monospace_font(GtkWidget * w)315 set_monospace_font (GtkWidget *w)
316 {
317   PangoAttrList *attrs;
318 
319   attrs = pango_attr_list_new ();
320   pango_attr_list_insert (attrs, pango_attr_fallback_new (FALSE));
321   pango_attr_list_insert (attrs, pango_attr_family_new ("Monospace"));
322   gtk_label_set_attributes (GTK_LABEL (w), attrs);
323   pango_attr_list_unref (attrs);
324 }
325 
326 static void
set_path_label(GtkWidget * w,const gchar * var)327 set_path_label (GtkWidget   *w,
328                 const gchar *var)
329 {
330   const gchar *v;
331 
332   v = g_getenv (var);
333   if (v != NULL)
334     {
335       set_monospace_font (w);
336       gtk_label_set_text (GTK_LABEL (w), v);
337     }
338   else
339     {
340        GtkWidget *r;
341        r = gtk_widget_get_ancestor (w, GTK_TYPE_LIST_BOX_ROW);
342        gtk_widget_hide (r);
343     }
344 }
345 
346 static void
init_env(GtkInspectorGeneral * gen)347 init_env (GtkInspectorGeneral *gen)
348 {
349   set_monospace_font (gen->priv->prefix);
350   gtk_label_set_text (GTK_LABEL (gen->priv->prefix), _gtk_get_data_prefix ());
351   set_path_label (gen->priv->xdg_data_home, "XDG_DATA_HOME");
352   set_path_label (gen->priv->xdg_data_dirs, "XDG_DATA_DIRS");
353   set_path_label (gen->priv->gtk_path, "GTK_PATH");
354   set_path_label (gen->priv->gtk_exe_prefix, "GTK_EXE_PREFIX");
355   set_path_label (gen->priv->gtk_data_prefix, "GTK_DATA_PREFIX");
356   set_path_label (gen->priv->gsettings_schema_dir, "GSETTINGS_SCHEMA_DIR");
357 }
358 
359 static const char *
translate_subpixel_layout(GdkSubpixelLayout subpixel)360 translate_subpixel_layout (GdkSubpixelLayout subpixel)
361 {
362   switch (subpixel)
363     {
364     case GDK_SUBPIXEL_LAYOUT_NONE: return "none";
365     case GDK_SUBPIXEL_LAYOUT_UNKNOWN: return "unknown";
366     case GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB: return "horizontal rgb";
367     case GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR: return "horizontal bgr";
368     case GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB: return "vertical rgb";
369     case GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR: return "vertical bgr";
370     default: g_assert_not_reached ();
371     }
372 }
373 
374 static void
populate_display(GdkScreen * screen,GtkInspectorGeneral * gen)375 populate_display (GdkScreen *screen, GtkInspectorGeneral *gen)
376 {
377   gchar *name;
378   gint i;
379   GList *children, *l;
380   GtkWidget *child;
381   GdkDisplay *display;
382   int n_monitors;
383   GtkListBox *list;
384 
385   list = GTK_LIST_BOX (gen->priv->display_box);
386   children = gtk_container_get_children (GTK_CONTAINER (list));
387   for (l = children; l; l = l->next)
388     {
389       child = l->data;
390       if (gtk_widget_is_ancestor (gen->priv->display_name, child) ||
391           gtk_widget_is_ancestor (gen->priv->display_rgba, child) ||
392           gtk_widget_is_ancestor (gen->priv->display_composited, child))
393         continue;
394 
395       gtk_widget_destroy (child);
396     }
397   g_list_free (children);
398 
399 G_GNUC_BEGIN_IGNORE_DEPRECATIONS
400   name = gdk_screen_make_display_name (screen);
401 G_GNUC_END_IGNORE_DEPRECATIONS
402   gtk_label_set_label (GTK_LABEL (gen->priv->display_name), name);
403   g_free (name);
404 
405   if (gdk_screen_get_rgba_visual (screen) != NULL)
406     gtk_widget_show (gen->priv->display_rgba);
407 
408   if (gdk_screen_is_composited (screen))
409     gtk_widget_show (gen->priv->display_composited);
410 
411   display = gdk_screen_get_display (screen);
412   n_monitors = gdk_display_get_n_monitors (display);
413   for (i = 0; i < n_monitors; i++)
414     {
415       gchar *name;
416       gchar *value;
417       GdkRectangle rect;
418       gint scale;
419       const char *manufacturer;
420       const char *model;
421       GdkMonitor *monitor;
422 
423       monitor = gdk_display_get_monitor (display, i);
424 
425       name = g_strdup_printf ("Monitor %d", i);
426       manufacturer = gdk_monitor_get_manufacturer (monitor);
427       model = gdk_monitor_get_model (monitor);
428       value = g_strdup_printf ("%s%s%s",
429                                manufacturer ? manufacturer : "",
430                                manufacturer || model ? " " : "",
431                                model ? model : "");
432       add_label_row (gen, list, name, value, 0);
433       g_free (name);
434       g_free (value);
435 
436       gdk_monitor_get_geometry (monitor, &rect);
437       scale = gdk_monitor_get_scale_factor (monitor);
438 
439       value = g_strdup_printf ("%d × %d%s at %d, %d",
440                                rect.width, rect.height,
441                                scale == 2 ? " @ 2" : "",
442                                rect.x, rect.y);
443       add_label_row (gen, list, "Geometry", value, 10);
444       g_free (value);
445 
446       value = g_strdup_printf ("%d × %d mm²",
447                                gdk_monitor_get_width_mm (monitor),
448                                gdk_monitor_get_height_mm (monitor));
449       add_label_row (gen, list, "Size", value, 10);
450       g_free (value);
451 
452       add_check_row (gen, list, "Primary", gdk_monitor_is_primary (monitor), 10);
453 
454       if (gdk_monitor_get_refresh_rate (monitor) != 0)
455         value = g_strdup_printf ("%.2f Hz",
456                                  0.001 * gdk_monitor_get_refresh_rate (monitor));
457       else
458         value = g_strdup ("unknown");
459       add_label_row (gen, list, "Refresh rate", value, 10);
460       g_free (value);
461 
462       value = g_strdup (translate_subpixel_layout (gdk_monitor_get_subpixel_layout (monitor)));
463       add_label_row (gen, list, "Subpixel layout", value, 10);
464       g_free (value);
465     }
466 }
467 
468 static void
init_display(GtkInspectorGeneral * gen)469 init_display (GtkInspectorGeneral *gen)
470 {
471   GdkScreen *screen;
472 
473   screen = gdk_screen_get_default ();
474 
475   g_signal_connect (screen, "size-changed", G_CALLBACK (populate_display), gen);
476   g_signal_connect (screen, "composited-changed", G_CALLBACK (populate_display), gen);
477   g_signal_connect (screen, "monitors-changed", G_CALLBACK (populate_display), gen);
478 
479   populate_display (screen, gen);
480 }
481 
482 static void populate_seats (GtkInspectorGeneral *gen);
483 
484 static void
add_device(GtkInspectorGeneral * gen,GdkDevice * device)485 add_device (GtkInspectorGeneral *gen,
486             GdkDevice           *device)
487 {
488   const gchar *name, *value;
489   GString *str;
490   int i;
491   guint n_touches;
492   gchar *text;
493   GdkAxisFlags axes;
494   const char *axis_name[] = {
495     "Ignore",
496     "X",
497     "Y",
498     "Pressure",
499     "X Tilt",
500     "Y Tilt",
501     "Wheel",
502     "Distance",
503     "Rotation",
504     "Slider"
505   };
506   const char *source_name[] = {
507     "Mouse",
508     "Pen",
509     "Eraser",
510     "Cursor",
511     "Keyboard",
512     "Touchscreen",
513     "Touchpad",
514     "Trackpoint",
515     "Pad"
516   };
517 
518   name = gdk_device_get_name (device);
519   value = source_name[gdk_device_get_source (device)];
520   add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), name, value, 10);
521 
522   str = g_string_new ("");
523 
524   axes = gdk_device_get_axes (device);
525   for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
526     {
527       if ((axes & (1 << i)) != 0)
528         {
529           if (str->len > 0)
530             g_string_append (str, ", ");
531           g_string_append (str, axis_name[i]);
532         }
533     }
534 
535   if (str->len > 0)
536     add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), "Axes", str->str, 20);
537 
538   g_string_free (str, TRUE);
539 
540   g_object_get (device, "num-touches", &n_touches, NULL);
541   if (n_touches > 0)
542     {
543       text = g_strdup_printf ("%d", n_touches);
544       add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), "Touches", text, 20);
545       g_free (text);
546     }
547 }
548 
549 static char *
get_seat_capabilities(GdkSeat * seat)550 get_seat_capabilities (GdkSeat *seat)
551 {
552   struct {
553     GdkSeatCapabilities cap;
554     const char *name;
555   } caps[] = {
556     { GDK_SEAT_CAPABILITY_POINTER,       "Pointer" },
557     { GDK_SEAT_CAPABILITY_TOUCH,         "Touch" },
558     { GDK_SEAT_CAPABILITY_TABLET_STYLUS, "Tablet" },
559     { GDK_SEAT_CAPABILITY_KEYBOARD,      "Keyboard" },
560     { 0, NULL }
561   };
562   GString *str;
563   GdkSeatCapabilities capabilities;
564   int i;
565 
566   str = g_string_new ("");
567   capabilities = gdk_seat_get_capabilities (seat);
568   for (i = 0; caps[i].cap != 0; i++)
569     {
570       if (capabilities & caps[i].cap)
571         {
572           if (str->len > 0)
573             g_string_append (str, ", ");
574           g_string_append (str, caps[i].name);
575         }
576     }
577 
578   return g_string_free (str, FALSE);
579 }
580 
581 static void
add_seat(GtkInspectorGeneral * gen,GdkSeat * seat,int num)582 add_seat (GtkInspectorGeneral *gen,
583           GdkSeat             *seat,
584           int                  num)
585 {
586   char *text;
587   char *caps;
588   GList *list, *l;
589 
590   if (!g_object_get_data (G_OBJECT (seat), "inspector-connected"))
591     {
592       g_object_set_data (G_OBJECT (seat), "inspector-connected", GINT_TO_POINTER (1));
593       g_signal_connect_swapped (seat, "device-added", G_CALLBACK (populate_seats), gen);
594       g_signal_connect_swapped (seat, "device-removed", G_CALLBACK (populate_seats), gen);
595     }
596 
597   text = g_strdup_printf ("Seat %d", num);
598   caps = get_seat_capabilities (seat);
599 
600   add_label_row (gen, GTK_LIST_BOX (gen->priv->device_box), text, caps, 0);
601   g_free (text);
602   g_free (caps);
603 
604   list = gdk_seat_get_slaves (seat, GDK_SEAT_CAPABILITY_ALL);
605 
606   for (l = list; l; l = l->next)
607     add_device (gen, GDK_DEVICE (l->data));
608 
609   g_list_free (list);
610 }
611 
612 static void
populate_seats(GtkInspectorGeneral * gen)613 populate_seats (GtkInspectorGeneral *gen)
614 {
615   GdkDisplay *display = gdk_display_get_default ();
616   GList *list, *l;
617   int i;
618 
619   list = gtk_container_get_children (GTK_CONTAINER (gen->priv->device_box));
620   for (l = list; l; l = l->next)
621     gtk_widget_destroy (GTK_WIDGET (l->data));
622   g_list_free (list);
623 
624   list = gdk_display_list_seats (display);
625 
626   for (l = list, i = 0; l; l = l->next, i++)
627     add_seat (gen, GDK_SEAT (l->data), i);
628 
629   g_list_free (list);
630 }
631 
632 static void
init_device(GtkInspectorGeneral * gen)633 init_device (GtkInspectorGeneral *gen)
634 {
635   GdkDisplay *display = gdk_display_get_default ();
636 
637   g_signal_connect_swapped (display, "seat-added", G_CALLBACK (populate_seats), gen);
638   g_signal_connect_swapped (display, "seat-removed", G_CALLBACK (populate_seats), gen);
639 
640   populate_seats (gen);
641 }
642 
643 static void
gtk_inspector_general_init(GtkInspectorGeneral * gen)644 gtk_inspector_general_init (GtkInspectorGeneral *gen)
645 {
646   gen->priv = gtk_inspector_general_get_instance_private (gen);
647   gtk_widget_init_template (GTK_WIDGET (gen));
648   init_version (gen);
649   init_env (gen);
650   init_display (gen);
651   init_gl (gen);
652   init_device (gen);
653 }
654 
655 static gboolean
keynav_failed(GtkWidget * widget,GtkDirectionType direction,GtkInspectorGeneral * gen)656 keynav_failed (GtkWidget *widget, GtkDirectionType direction, GtkInspectorGeneral *gen)
657 {
658   GtkWidget *next;
659   gdouble value, lower, upper, page;
660 
661   if (direction == GTK_DIR_DOWN && widget == gen->priv->version_box)
662     next = gen->priv->env_box;
663   else if (direction == GTK_DIR_DOWN && widget == gen->priv->env_box)
664     next = gen->priv->display_box;
665   else if (direction == GTK_DIR_DOWN && widget == gen->priv->display_box)
666     next = gen->priv->gl_box;
667   else if (direction == GTK_DIR_DOWN && widget == gen->priv->gl_box)
668     next = gen->priv->device_box;
669   else if (direction == GTK_DIR_UP && widget == gen->priv->device_box)
670     next = gen->priv->gl_box;
671   else if (direction == GTK_DIR_UP && widget == gen->priv->gl_box)
672     next = gen->priv->display_box;
673   else if (direction == GTK_DIR_UP && widget == gen->priv->display_box)
674     next = gen->priv->env_box;
675   else if (direction == GTK_DIR_UP && widget == gen->priv->env_box)
676     next = gen->priv->version_box;
677   else
678     next = NULL;
679 
680   if (next)
681     {
682       gtk_widget_child_focus (next, direction);
683       return TRUE;
684     }
685 
686   value = gtk_adjustment_get_value (gen->priv->focus_adjustment);
687   lower = gtk_adjustment_get_lower (gen->priv->focus_adjustment);
688   upper = gtk_adjustment_get_upper (gen->priv->focus_adjustment);
689   page  = gtk_adjustment_get_page_size (gen->priv->focus_adjustment);
690 
691   if (direction == GTK_DIR_UP && value > lower)
692     {
693       gtk_adjustment_set_value (gen->priv->focus_adjustment, lower);
694       return TRUE;
695     }
696   else if (direction == GTK_DIR_DOWN && value < upper - page)
697     {
698       gtk_adjustment_set_value (gen->priv->focus_adjustment, upper - page);
699       return TRUE;
700     }
701 
702   return FALSE;
703 }
704 
705 static void
gtk_inspector_general_constructed(GObject * object)706 gtk_inspector_general_constructed (GObject *object)
707 {
708   GtkInspectorGeneral *gen = GTK_INSPECTOR_GENERAL (object);
709 
710   G_OBJECT_CLASS (gtk_inspector_general_parent_class)->constructed (object);
711 
712   gen->priv->focus_adjustment = gtk_scrolled_window_get_vadjustment (GTK_SCROLLED_WINDOW (gen));
713   gtk_container_set_focus_vadjustment (GTK_CONTAINER (gtk_bin_get_child (GTK_BIN (gen))),
714                                        gen->priv->focus_adjustment);
715 
716    g_signal_connect (gen->priv->version_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
717    g_signal_connect (gen->priv->env_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
718    g_signal_connect (gen->priv->display_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
719    g_signal_connect (gen->priv->gl_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
720    g_signal_connect (gen->priv->device_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
721 }
722 
723 static void
gtk_inspector_general_class_init(GtkInspectorGeneralClass * klass)724 gtk_inspector_general_class_init (GtkInspectorGeneralClass *klass)
725 {
726   GObjectClass *object_class = G_OBJECT_CLASS (klass);
727   GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
728 
729   object_class->constructed = gtk_inspector_general_constructed;
730 
731   gtk_widget_class_set_template_from_resource (widget_class, "/org/gtk/libgtk/inspector/general.ui");
732   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, version_box);
733   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, env_box);
734   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, display_box);
735   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gl_box);
736   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gtk_version);
737   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gdk_backend);
738   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gl_version);
739   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gl_vendor);
740   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, prefix);
741   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, xdg_data_home);
742   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, xdg_data_dirs);
743   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gtk_path);
744   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gtk_exe_prefix);
745   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gtk_data_prefix);
746   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, gsettings_schema_dir);
747   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, labels);
748   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, display_name);
749   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, display_composited);
750   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, display_rgba);
751   gtk_widget_class_bind_template_child_private (widget_class, GtkInspectorGeneral, device_box);
752 }
753 
754 // vim: set et sw=2 ts=2:
755