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#include "window.h"
23
24#include "gtkdebug.h"
25#include "gtklabel.h"
26#include "gtkscale.h"
27#include "gtkswitch.h"
28#include "gtklistbox.h"
29#include "gtkprivate.h"
30#include "gtksizegroup.h"
31#include "gtkimage.h"
32#include "gtkadjustment.h"
33#include "gtkbox.h"
34#include "gtkbinlayout.h"
35#include "gtkmediafileprivate.h"
36#include "gtkimmoduleprivate.h"
37
38#include "gdk/gdkdebug.h"
39
40#ifdef GDK_WINDOWING_X11
41#include "x11/gdkx.h"
42#include <epoxy/glx.h>
43#include <epoxy/egl.h>
44#endif
45
46#ifdef GDK_WINDOWING_WIN32
47#include "win32/gdkwin32.h"
48#include "gdkglcontextprivate.h"
49#include <epoxy/wgl.h>
50#ifdef GDK_WIN32_ENABLE_EGL
51#include <epoxy/egl.h>
52#endif
53#endif
54
55#ifdef GDK_WINDOWING_MACOS
56#include "macos/gdkmacos.h"
57#endif
58
59#ifdef GDK_WINDOWING_WAYLAND
60#include "wayland/gdkwayland.h"
61#include <epoxy/egl.h>
62#include <xkbcommon/xkbcommon.h>
63#endif
64
65#ifdef GDK_WINDOWING_BROADWAY
66#include "broadway/gdkbroadway.h"
67#endif
68
69#ifdef GDK_RENDERING_VULKAN
70#include <vulkan/vulkan.h>
71#endif
72
73struct _GtkInspectorGeneral
74{
75 GtkWidget parent;
76
77 GtkWidget *swin;
78 GtkWidget *box;
79 GtkWidget *version_box;
80 GtkWidget *env_box;
81 GtkWidget *display_box;
82 GtkWidget *monitor_box;
83 GtkWidget *gl_box;
84 GtkWidget *vulkan_box;
85 GtkWidget *device_box;
86 GtkWidget *gtk_version;
87 GtkWidget *gdk_backend;
88 GtkWidget *gsk_renderer;
89 GtkWidget *pango_fontmap;
90 GtkWidget *media_backend;
91 GtkWidget *im_module;
92 GtkWidget *gl_version;
93 GtkWidget *gl_error;
94 GtkWidget *gl_error_row;
95 GtkWidget *gl_vendor;
96 GtkWidget *gl_vendor_row;
97 GtkWidget *vk_device;
98 GtkWidget *vk_api_version;
99 GtkWidget *vk_driver_version;
100 GtkWidget *app_id_frame;
101 GtkWidget *app_id;
102 GtkWidget *resource_path;
103 GtkWidget *prefix;
104 GtkWidget *xdg_data_home;
105 GtkWidget *xdg_data_dirs;
106 GtkWidget *gtk_path;
107 GtkWidget *gtk_exe_prefix;
108 GtkWidget *gtk_data_prefix;
109 GtkWidget *gsettings_schema_dir;
110 GtkWidget *display_name;
111 GtkWidget *display_rgba;
112 GtkWidget *display_composited;
113 GtkSizeGroup *labels;
114
115 GdkDisplay *display;
116};
117
118typedef struct _GtkInspectorGeneralClass
119{
120 GtkWidgetClass parent_class;
121} GtkInspectorGeneralClass;
122
123G_DEFINE_TYPE (GtkInspectorGeneral, gtk_inspector_general, GTK_TYPE_WIDGET)
124
125static void
126init_version (GtkInspectorGeneral *gen)
127{
128 const char *backend;
129 GdkSurface *surface;
130 GskRenderer *gsk_renderer;
131 const char *renderer;
132
133#ifdef GDK_WINDOWING_X11
134 if (GDK_IS_X11_DISPLAY (gen->display))
135 backend = "X11";
136 else
137#endif
138#ifdef GDK_WINDOWING_WAYLAND
139 if (GDK_IS_WAYLAND_DISPLAY (gen->display))
140 backend = "Wayland";
141 else
142#endif
143#ifdef GDK_WINDOWING_BROADWAY
144 if (GDK_IS_BROADWAY_DISPLAY (gen->display))
145 backend = "Broadway";
146 else
147#endif
148#ifdef GDK_WINDOWING_WIN32
149 if (GDK_IS_WIN32_DISPLAY (gen->display))
150 backend = "Windows";
151 else
152#endif
153#ifdef GDK_WINDOWING_MACOS
154 if (GDK_IS_MACOS_DISPLAY (gen->display))
155 backend = "MacOS";
156 else
157#endif
158 backend = "Unknown";
159
160 surface = gdk_surface_new_toplevel (display: gen->display);
161 gsk_renderer = gsk_renderer_new_for_surface (surface);
162 if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), s2: "GskVulkanRenderer") == 0)
163 renderer = "Vulkan";
164 else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), s2: "GskGLRenderer") == 0)
165 renderer = "GL";
166 else if (strcmp (G_OBJECT_TYPE_NAME (gsk_renderer), s2: "GskCairoRenderer") == 0)
167 renderer = "Cairo";
168 else
169 renderer = "Unknown";
170
171 gsk_renderer_unrealize (renderer: gsk_renderer);
172 g_object_unref (object: gsk_renderer);
173 gdk_surface_destroy (surface);
174
175 gtk_label_set_text (GTK_LABEL (gen->gtk_version), GTK_VERSION);
176 gtk_label_set_text (GTK_LABEL (gen->gdk_backend), str: backend);
177 gtk_label_set_text (GTK_LABEL (gen->gsk_renderer), str: renderer);
178}
179
180static void
181init_app_id (GtkInspectorGeneral *gen)
182{
183 GApplication *app;
184
185 app = g_application_get_default ();
186 if (!app)
187 {
188 gtk_widget_hide (widget: gen->app_id_frame);
189 return;
190 }
191
192 gtk_label_set_text (GTK_LABEL (gen->app_id),
193 str: g_application_get_application_id (application: app));
194 gtk_label_set_text (GTK_LABEL (gen->resource_path),
195 str: g_application_get_resource_base_path (application: app));
196}
197
198static G_GNUC_UNUSED void
199add_check_row (GtkInspectorGeneral *gen,
200 GtkListBox *list,
201 const char *name,
202 gboolean value,
203 int indent)
204{
205 GtkWidget *row, *box, *label, *check;
206
207 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 40);
208 g_object_set (object: box, first_property_name: "margin-start", indent, NULL);
209
210 label = gtk_label_new (str: name);
211 gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START);
212 gtk_widget_set_valign (widget: label, align: GTK_ALIGN_BASELINE);
213 gtk_label_set_xalign (GTK_LABEL (label), xalign: 0.0);
214 gtk_widget_set_hexpand (widget: label, TRUE);
215 gtk_box_append (GTK_BOX (box), child: label);
216
217 check = gtk_image_new_from_icon_name (icon_name: "object-select-symbolic");
218 gtk_widget_set_halign (widget: check, align: GTK_ALIGN_END);
219 gtk_widget_set_valign (widget: check, align: GTK_ALIGN_BASELINE);
220 gtk_widget_set_opacity (widget: check, opacity: value ? 1.0 : 0.0);
221 gtk_box_append (GTK_BOX (box), child: check);
222
223 row = gtk_list_box_row_new ();
224 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), child: box);
225 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
226
227 gtk_widget_set_hexpand (widget: box, FALSE);
228 gtk_list_box_insert (box: list, child: row, position: -1);
229
230 gtk_size_group_add_widget (GTK_SIZE_GROUP (gen->labels), widget: label);
231}
232
233static void
234add_label_row (GtkInspectorGeneral *gen,
235 GtkListBox *list,
236 const char *name,
237 const char *value,
238 int indent)
239{
240 GtkWidget *box;
241 GtkWidget *label;
242 GtkWidget *row;
243
244 box = gtk_box_new (orientation: GTK_ORIENTATION_HORIZONTAL, spacing: 40);
245 g_object_set (object: box, first_property_name: "margin-start", indent, NULL);
246
247 label = gtk_label_new (str: name);
248 gtk_widget_set_halign (widget: label, align: GTK_ALIGN_START);
249 gtk_widget_set_valign (widget: label, align: GTK_ALIGN_BASELINE);
250 gtk_label_set_xalign (GTK_LABEL (label), xalign: 0.0);
251 gtk_widget_set_hexpand (widget: label, TRUE);
252 gtk_box_append (GTK_BOX (box), child: label);
253
254 label = gtk_label_new (str: value);
255 gtk_label_set_selectable (GTK_LABEL (label), TRUE);
256 gtk_widget_set_halign (widget: label, align: GTK_ALIGN_END);
257 gtk_widget_set_valign (widget: label, align: GTK_ALIGN_BASELINE);
258 gtk_label_set_xalign (GTK_LABEL (label), xalign: 1.0);
259 gtk_box_append (GTK_BOX (box), child: label);
260
261 row = gtk_list_box_row_new ();
262 gtk_list_box_row_set_child (GTK_LIST_BOX_ROW (row), child: box);
263 gtk_list_box_row_set_activatable (GTK_LIST_BOX_ROW (row), FALSE);
264
265 gtk_widget_set_hexpand (widget: box, FALSE);
266 gtk_list_box_insert (GTK_LIST_BOX (list), child: row, position: -1);
267
268 gtk_size_group_add_widget (GTK_SIZE_GROUP (gen->labels), widget: label);
269}
270
271#ifdef GDK_WINDOWING_X11
272static void
273append_glx_extension_row (GtkInspectorGeneral *gen,
274 Display *dpy,
275 const char *ext)
276{
277 add_check_row (gen, GTK_LIST_BOX (gen->gl_box), name: ext, value: epoxy_has_glx_extension (dpy, screen: 0, extension: ext), indent: 0);
278}
279#endif
280
281#ifdef GDK_WINDOWING_WIN32
282static void
283append_wgl_extension_row (GtkInspectorGeneral *gen,
284 const char *ext)
285{
286 HDC hdc = 0;
287
288 add_check_row (gen, GTK_LIST_BOX (gen->gl_box), ext, epoxy_has_wgl_extension (hdc, ext), 0);
289}
290#endif
291
292#if defined(GDK_WINDOWING_WAYLAND) || defined (GDK_WINDOWING_X11) || (defined (GDK_WINDOWING_WIN32) && defined(GDK_WIN32_ENABLE_EGL))
293static void
294append_egl_extension_row (GtkInspectorGeneral *gen,
295 EGLDisplay dpy,
296 const char *ext)
297{
298 add_check_row (gen, GTK_LIST_BOX (gen->gl_box), name: ext, value: epoxy_has_egl_extension (dpy, extension: ext), indent: 0);
299}
300
301static EGLDisplay
302get_egl_display (GdkDisplay *display)
303{
304#ifdef GDK_WINDOWING_WAYLAND
305 if (GDK_IS_WAYLAND_DISPLAY (display))
306 return gdk_wayland_display_get_egl_display (display);
307 else
308#endif
309#ifdef GDK_WINDOWING_X11
310 if (GDK_IS_X11_DISPLAY (display))
311 return gdk_x11_display_get_egl_display (display);
312 else
313#endif
314#ifdef GDK_WINDOWING_WIN32
315 if (GDK_IS_WIN32_DISPLAY (display))
316 return gdk_win32_display_get_egl_display (display);
317 else
318#endif
319 return NULL;
320}
321#endif
322
323static void
324init_gl (GtkInspectorGeneral *gen)
325{
326 GError *error = NULL;
327
328 if (!gdk_display_prepare_gl (self: gen->display, error: &error))
329 {
330 gtk_label_set_text (GTK_LABEL (gen->gl_version), C_("GL version", "None"));
331 gtk_widget_set_visible (widget: gen->gl_vendor_row, FALSE);
332 gtk_widget_set_visible (widget: gen->gl_error_row, TRUE);
333 gtk_label_set_text (GTK_LABEL (gen->gl_error), str: error->message);
334 g_error_free (error);
335 }
336
337 if (gdk_display_get_debug_flags (display: gen->display) & GDK_DEBUG_GL_DISABLE)
338 {
339 gtk_label_set_text (GTK_LABEL (gen->gl_version), C_("GL version", "Disabled"));
340 gtk_label_set_text (GTK_LABEL (gen->gl_vendor), C_("GL vendor", "Disabled"));
341 return;
342 }
343
344#if defined(GDK_WINDOWING_X11) || defined(GDK_WINDOWING_WAYLAND) || (defined(GDK_WINDOWING_WIN32) && defined(GDK_WIN32_ENABLE_EGL))
345 EGLDisplay egl_display = get_egl_display (display: gen->display);
346 if (egl_display)
347 {
348 char *version;
349
350 version = g_strconcat (string1: "EGL ", eglQueryString (egl_display, EGL_VERSION), NULL);
351 gtk_label_set_text (GTK_LABEL (gen->gl_version), str: version);
352 g_free (mem: version);
353
354 gtk_label_set_text (GTK_LABEL (gen->gl_vendor), eglQueryString (egl_display, EGL_VENDOR));
355
356 append_egl_extension_row (gen, dpy: egl_display, ext: "EGL_KHR_create_context");
357 append_egl_extension_row (gen, dpy: egl_display, ext: "EGL_EXT_buffer_age");
358 append_egl_extension_row (gen, dpy: egl_display, ext: "EGL_EXT_swap_buffers_with_damage");
359 append_egl_extension_row (gen, dpy: egl_display, ext: "EGL_KHR_surfaceless_context");
360 }
361 else
362#endif
363#ifdef GDK_WINDOWING_X11
364 if (GDK_IS_X11_DISPLAY (gen->display))
365 {
366 Display *dpy = GDK_DISPLAY_XDISPLAY (gen->display);
367 int error_base, event_base;
368 char *version;
369
370 if (!glXQueryExtension (dpy, &error_base, &event_base))
371 return;
372
373 version = g_strconcat (string1: "GLX ", glXGetClientString (dpy, GLX_VERSION), NULL);
374 gtk_label_set_text (GTK_LABEL (gen->gl_version), str: version);
375 g_free (mem: version);
376 gtk_label_set_text (GTK_LABEL (gen->gl_vendor), glXGetClientString (dpy, GLX_VENDOR));
377
378 append_glx_extension_row (gen, dpy, ext: "GLX_ARB_create_context_profile");
379 append_glx_extension_row (gen, dpy, ext: "GLX_SGI_swap_control");
380 append_glx_extension_row (gen, dpy, ext: "GLX_EXT_texture_from_pixmap");
381 append_glx_extension_row (gen, dpy, ext: "GLX_SGI_video_sync");
382 append_glx_extension_row (gen, dpy, ext: "GLX_EXT_buffer_age");
383 append_glx_extension_row (gen, dpy, ext: "GLX_OML_sync_control");
384 append_glx_extension_row (gen, dpy, ext: "GLX_ARB_multisample");
385 append_glx_extension_row (gen, dpy, ext: "GLX_EXT_visual_rating");
386 }
387 else
388#endif
389#ifdef GDK_WINDOWING_WIN32
390 if (GDK_IS_WIN32_DISPLAY (gen->display) &&
391 gdk_gl_backend_can_be_used (GDK_GL_WGL, NULL))
392 {
393 int gl_version;
394 char *version;
395
396 gl_version = epoxy_gl_version ();
397 version = g_strdup_printf ("WGL %d.%d", gl_version / 10, gl_version % 10);
398 gtk_label_set_text (GTK_LABEL (gen->gl_version), version);
399 g_free (version);
400 gtk_label_set_text (GTK_LABEL (gen->gl_vendor), (const char *) glGetString (GL_VENDOR));
401
402 append_wgl_extension_row (gen, "WGL_EXT_create_context");
403 append_wgl_extension_row (gen, "WGL_EXT_swap_control");
404 append_wgl_extension_row (gen, "WGL_OML_sync_control");
405 append_wgl_extension_row (gen, "WGL_ARB_pixel_format");
406 append_wgl_extension_row (gen, "WGL_ARB_multisample");
407 }
408 else
409#endif
410 {
411 gtk_label_set_text (GTK_LABEL (gen->gl_version), C_("GL version", "None"));
412 gtk_label_set_text (GTK_LABEL (gen->gl_vendor), C_("GL vendor", "None"));
413 }
414}
415
416#ifdef GDK_RENDERING_VULKAN
417static gboolean
418has_debug_extension (GdkVulkanContext *context)
419{
420 uint32_t i;
421 uint32_t n_extensions;
422 vkEnumerateInstanceExtensionProperties (NULL, &n_extensions, NULL);
423 VkExtensionProperties *extensions = g_newa (VkExtensionProperties, n_extensions);
424 vkEnumerateInstanceExtensionProperties (NULL, &n_extensions, extensions);
425
426 for (i = 0; i < n_extensions; i++)
427 {
428 if (g_str_equal (extensions[i].extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
429 return TRUE;
430 }
431
432 return FALSE;
433}
434
435static gboolean
436has_validation_layer (GdkVulkanContext *context)
437{
438 uint32_t i;
439 uint32_t n_layers;
440 vkEnumerateInstanceLayerProperties (&n_layers, NULL);
441 VkLayerProperties *layers = g_newa (VkLayerProperties, n_layers);
442 vkEnumerateInstanceLayerProperties (&n_layers, layers);
443
444 for (i = 0; i < n_layers; i++)
445 {
446 if (g_str_equal (layers[i].layerName, "VK_LAYER_LUNARG_standard_validation"))
447 return TRUE;
448 }
449
450 return FALSE;
451}
452#endif
453
454static void
455init_vulkan (GtkInspectorGeneral *gen)
456{
457#ifdef GDK_RENDERING_VULKAN
458 GdkSurface *surface;
459 GdkVulkanContext *context;
460
461 if (gdk_display_get_debug_flags (gen->display) & GDK_DEBUG_VULKAN_DISABLE)
462 {
463 gtk_label_set_text (GTK_LABEL (gen->vk_device), C_("Vulkan device", "Disabled"));
464 gtk_label_set_text (GTK_LABEL (gen->vk_api_version), C_("Vulkan version", "Disabled"));
465 gtk_label_set_text (GTK_LABEL (gen->vk_driver_version), C_("Vulkan version", "Disabled"));
466 return;
467 }
468
469 surface = gdk_surface_new_toplevel (gen->display);
470 context = gdk_surface_create_vulkan_context (surface, NULL);
471 gdk_surface_destroy (surface);
472
473 if (context)
474 {
475 VkPhysicalDevice vk_device;
476 VkPhysicalDeviceProperties props;
477 char *device_name;
478 char *api_version;
479 char *driver_version;
480
481 vk_device = gdk_vulkan_context_get_physical_device (context);
482 vkGetPhysicalDeviceProperties (vk_device, &props);
483
484 device_name = g_strdup_printf ("%s (%d)", props.deviceName, props.deviceType);
485 api_version = g_strdup_printf ("%d.%d.%d",
486 VK_VERSION_MAJOR (props.apiVersion),
487 VK_VERSION_MINOR (props.apiVersion),
488 VK_VERSION_PATCH (props.apiVersion));
489 driver_version = g_strdup_printf ("%d.%d.%d",
490 VK_VERSION_MAJOR (props.driverVersion),
491 VK_VERSION_MINOR (props.driverVersion),
492 VK_VERSION_PATCH (props.driverVersion));
493
494 gtk_label_set_text (GTK_LABEL (gen->vk_device), device_name);
495 gtk_label_set_text (GTK_LABEL (gen->vk_api_version), api_version);
496 gtk_label_set_text (GTK_LABEL (gen->vk_driver_version), driver_version);
497
498 g_free (device_name);
499 g_free (api_version);
500 g_free (driver_version);
501
502 add_check_row (gen, GTK_LIST_BOX (gen->vulkan_box), VK_KHR_SURFACE_EXTENSION_NAME, TRUE, 0);
503#ifdef GDK_WINDOWING_X11
504 if (GDK_IS_X11_DISPLAY (gen->display))
505 add_check_row (gen, GTK_LIST_BOX (gen->vulkan_box), "VK_KHR_xlib_surface", TRUE, 0);
506#endif
507#ifdef GDK_WINDOWING_WAYLAND
508 if (GDK_IS_WAYLAND_DISPLAY (gen->display))
509 add_check_row (gen, GTK_LIST_BOX (gen->vulkan_box), "VK_KHR_wayland_surface", TRUE, 0);
510#endif
511 add_check_row (gen, GTK_LIST_BOX (gen->vulkan_box), VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
512 has_debug_extension (context), 0);
513 add_check_row (gen, GTK_LIST_BOX (gen->vulkan_box), "VK_LAYER_LUNARG_standard_validation",
514 has_validation_layer (context), 0);
515
516 g_object_unref (context);
517 }
518 else
519#endif
520 {
521 gtk_label_set_text (GTK_LABEL (gen->vk_device), C_("Vulkan device", "None"));
522 gtk_label_set_text (GTK_LABEL (gen->vk_api_version), C_("Vulkan version", "None"));
523 gtk_label_set_text (GTK_LABEL (gen->vk_driver_version), C_("Vulkan version", "None"));
524 }
525}
526
527static void
528set_monospace_font (GtkWidget *w)
529{
530 PangoAttrList *attrs;
531
532 attrs = pango_attr_list_new ();
533 pango_attr_list_insert (list: attrs, attr: pango_attr_fallback_new (FALSE));
534 pango_attr_list_insert (list: attrs, attr: pango_attr_family_new (family: "Monospace"));
535 gtk_label_set_attributes (GTK_LABEL (w), attrs);
536 pango_attr_list_unref (list: attrs);
537}
538
539static void
540set_path_label (GtkWidget *w,
541 const char *var)
542{
543 const char *v;
544
545 v = g_getenv (variable: var);
546 if (v != NULL)
547 {
548 set_monospace_font (w);
549 gtk_label_set_text (GTK_LABEL (w), str: v);
550 }
551 else
552 {
553 GtkWidget *r;
554 r = gtk_widget_get_ancestor (widget: w, GTK_TYPE_LIST_BOX_ROW);
555 gtk_widget_hide (widget: r);
556 }
557}
558
559static void
560init_env (GtkInspectorGeneral *gen)
561{
562 set_monospace_font (gen->prefix);
563 gtk_label_set_text (GTK_LABEL (gen->prefix), str: _gtk_get_data_prefix ());
564 set_path_label (w: gen->xdg_data_home, var: "XDG_DATA_HOME");
565 set_path_label (w: gen->xdg_data_dirs, var: "XDG_DATA_DIRS");
566 set_path_label (w: gen->gtk_path, var: "GTK_PATH");
567 set_path_label (w: gen->gtk_exe_prefix, var: "GTK_EXE_PREFIX");
568 set_path_label (w: gen->gtk_data_prefix, var: "GTK_DATA_PREFIX");
569 set_path_label (w: gen->gsettings_schema_dir, var: "GSETTINGS_SCHEMA_DIR");
570}
571
572static const char *
573translate_subpixel_layout (GdkSubpixelLayout subpixel)
574{
575 switch (subpixel)
576 {
577 case GDK_SUBPIXEL_LAYOUT_NONE: return "none";
578 case GDK_SUBPIXEL_LAYOUT_UNKNOWN: return "unknown";
579 case GDK_SUBPIXEL_LAYOUT_HORIZONTAL_RGB: return "horizontal rgb";
580 case GDK_SUBPIXEL_LAYOUT_HORIZONTAL_BGR: return "horizontal bgr";
581 case GDK_SUBPIXEL_LAYOUT_VERTICAL_RGB: return "vertical rgb";
582 case GDK_SUBPIXEL_LAYOUT_VERTICAL_BGR: return "vertical bgr";
583 default: g_assert_not_reached (); return "none";
584 }
585}
586
587static void
588populate_display (GdkDisplay *display, GtkInspectorGeneral *gen)
589{
590 GList *children, *l;
591 GtkWidget *child;
592 GtkListBox *list;
593
594 gtk_widget_show (widget: gen->display_composited);
595 list = GTK_LIST_BOX (gen->display_box);
596 children = NULL;
597 for (child = gtk_widget_get_first_child (GTK_WIDGET (list));
598 child != NULL;
599 child = gtk_widget_get_next_sibling (widget: child))
600 {
601 if (GTK_IS_LIST_BOX_ROW (child))
602 children = g_list_prepend (list: children, data: child);
603 }
604 for (l = children; l; l = l->next)
605 {
606 child = l->data;
607 if (gtk_widget_is_ancestor (widget: gen->display_name, ancestor: child) ||
608 gtk_widget_is_ancestor (widget: gen->display_rgba, ancestor: child) ||
609 gtk_widget_is_ancestor (widget: gen->display_composited, ancestor: child))
610 continue;
611
612 gtk_list_box_remove (box: list, child);
613 }
614 g_list_free (list: children);
615
616 gtk_label_set_label (GTK_LABEL (gen->display_name), str: gdk_display_get_name (display));
617
618 gtk_widget_set_visible (widget: gen->display_rgba,
619 visible: gdk_display_is_rgba (display));
620 gtk_widget_set_visible (widget: gen->display_composited,
621 visible: gdk_display_is_composited (display));
622}
623
624static void
625add_monitor (GtkInspectorGeneral *gen,
626 GdkMonitor *monitor,
627 guint i)
628{
629 GtkListBox *list;
630 char *value;
631 GdkRectangle rect;
632 int scale;
633 char *name;
634 char *scale_str = NULL;
635 const char *manufacturer;
636 const char *model;
637
638 list = GTK_LIST_BOX (gen->monitor_box);
639
640 name = g_strdup_printf (format: "Monitor %u", i);
641 manufacturer = gdk_monitor_get_manufacturer (monitor);
642 model = gdk_monitor_get_model (monitor);
643 value = g_strdup_printf (format: "%s%s%s",
644 manufacturer ? manufacturer : "",
645 manufacturer || model ? " " : "",
646 model ? model : "");
647 add_label_row (gen, list, name, value, indent: 0);
648 g_free (mem: value);
649 g_free (mem: name);
650
651 add_label_row (gen, list, name: "Connector", value: gdk_monitor_get_connector (monitor), indent: 10);
652
653 gdk_monitor_get_geometry (monitor, geometry: &rect);
654 scale = gdk_monitor_get_scale_factor (monitor);
655 if (scale != 1)
656 scale_str = g_strdup_printf (format: " @ %d", scale);
657
658 value = g_strdup_printf (format: "%d × %d%s at %d, %d",
659 rect.width, rect.height,
660 scale_str ? scale_str : "",
661 rect.x, rect.y);
662 add_label_row (gen, list, name: "Geometry", value, indent: 10);
663 g_free (mem: value);
664 g_free (mem: scale_str);
665
666 value = g_strdup_printf (format: "%d × %d mm²",
667 gdk_monitor_get_width_mm (monitor),
668 gdk_monitor_get_height_mm (monitor));
669 add_label_row (gen, list, name: "Size", value, indent: 10);
670 g_free (mem: value);
671
672 if (gdk_monitor_get_refresh_rate (monitor) != 0)
673 {
674 value = g_strdup_printf (format: "%.2f Hz",
675 0.001 * gdk_monitor_get_refresh_rate (monitor));
676 add_label_row (gen, list, name: "Refresh rate", value, indent: 10);
677 g_free (mem: value);
678 }
679
680 if (gdk_monitor_get_subpixel_layout (monitor) != GDK_SUBPIXEL_LAYOUT_UNKNOWN)
681 {
682 add_label_row (gen, list, name: "Subpixel layout",
683 value: translate_subpixel_layout (subpixel: gdk_monitor_get_subpixel_layout (monitor)),
684 indent: 10);
685 }
686}
687
688static void
689populate_monitors (GdkDisplay *display,
690 GtkInspectorGeneral *gen)
691{
692 GtkWidget *child;
693 GListModel *list;
694
695 while ((child = gtk_widget_get_first_child (widget: gen->monitor_box)))
696 gtk_list_box_remove (GTK_LIST_BOX (gen->monitor_box), child);
697
698 list = gdk_display_get_monitors (self: gen->display);
699
700 for (guint i = 0; i < g_list_model_get_n_items (list); i++)
701 {
702 GdkMonitor *monitor = g_list_model_get_item (list, position: i);
703 add_monitor (gen, monitor, i);
704 g_object_unref (object: monitor);
705 }
706}
707
708static void
709populate_display_notify_cb (GdkDisplay *display,
710 GParamSpec *pspec,
711 GtkInspectorGeneral *gen)
712{
713 populate_display (display, gen);
714}
715
716static void
717monitors_changed_cb (GListModel *monitors,
718 guint position,
719 guint removed,
720 guint added,
721 GtkInspectorGeneral *gen)
722{
723 populate_monitors (display: gen->display, gen);
724}
725
726static void
727init_display (GtkInspectorGeneral *gen)
728{
729 g_signal_connect (gen->display, "notify", G_CALLBACK (populate_display_notify_cb), gen);
730 g_signal_connect (gdk_display_get_monitors (gen->display), "items-changed",
731 G_CALLBACK (monitors_changed_cb), gen);
732
733 populate_display (display: gen->display, gen);
734 populate_monitors (display: gen->display, gen);
735}
736
737static void
738init_pango (GtkInspectorGeneral *gen)
739{
740 PangoFontMap *fontmap;
741 const char *type;
742 const char *name;
743
744 fontmap = pango_cairo_font_map_get_default ();
745 type = G_OBJECT_TYPE_NAME (fontmap);
746 if (strcmp (s1: type, s2: "PangoCairoFcFontMap") == 0)
747 name = "fontconfig";
748 else if (strcmp (s1: type, s2: "PangoCairoCoreTextFontMap") == 0)
749 name = "coretext";
750 else if (strcmp (s1: type, s2: "PangoCairoWin32FontMap") == 0)
751 name = "win32";
752 else
753 name = type;
754
755 gtk_label_set_label (GTK_LABEL (gen->pango_fontmap), str: name);
756}
757
758static void
759init_media (GtkInspectorGeneral *gen)
760{
761 GIOExtension *e;
762 const char *name;
763
764 e = gtk_media_file_get_extension ();
765 name = g_io_extension_get_name (extension: e);
766 gtk_label_set_label (GTK_LABEL (gen->media_backend), str: name);
767}
768
769static void
770im_module_changed (GtkSettings *settings,
771 GParamSpec *pspec,
772 GtkInspectorGeneral *gen)
773{
774 if (!gen->display)
775 return;
776
777 gtk_label_set_label (GTK_LABEL (gen->im_module),
778 str: _gtk_im_module_get_default_context_id (display: gen->display));
779}
780
781static void
782init_im_module (GtkInspectorGeneral *gen)
783{
784 GtkSettings *settings = gtk_settings_get_for_display (display: gen->display);
785 const char *default_context_id = _gtk_im_module_get_default_context_id (display: gen->display);
786
787 gtk_label_set_label (GTK_LABEL (gen->im_module), str: default_context_id);
788
789 if (g_getenv (variable: "GTK_IM_MODULE") != NULL)
790 {
791 /* This can't update if GTK_IM_MODULE envvar is set */
792 gtk_widget_set_tooltip_text (widget: gen->im_module,
793 _("IM Context is hardcoded by GTK_IM_MODULE"));
794 gtk_widget_set_sensitive (widget: gen->im_module, FALSE);
795 return;
796 }
797
798 g_signal_connect_object (instance: settings,
799 detailed_signal: "notify::gtk-im-module",
800 G_CALLBACK (im_module_changed),
801 gobject: gen, connect_flags: 0);
802}
803
804
805static void populate_seats (GtkInspectorGeneral *gen);
806
807static void
808add_tool (GtkInspectorGeneral *gen,
809 GdkDeviceTool *tool)
810{
811 GdkAxisFlags axes;
812 GString *str;
813 char *val;
814 int i;
815 GEnumClass *eclass;
816 GEnumValue *evalue;
817 GFlagsClass *fclass;
818 GFlagsValue *fvalue;
819
820 val = g_strdup_printf (format: "Serial %" G_GUINT64_FORMAT, gdk_device_tool_get_serial (tool));
821 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: "Tool", value: val, indent: 10);
822 g_free (mem: val);
823
824 eclass = g_type_class_ref (type: GDK_TYPE_DEVICE_TOOL_TYPE);
825 evalue = g_enum_get_value (enum_class: eclass, value: gdk_device_tool_get_tool_type (tool));
826 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: "Type", value: evalue->value_nick, indent: 20);
827 g_type_class_unref (g_class: eclass);
828
829 fclass = g_type_class_ref (type: GDK_TYPE_AXIS_FLAGS);
830 str = g_string_new (init: "");
831 axes = gdk_device_tool_get_axes (tool);
832 for (i = GDK_AXIS_X; i < GDK_AXIS_LAST; i++)
833 {
834 if ((axes & (1 << i)) != 0)
835 {
836 fvalue = g_flags_get_first_value (flags_class: fclass, value: i);
837 if (str->len > 0)
838 g_string_append (string: str, val: ", ");
839 g_string_append (string: str, val: fvalue->value_nick);
840 }
841 }
842 g_type_class_unref (g_class: fclass);
843
844 if (str->len > 0)
845 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: "Axes", value: str->str, indent: 20);
846
847 g_string_free (string: str, TRUE);
848}
849
850static void
851add_device (GtkInspectorGeneral *gen,
852 GdkDevice *device)
853{
854 const char *name;
855 guint n_touches;
856 char *text;
857 GEnumClass *class;
858 GEnumValue *value;
859
860 name = gdk_device_get_name (device);
861
862 class = g_type_class_ref (type: GDK_TYPE_INPUT_SOURCE);
863 value = g_enum_get_value (enum_class: class, value: gdk_device_get_source (device));
864
865 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name, value: value->value_nick, indent: 10);
866
867 g_object_get (object: device, first_property_name: "num-touches", &n_touches, NULL);
868 if (n_touches > 0)
869 {
870 text = g_strdup_printf (format: "%d", n_touches);
871 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: "Touches", value: text, indent: 20);
872 g_free (mem: text);
873 }
874
875#ifdef GDK_WINDOWING_WAYLAND
876 if (GDK_IS_WAYLAND_DEVICE (device) &&
877 gdk_device_get_source (device) == GDK_SOURCE_KEYBOARD)
878 {
879 struct xkb_keymap *keymap = gdk_wayland_device_get_xkb_keymap (device);
880 GString *s;
881
882 s = g_string_new (init: "");
883 for (int i = 0; i < xkb_keymap_num_layouts (keymap); i++)
884 {
885 if (s->len > 0)
886 g_string_append (string: s, val: ", ");
887 g_string_append (string: s, val: xkb_keymap_layout_get_name (keymap, idx: i));
888 }
889
890 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: "Layouts", value: s->str, indent: 20);
891 g_string_free (string: s, TRUE);
892 }
893#endif
894
895 g_type_class_unref (g_class: class);
896}
897
898static char *
899get_seat_capabilities (GdkSeat *seat)
900{
901 struct {
902 GdkSeatCapabilities cap;
903 const char *name;
904 } caps[] = {
905 { GDK_SEAT_CAPABILITY_POINTER, "Pointer" },
906 { GDK_SEAT_CAPABILITY_TOUCH, "Touch" },
907 { GDK_SEAT_CAPABILITY_TABLET_STYLUS, "Tablet" },
908 { GDK_SEAT_CAPABILITY_KEYBOARD, "Keyboard" },
909 { 0, NULL }
910 };
911 GString *str;
912 GdkSeatCapabilities capabilities;
913 int i;
914
915 str = g_string_new (init: "");
916 capabilities = gdk_seat_get_capabilities (seat);
917 for (i = 0; caps[i].cap != 0; i++)
918 {
919 if (capabilities & caps[i].cap)
920 {
921 if (str->len > 0)
922 g_string_append (string: str, val: ", ");
923 g_string_append (string: str, val: caps[i].name);
924 }
925 }
926
927 return g_string_free (string: str, FALSE);
928}
929
930static void
931add_seat (GtkInspectorGeneral *gen,
932 GdkSeat *seat,
933 int num)
934{
935 char *text;
936 char *caps;
937 GList *list, *l;
938
939 if (!g_object_get_data (G_OBJECT (seat), key: "inspector-connected"))
940 {
941 g_object_set_data (G_OBJECT (seat), key: "inspector-connected", GINT_TO_POINTER (1));
942 g_signal_connect_swapped (seat, "device-added", G_CALLBACK (populate_seats), gen);
943 g_signal_connect_swapped (seat, "device-removed", G_CALLBACK (populate_seats), gen);
944 g_signal_connect_swapped (seat, "tool-added", G_CALLBACK (populate_seats), gen);
945 g_signal_connect_swapped (seat, "tool-removed", G_CALLBACK (populate_seats), gen);
946 }
947
948 text = g_strdup_printf (format: "Seat %d", num);
949 caps = get_seat_capabilities (seat);
950
951 add_label_row (gen, GTK_LIST_BOX (gen->device_box), name: text, value: caps, indent: 0);
952 g_free (mem: text);
953 g_free (mem: caps);
954
955 list = gdk_seat_get_devices (seat, capabilities: GDK_SEAT_CAPABILITY_ALL);
956
957 for (l = list; l; l = l->next)
958 add_device (gen, GDK_DEVICE (l->data));
959
960 g_list_free (list);
961
962 list = gdk_seat_get_tools (seat);
963
964 for (l = list; l; l = l->next)
965 add_tool (gen, tool: l->data);
966
967 g_list_free (list);
968}
969
970static void
971disconnect_seat (GtkInspectorGeneral *gen,
972 GdkSeat *seat)
973{
974 g_signal_handlers_disconnect_by_func (seat, G_CALLBACK (populate_seats), gen);
975}
976
977static void
978populate_seats (GtkInspectorGeneral *gen)
979{
980 GtkWidget *child;
981 GList *list, *l;
982 int i;
983
984 while ((child = gtk_widget_get_first_child (widget: gen->device_box)))
985 gtk_list_box_remove (GTK_LIST_BOX (gen->device_box), child);
986
987 list = gdk_display_list_seats (display: gen->display);
988
989 for (l = list, i = 0; l; l = l->next, i++)
990 add_seat (gen, GDK_SEAT (l->data), num: i);
991
992 g_list_free (list);
993}
994
995static void
996seat_added (GdkDisplay *display,
997 GdkSeat *seat,
998 GtkInspectorGeneral *gen)
999{
1000 populate_seats (gen);
1001}
1002
1003static void
1004seat_removed (GdkDisplay *display,
1005 GdkSeat *seat,
1006 GtkInspectorGeneral *gen)
1007{
1008 disconnect_seat (gen, seat);
1009 populate_seats (gen);
1010}
1011
1012static void
1013init_device (GtkInspectorGeneral *gen)
1014{
1015 g_signal_connect (gen->display, "seat-added", G_CALLBACK (seat_added), gen);
1016 g_signal_connect (gen->display, "seat-removed", G_CALLBACK (seat_removed), gen);
1017
1018 populate_seats (gen);
1019}
1020
1021static void
1022gtk_inspector_general_init (GtkInspectorGeneral *gen)
1023{
1024 gtk_widget_init_template (GTK_WIDGET (gen));
1025}
1026
1027static gboolean
1028keynav_failed (GtkWidget *widget, GtkDirectionType direction, GtkInspectorGeneral *gen)
1029{
1030 GtkWidget *next;
1031
1032 if (direction == GTK_DIR_DOWN && widget == gen->version_box)
1033 next = gen->env_box;
1034 else if (direction == GTK_DIR_DOWN && widget == gen->env_box)
1035 next = gen->display_box;
1036 else if (direction == GTK_DIR_DOWN && widget == gen->display_box)
1037 next = gen->monitor_box;
1038 else if (direction == GTK_DIR_DOWN && widget == gen->monitor_box)
1039 next = gen->gl_box;
1040 else if (direction == GTK_DIR_DOWN && widget == gen->gl_box)
1041 next = gen->vulkan_box;
1042 else if (direction == GTK_DIR_DOWN && widget == gen->vulkan_box)
1043 next = gen->device_box;
1044 else if (direction == GTK_DIR_UP && widget == gen->device_box)
1045 next = gen->vulkan_box;
1046 else if (direction == GTK_DIR_UP && widget == gen->vulkan_box)
1047 next = gen->gl_box;
1048 else if (direction == GTK_DIR_UP && widget == gen->gl_box)
1049 next = gen->monitor_box;
1050 else if (direction == GTK_DIR_UP && widget == gen->monitor_box)
1051 next = gen->display_box;
1052 else if (direction == GTK_DIR_UP && widget == gen->display_box)
1053 next = gen->env_box;
1054 else if (direction == GTK_DIR_UP && widget == gen->env_box)
1055 next = gen->version_box;
1056 else
1057 next = NULL;
1058
1059 if (next)
1060 {
1061 gtk_widget_child_focus (widget: next, direction);
1062 return TRUE;
1063 }
1064
1065 return FALSE;
1066}
1067
1068static void
1069gtk_inspector_general_constructed (GObject *object)
1070{
1071 GtkInspectorGeneral *gen = GTK_INSPECTOR_GENERAL (object);
1072
1073 G_OBJECT_CLASS (gtk_inspector_general_parent_class)->constructed (object);
1074
1075 g_signal_connect (gen->version_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1076 g_signal_connect (gen->env_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1077 g_signal_connect (gen->display_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1078 g_signal_connect (gen->monitor_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1079 g_signal_connect (gen->gl_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1080 g_signal_connect (gen->vulkan_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1081 g_signal_connect (gen->device_box, "keynav-failed", G_CALLBACK (keynav_failed), gen);
1082}
1083
1084static void
1085gtk_inspector_general_dispose (GObject *object)
1086{
1087 GtkInspectorGeneral *gen = GTK_INSPECTOR_GENERAL (object);
1088 GList *list, *l;
1089
1090 g_clear_pointer (&gen->swin, gtk_widget_unparent);
1091
1092 g_signal_handlers_disconnect_by_func (gen->display, G_CALLBACK (seat_added), gen);
1093 g_signal_handlers_disconnect_by_func (gen->display, G_CALLBACK (seat_removed), gen);
1094 g_signal_handlers_disconnect_by_func (gen->display, G_CALLBACK (populate_display_notify_cb), gen);
1095 g_signal_handlers_disconnect_by_func (gdk_display_get_monitors (gen->display), G_CALLBACK (monitors_changed_cb), gen);
1096
1097 list = gdk_display_list_seats (display: gen->display);
1098 for (l = list; l; l = l->next)
1099 disconnect_seat (gen, GDK_SEAT (l->data));
1100 g_list_free (list);
1101
1102 G_OBJECT_CLASS (gtk_inspector_general_parent_class)->dispose (object);
1103}
1104
1105static void
1106gtk_inspector_general_class_init (GtkInspectorGeneralClass *klass)
1107{
1108 GObjectClass *object_class = G_OBJECT_CLASS (klass);
1109 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
1110
1111 object_class->constructed = gtk_inspector_general_constructed;
1112 object_class->dispose = gtk_inspector_general_dispose;
1113
1114 gtk_widget_class_set_template_from_resource (widget_class, resource_name: "/org/gtk/libgtk/inspector/general.ui");
1115 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, swin);
1116 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, box);
1117 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, version_box);
1118 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, env_box);
1119 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, display_box);
1120 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, monitor_box);
1121 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_box);
1122 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, vulkan_box);
1123 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gtk_version);
1124 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gdk_backend);
1125 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gsk_renderer);
1126 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, pango_fontmap);
1127 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, media_backend);
1128 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, im_module);
1129 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_version);
1130 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_error);
1131 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_error_row);
1132 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_vendor);
1133 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gl_vendor_row);
1134 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, vk_device);
1135 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, vk_api_version);
1136 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, vk_driver_version);
1137 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, app_id_frame);
1138 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, app_id);
1139 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, resource_path);
1140 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, prefix);
1141 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, xdg_data_home);
1142 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, xdg_data_dirs);
1143 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gtk_path);
1144 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gtk_exe_prefix);
1145 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gtk_data_prefix);
1146 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, gsettings_schema_dir);
1147 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, labels);
1148 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, display_name);
1149 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, display_composited);
1150 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, display_rgba);
1151 gtk_widget_class_bind_template_child (widget_class, GtkInspectorGeneral, device_box);
1152
1153 gtk_widget_class_set_layout_manager_type (widget_class, GTK_TYPE_BIN_LAYOUT);
1154}
1155
1156void
1157gtk_inspector_general_set_display (GtkInspectorGeneral *gen,
1158 GdkDisplay *display)
1159{
1160 gen->display = display;
1161
1162 init_version (gen);
1163 init_env (gen);
1164 init_app_id (gen);
1165 init_display (gen);
1166 init_pango (gen);
1167 init_media (gen);
1168 init_gl (gen);
1169 init_vulkan (gen);
1170 init_device (gen);
1171 init_im_module (gen);
1172}
1173
1174// vim: set et sw=2 ts=2:
1175

source code of gtk/gtk/inspector/general.c