/* GDK - The GIMP Drawing Kit * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS * file for a list of people on the GTK+ Team. See the ChangeLog * files for a list of changes. These files are distributed with * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ #include "config.h" #include "version/gdkversionmacros.h" #include "gdkresources.h" #include "gdkconstructorprivate.h" #include "gdkdebugprivate.h" #include "gdkdisplayprivate.h" #include "gdkglcontextprivate.h" #include #include "gdkprivate.h" #include #include #include #include #include #include /* GTK has a general architectural assumption that gsize is pointer-sized * (equivalent to uintptr_t), making it non-portable to architectures like * CHERI where that isn't true. If a future release relaxes that * assumption, changes will be needed in numerous places. * See also https://gitlab.gnome.org/GNOME/glib/-/issues/2842 for the * equivalent in GLib, which would be a prerequisite. */ G_STATIC_ASSERT (sizeof (gsize) == sizeof (void *)); G_STATIC_ASSERT (G_ALIGNOF (gsize) == G_ALIGNOF (void *)); /** * GDK_WINDOWING_X11: * * The `GDK_WINDOWING_X11` macro is defined if the X11 backend * is supported. * * Use this macro to guard code that is specific to the X11 backend. */ /** * GDK_WINDOWING_WIN32: * * The `GDK_WINDOWING_WIN32` macro is defined if the Win32 backend * is supported. * * Use this macro to guard code that is specific to the Win32 backend. */ /** * GDK_WINDOWING_MACOS: * * The `GDK_WINDOWING_MACOS` macro is defined if the MacOS backend * is supported. * * Use this macro to guard code that is specific to the MacOS backend. */ /** * GDK_WINDOWING_WAYLAND: * * The `GDK_WINDOWING_WAYLAND` macro is defined if the Wayland backend * is supported. * * Use this macro to guard code that is specific to the Wayland backend. */ /** * GDK_DISABLE_DEPRECATION_WARNINGS: * * A macro that should be defined before including the gdk.h header. * * If it is defined, no compiler warnings will be produced for uses * of deprecated GDK APIs. */ typedef struct _GdkThreadsDispatch GdkThreadsDispatch; struct _GdkThreadsDispatch { GSourceFunc func; gpointer data; GDestroyNotify destroy; }; /* Private variable declarations */ static int gdk_initialized = 0; /* 1 if the library is initialized, * 0 otherwise. */ static const GdkDebugKey gdk_debug_keys[] = { { "misc", GDK_DEBUG_MISC, "Miscellaneous information" }, { "events", GDK_DEBUG_EVENTS, "Information about events" }, { "dnd", GDK_DEBUG_DND, "Information about Drag-and-Drop" }, { "input", GDK_DEBUG_INPUT, "Information about input (Windows)" }, { "eventloop", GDK_DEBUG_EVENTLOOP, "Information about event loop operation (Quartz)" }, { "frames", GDK_DEBUG_FRAMES, "Information about the frame clock" }, { "settings", GDK_DEBUG_SETTINGS, "Information about xsettings" }, { "opengl", GDK_DEBUG_OPENGL, "Information about OpenGL" }, { "vulkan", GDK_DEBUG_VULKAN, "Information about Vulkan" }, { "selection", GDK_DEBUG_SELECTION, "Information about selections" }, { "clipboard", GDK_DEBUG_CLIPBOARD, "Information about clipboards" }, { "dmabuf", GDK_DEBUG_DMABUF, "Information about dmabuf buffers" }, { "offload", GDK_DEBUG_OFFLOAD, "Information about subsurfaces and graphics offload" }, { "linear", GDK_DEBUG_LINEAR, "Enable linear rendering" }, { "hdr", GDK_DEBUG_HDR, "Force HDR rendering" }, { "portals", GDK_DEBUG_PORTALS, "Force use of portals" }, { "no-portals", GDK_DEBUG_NO_PORTALS, "Disable use of portals" }, { "force-offload", GDK_DEBUG_FORCE_OFFLOAD, "Force graphics offload for all textures" }, { "gl-no-fractional", GDK_DEBUG_GL_NO_FRACTIONAL, "Disable fractional scaling for OpenGL" }, { "gl-debug", GDK_DEBUG_GL_DEBUG, "Insert debugging information in OpenGL" }, { "gl-prefer-gl", GDK_DEBUG_GL_PREFER_GL, "Prefer GL over GLES API" }, { "default-settings",GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings" }, { "high-depth", GDK_DEBUG_HIGH_DEPTH, "Use high bit depth rendering if possible" }, { "no-vsync", GDK_DEBUG_NO_VSYNC, "Repaint instantly (uses 100% CPU with animations)" }, }; static const GdkDebugKey gdk_feature_keys[] = { { "gl", GDK_FEATURE_OPENGL, "Disable OpenGL support" }, { "gl-api", GDK_FEATURE_GL_API, "Disable non-GLES GL API" }, { "gles-api", GDK_FEATURE_GLES_API, "Disable GLES GL API" }, { "egl", GDK_FEATURE_EGL, "Disable EGL" }, { "glx", GDK_FEATURE_GLX, "Disable GLX" }, { "wgl", GDK_FEATURE_WGL, "Disable WGL" }, { "vulkan", GDK_FEATURE_VULKAN, "Disable Vulkan support" }, { "dmabuf", GDK_FEATURE_DMABUF, "Disable dmabuf support" }, { "offload", GDK_FEATURE_OFFLOAD, "Disable graphics offload" }, { "color-mgmt", GDK_FEATURE_COLOR_MANAGEMENT, "Disable color management" }, }; static GdkFeatures gdk_features; gboolean gdk_has_feature (GdkFeatures features) { return (features & gdk_features) == features; } #ifdef G_HAS_CONSTRUCTORS #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(stash_and_unset_environment) #endif G_DEFINE_CONSTRUCTOR(stash_and_unset_environment) #endif static char *startup_notification_id = NULL; static char *xdg_activation_token = NULL; static void stash_and_unset_environment (void) { /* Copies environment variables and unsets them so they won't be inherited by * child processes and confuse things. * * Changing environment variables can be racy so we try to do this as early as * possible in the program flow and before any printing that might involve * environment variables. */ struct { const char *key; char **dst; } vars[] = { { "DESKTOP_STARTUP_ID", &startup_notification_id }, { "XDG_ACTIVATION_TOKEN", &xdg_activation_token }, }; size_t i; for (i = 0; i < G_N_ELEMENTS (vars); i++) *vars[i].dst = g_strdup (g_getenv (vars[i].key)); for (i = 0; i < G_N_ELEMENTS (vars); i++) g_unsetenv (vars[i].key); for (i = 0; i < G_N_ELEMENTS (vars); i++) { if (*vars[i].dst == NULL) continue; if (!g_utf8_validate (*vars[i].dst, -1, NULL)) { g_warning ("%s contains invalid UTF-8", vars[i].key); g_clear_pointer (vars[i].dst, g_free); } } } static gpointer register_resources (gpointer dummy G_GNUC_UNUSED) { _gdk_register_resource (); return NULL; } static void gdk_ensure_resources (void) { static GOnce register_resources_once = G_ONCE_INIT; g_once (®ister_resources_once, register_resources, NULL); } guint gdk_parse_debug_var (const char *variable, const char *docs, const GdkDebugKey *keys, guint nkeys) { guint i; guint result = 0; const char *string; const char *p; const char *q; gboolean invert; gboolean help; string = g_getenv (variable); if (string == NULL) return 0; p = string; invert = FALSE; help = FALSE; while (*p) { q = strpbrk (p, ":;, \t"); if (!q) q = p + strlen (p); if (3 == q - p && g_ascii_strncasecmp ("all", p, q - p) == 0) { invert = TRUE; } else if (4 == q - p && g_ascii_strncasecmp ("help", p, q - p) == 0) { help = TRUE; } else { for (i = 0; i < nkeys; i++) { if (strlen (keys[i].key) == q - p && g_ascii_strncasecmp (keys[i].key, p, q - p) == 0) { result |= keys[i].value; break; } } if (i == nkeys) fprintf (stderr, "Unrecognized value \"%.*s\". Try %s=help\n", (int) (q - p), p, variable); } p = q; if (*p) p++; } if (help) { int max_width = 4; for (i = 0; i < nkeys; i++) max_width = MAX (max_width, strlen (keys[i].key)); max_width += 4; fprintf (stderr, "%s\n", docs); fprintf (stderr, "Supported %s values:\n", variable); for (i = 0; i < nkeys; i++) { fprintf (stderr, " %s%*s%s\n", keys[i].key, (int)(max_width - strlen (keys[i].key)), " ", keys[i].help); } fprintf (stderr, " %s%*s%s\n", "all", max_width - 3, " ", "Enable all values. Other given values are subtracted"); fprintf (stderr, " %s%*s%s\n", "help", max_width - 4, " ", "Print this help"); fprintf (stderr, "\nMultiple values can be given, separated by : or space.\n"); } if (invert) { guint all_flags = 0; for (i = 0; i < nkeys; i++) { all_flags |= keys[i].value; } result = all_flags & (~result); } return result; } void gdk_pre_parse (void) { GdkFeatures disabled_features; gdk_initialized = TRUE; gdk_ensure_resources (); _gdk_debug_flags = gdk_parse_debug_var ("GDK_DEBUG", "GDK_DEBUG can be set to values that make GDK print out different\n" "types of debugging information or change the behavior of GDK for\n" "debugging purposes.\n", gdk_debug_keys, G_N_ELEMENTS (gdk_debug_keys)); disabled_features = gdk_parse_debug_var ("GDK_DISABLE", "GDK_DISABLE can be set to values which cause GDK to disable\n" "certain features.\n", gdk_feature_keys, G_N_ELEMENTS (gdk_feature_keys)); gdk_features = GDK_ALL_FEATURES & ~disabled_features; #ifndef G_HAS_CONSTRUCTORS stash_and_unset_environment (); #endif } /*< private > * gdk_display_open_default: * * Opens the default display specified by command line arguments or * environment variables, sets it as the default display, and returns * it. If the default display has previously been set, simply returns * that. An internal function that should not be used by applications. * * Returns: (nullable) (transfer none): the default display, if it * could be opened, otherwise %NULL. */ GdkDisplay * gdk_display_open_default (void) { GdkDisplay *display; g_return_val_if_fail (gdk_initialized, NULL); display = gdk_display_get_default (); if (display) return display; display = gdk_display_open (NULL); return display; } /*< private > * gdk_get_startup_notification_id: * * Returns the original value of the XDG_ACTIVATION_TOKEN environment * variable if it was defined and valid, otherwise it returns the original * value of the DESKTOP_STARTUP_ID environment variable if it was defined * and valid, or %NULL if neither of them were defined and valid. * * Returns: (nullable) (transfer none): the original value of the * XDG_ACTIVATION_TOKEN or DESKTOP_STARTUP_ID environment variable */ const char * gdk_get_startup_notification_id (void) { if (xdg_activation_token) return xdg_activation_token; return startup_notification_id; } gboolean gdk_running_in_sandbox (void) { return g_file_test ("/.flatpak-info", G_FILE_TEST_EXISTS); } gboolean gdk_should_use_portal (void) { if (gdk_display_get_debug_flags (NULL) & GDK_DEBUG_PORTALS) return TRUE; if (gdk_display_get_debug_flags (NULL) & GDK_DEBUG_NO_PORTALS) return FALSE; if (gdk_running_in_sandbox ()) return TRUE; return FALSE; } PangoDirection gdk_unichar_direction (gunichar ch) { FriBidiCharType fribidi_ch_type; G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar)); fribidi_ch_type = fribidi_get_bidi_type (ch); if (!FRIBIDI_IS_STRONG (fribidi_ch_type)) return PANGO_DIRECTION_NEUTRAL; else if (FRIBIDI_IS_RTL (fribidi_ch_type)) return PANGO_DIRECTION_RTL; else return PANGO_DIRECTION_LTR; } PangoDirection gdk_find_base_dir (const char *text, int length) { PangoDirection dir = PANGO_DIRECTION_NEUTRAL; const char *p; g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL); p = text; while ((length < 0 || p < text + length) && *p) { gunichar wc = g_utf8_get_char (p); dir = gdk_unichar_direction (wc); if (dir != PANGO_DIRECTION_NEUTRAL) break; p = g_utf8_next_char (p); } return dir; } void gdk_source_set_static_name_by_id (guint tag, const char *name) { GSource *source; g_return_if_fail (tag > 0); source = g_main_context_find_source_by_id (NULL, tag); if (source == NULL) return; g_source_set_static_name (source, name); }