1 | /* GDK - The GIMP Drawing Kit |
2 | * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
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 | /* |
19 | * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
20 | * file for a list of people on the GTK+ Team. See the ChangeLog |
21 | * files for a list of changes. These files are distributed with |
22 | * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
23 | */ |
24 | |
25 | #include "config.h" |
26 | |
27 | #include "gdkversionmacros.h" |
28 | |
29 | #include "gdkresources.h" |
30 | |
31 | #include "gdkconstructor.h" |
32 | #include "gdkdebug.h" |
33 | #include "gdkdisplay.h" |
34 | #include "gdkglcontextprivate.h" |
35 | #include "gdkintl.h" |
36 | #include "gdk-private.h" |
37 | |
38 | #include <string.h> |
39 | #include <stdlib.h> |
40 | #include <stdio.h> |
41 | #include <ctype.h> |
42 | |
43 | #include <fribidi.h> |
44 | |
45 | |
46 | /** |
47 | * GDK_WINDOWING_X11: |
48 | * |
49 | * The `GDK_WINDOWING_X11` macro is defined if the X11 backend |
50 | * is supported. |
51 | * |
52 | * Use this macro to guard code that is specific to the X11 backend. |
53 | */ |
54 | |
55 | /** |
56 | * GDK_WINDOWING_WIN32: |
57 | * |
58 | * The `GDK_WINDOWING_WIN32` macro is defined if the Win32 backend |
59 | * is supported. |
60 | * |
61 | * Use this macro to guard code that is specific to the Win32 backend. |
62 | */ |
63 | |
64 | /** |
65 | * GDK_WINDOWING_MACOS: |
66 | * |
67 | * The `GDK_WINDOWING_MACOS` macro is defined if the MacOS backend |
68 | * is supported. |
69 | * |
70 | * Use this macro to guard code that is specific to the MacOS backend. |
71 | */ |
72 | |
73 | /** |
74 | * GDK_WINDOWING_WAYLAND: |
75 | * |
76 | * The `GDK_WINDOWING_WAYLAND` macro is defined if the Wayland backend |
77 | * is supported. |
78 | * |
79 | * Use this macro to guard code that is specific to the Wayland backend. |
80 | */ |
81 | |
82 | /** |
83 | * GDK_DISABLE_DEPRECATION_WARNINGS: |
84 | * |
85 | * A macro that should be defined before including the gdk.h header. |
86 | * |
87 | * If it is defined, no compiler warnings will be produced for uses |
88 | * of deprecated GDK APIs. |
89 | */ |
90 | |
91 | typedef struct _GdkThreadsDispatch GdkThreadsDispatch; |
92 | |
93 | struct _GdkThreadsDispatch |
94 | { |
95 | GSourceFunc func; |
96 | gpointer data; |
97 | GDestroyNotify destroy; |
98 | }; |
99 | |
100 | |
101 | /* Private variable declarations |
102 | */ |
103 | static int gdk_initialized = 0; /* 1 if the library is initialized, |
104 | * 0 otherwise. |
105 | */ |
106 | |
107 | static const GdkDebugKey gdk_debug_keys[] = { |
108 | { "misc" , GDK_DEBUG_MISC, "Miscellaneous information" }, |
109 | { "events" , GDK_DEBUG_EVENTS, "Information about events" }, |
110 | { "dnd" , GDK_DEBUG_DND, "Information about Drag-and-Drop" }, |
111 | { "input" , GDK_DEBUG_INPUT, "Information about input (Windows)" }, |
112 | { "eventloop" , GDK_DEBUG_EVENTLOOP, "Information about event loop operation (Quartz)" }, |
113 | { "frames" , GDK_DEBUG_FRAMES, "Information about the frame clock" }, |
114 | { "settings" , GDK_DEBUG_SETTINGS, "Information about xsettings" }, |
115 | { "opengl" , GDK_DEBUG_OPENGL, "Information about OpenGL" }, |
116 | { "vulkan" , GDK_DEBUG_VULKAN, "Information about Vulkan" }, |
117 | { "selection" , GDK_DEBUG_SELECTION, "Information about selections" }, |
118 | { "clipboard" , GDK_DEBUG_CLIPBOARD, "Information about clipboards" }, |
119 | { "nograbs" , GDK_DEBUG_NOGRABS, "Disable pointer and keyboard grabs (X11)" }, |
120 | { "gl-disable" , GDK_DEBUG_GL_DISABLE, "Disable OpenGL support" }, |
121 | { "gl-software" , GDK_DEBUG_GL_SOFTWARE, "Force OpenGL software rendering" }, |
122 | { "gl-texture-rect" , GDK_DEBUG_GL_TEXTURE_RECT, "Use OpenGL texture rectangle extension" }, |
123 | { "gl-legacy" , GDK_DEBUG_GL_LEGACY, "Use a legacy OpenGL context" }, |
124 | { "gl-gles" , GDK_DEBUG_GL_GLES, "Only allow OpenGL GLES API" }, |
125 | { "gl-debug" , GDK_DEBUG_GL_DEBUG, "Insert debugging information in OpenGL" }, |
126 | { "gl-egl" , GDK_DEBUG_GL_EGL, "Use EGL on X11 or Windows" }, |
127 | { "gl-glx" , GDK_DEBUG_GL_GLX, "Use GLX on X11" }, |
128 | { "gl-wgl" , GDK_DEBUG_GL_WGL, "Use WGL on Windows" }, |
129 | { "vulkan-disable" , GDK_DEBUG_VULKAN_DISABLE, "Disable Vulkan support" }, |
130 | { "vulkan-validate" , GDK_DEBUG_VULKAN_VALIDATE, "Load the Vulkan validation layer" }, |
131 | { "default-settings" ,GDK_DEBUG_DEFAULT_SETTINGS, "Force default values for xsettings" , TRUE }, |
132 | { "high-depth" , GDK_DEBUG_HIGH_DEPTH, "Use high bit depth rendering if possible" , TRUE }, |
133 | }; |
134 | |
135 | |
136 | #ifdef G_HAS_CONSTRUCTORS |
137 | #ifdef G_DEFINE_CONSTRUCTOR_NEEDS_PRAGMA |
138 | #pragma G_DEFINE_CONSTRUCTOR_PRAGMA_ARGS(stash_desktop_startup_notification_id) |
139 | #endif |
140 | G_DEFINE_CONSTRUCTOR(stash_desktop_startup_notification_id) |
141 | #endif |
142 | |
143 | static char *startup_notification_id = NULL; |
144 | |
145 | static void |
146 | stash_desktop_startup_notification_id (void) |
147 | { |
148 | const char *desktop_startup_id; |
149 | |
150 | desktop_startup_id = g_getenv (variable: "DESKTOP_STARTUP_ID" ); |
151 | if (desktop_startup_id && *desktop_startup_id != '\0') |
152 | { |
153 | if (!g_utf8_validate (str: desktop_startup_id, max_len: -1, NULL)) |
154 | g_warning ("DESKTOP_STARTUP_ID contains invalid UTF-8" ); |
155 | else |
156 | startup_notification_id = g_strdup (str: desktop_startup_id); |
157 | } |
158 | |
159 | /* Clear the environment variable so it won't be inherited by |
160 | * child processes and confuse things. |
161 | */ |
162 | g_unsetenv (variable: "DESKTOP_STARTUP_ID" ); |
163 | } |
164 | |
165 | static gpointer |
166 | register_resources (gpointer dummy G_GNUC_UNUSED) |
167 | { |
168 | _gdk_register_resource (); |
169 | |
170 | return NULL; |
171 | } |
172 | |
173 | static void |
174 | gdk_ensure_resources (void) |
175 | { |
176 | static GOnce register_resources_once = G_ONCE_INIT; |
177 | |
178 | g_once (®ister_resources_once, register_resources, NULL); |
179 | } |
180 | |
181 | guint |
182 | gdk_parse_debug_var (const char *variable, |
183 | const GdkDebugKey *keys, |
184 | guint nkeys) |
185 | { |
186 | guint i; |
187 | guint result = 0; |
188 | const char *string; |
189 | const char *p; |
190 | const char *q; |
191 | gboolean invert; |
192 | gboolean help; |
193 | gboolean debug_enabled; |
194 | |
195 | #ifdef G_ENABLE_DEBUG |
196 | debug_enabled = TRUE; |
197 | #else |
198 | debug_enabled = FALSE; |
199 | #endif |
200 | |
201 | string = g_getenv (variable); |
202 | if (string == NULL) |
203 | return 0; |
204 | |
205 | p = string; |
206 | invert = FALSE; |
207 | help = FALSE; |
208 | |
209 | while (*p) |
210 | { |
211 | q = strpbrk (s: p, accept: ":;, \t" ); |
212 | if (!q) |
213 | q = p + strlen (s: p); |
214 | |
215 | if (3 == q - p && g_ascii_strncasecmp (s1: "all" , s2: p, n: q - p) == 0) |
216 | { |
217 | invert = TRUE; |
218 | } |
219 | else if (4 == q - p && g_ascii_strncasecmp (s1: "help" , s2: p, n: q - p) == 0) |
220 | { |
221 | help = TRUE; |
222 | } |
223 | else |
224 | { |
225 | char *val = g_strndup (str: p, n: q - p); |
226 | for (i = 0; i < nkeys; i++) |
227 | { |
228 | if (strlen (s: keys[i].key) == q - p && |
229 | g_ascii_strncasecmp (s1: keys[i].key, s2: p, n: q - p) == 0) |
230 | { |
231 | if (!debug_enabled && !keys[i].always_enabled) |
232 | { |
233 | fprintf (stderr, format: "\"%s\" is only available when building GTK with G_ENABLE_DEBUG. See %s=help\n" , |
234 | val, variable); |
235 | break; |
236 | } |
237 | result |= keys[i].value; |
238 | break; |
239 | } |
240 | } |
241 | if (i == nkeys) |
242 | fprintf (stderr, format: "Unrecognized value \"%s\". Try %s=help\n" , val, variable); |
243 | g_free (mem: val); |
244 | } |
245 | |
246 | p = q; |
247 | if (*p) |
248 | p++; |
249 | } |
250 | |
251 | if (help) |
252 | { |
253 | int max_width = 4; |
254 | for (i = 0; i < nkeys; i++) |
255 | max_width = MAX (max_width, strlen (keys[i].key)); |
256 | max_width += 4; |
257 | |
258 | fprintf (stderr, format: "Supported %s values:\n" , variable); |
259 | for (i = 0; i < nkeys; i++) { |
260 | fprintf (stderr, format: " %s%*s%s" , keys[i].key, (int)(max_width - strlen (s: keys[i].key)), " " , keys[i].help); |
261 | if (!debug_enabled && !keys[i].always_enabled) |
262 | fprintf (stderr, format: " [unavailable]" ); |
263 | fprintf (stderr, format: "\n" ); |
264 | } |
265 | fprintf (stderr, format: " %s%*s%s\n" , "all" , max_width - 3, " " , "Enable all values" ); |
266 | fprintf (stderr, format: " %s%*s%s\n" , "help" , max_width - 4, " " , "Print this help" ); |
267 | fprintf (stderr, format: "\nMultiple values can be given, separated by : or space.\n" ); |
268 | if (!debug_enabled) |
269 | fprintf (stderr, format: "Values marked as [unavailable] are only accessible if GTK is built with G_ENABLE_DEBUG.\n" ); |
270 | } |
271 | |
272 | if (invert) |
273 | { |
274 | guint all_flags = 0; |
275 | |
276 | for (i = 0; i < nkeys; i++) |
277 | { |
278 | if (debug_enabled || keys[i].always_enabled) |
279 | all_flags |= keys[i].value; |
280 | } |
281 | |
282 | result = all_flags & (~result); |
283 | } |
284 | |
285 | return result; |
286 | } |
287 | |
288 | void |
289 | gdk_pre_parse (void) |
290 | { |
291 | gdk_initialized = TRUE; |
292 | |
293 | gdk_ensure_resources (); |
294 | |
295 | _gdk_debug_flags = gdk_parse_debug_var (variable: "GDK_DEBUG" , |
296 | keys: gdk_debug_keys, |
297 | G_N_ELEMENTS (gdk_debug_keys)); |
298 | |
299 | /* These are global */ |
300 | if (GDK_DEBUG_CHECK (GL_EGL)) |
301 | gdk_gl_backend_use (backend_type: GDK_GL_EGL); |
302 | else if (GDK_DEBUG_CHECK (GL_GLX)) |
303 | gdk_gl_backend_use (backend_type: GDK_GL_GLX); |
304 | else if (GDK_DEBUG_CHECK (GL_WGL)) |
305 | gdk_gl_backend_use (backend_type: GDK_GL_WGL); |
306 | |
307 | #ifndef G_HAS_CONSTRUCTORS |
308 | stash_desktop_startup_notification_id (); |
309 | #endif |
310 | } |
311 | |
312 | /*< private > |
313 | * gdk_display_open_default: |
314 | * |
315 | * Opens the default display specified by command line arguments or |
316 | * environment variables, sets it as the default display, and returns |
317 | * it. If the default display has previously been set, simply returns |
318 | * that. An internal function that should not be used by applications. |
319 | * |
320 | * Returns: (nullable) (transfer none): the default display, if it |
321 | * could be opened, otherwise %NULL. |
322 | */ |
323 | GdkDisplay * |
324 | gdk_display_open_default (void) |
325 | { |
326 | GdkDisplay *display; |
327 | |
328 | g_return_val_if_fail (gdk_initialized, NULL); |
329 | |
330 | display = gdk_display_get_default (); |
331 | if (display) |
332 | return display; |
333 | |
334 | display = gdk_display_open (NULL); |
335 | |
336 | return display; |
337 | } |
338 | |
339 | /*< private > |
340 | * gdk_get_startup_notification_id: |
341 | * |
342 | * Returns the original value of the DESKTOP_STARTUP_ID environment |
343 | * variable if it was defined and valid, or %NULL otherwise. |
344 | * |
345 | * Returns: (nullable) (transfer none): the original value of the |
346 | * DESKTOP_STARTUP_ID environment variable |
347 | */ |
348 | const char * |
349 | gdk_get_startup_notification_id (void) |
350 | { |
351 | return startup_notification_id; |
352 | } |
353 | |
354 | gboolean |
355 | gdk_running_in_sandbox (void) |
356 | { |
357 | return g_file_test (filename: "/.flatpak-info" , test: G_FILE_TEST_EXISTS); |
358 | } |
359 | |
360 | gboolean |
361 | gdk_should_use_portal (void) |
362 | { |
363 | static const char *use_portal = NULL; |
364 | |
365 | if (G_UNLIKELY (use_portal == NULL)) |
366 | { |
367 | if (gdk_running_in_sandbox ()) |
368 | use_portal = "1" ; |
369 | else |
370 | { |
371 | use_portal = g_getenv (variable: "GTK_USE_PORTAL" ); |
372 | if (!use_portal) |
373 | use_portal = "" ; |
374 | } |
375 | } |
376 | |
377 | return use_portal[0] == '1'; |
378 | } |
379 | |
380 | PangoDirection |
381 | gdk_unichar_direction (gunichar ch) |
382 | { |
383 | FriBidiCharType fribidi_ch_type; |
384 | |
385 | G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar)); |
386 | |
387 | fribidi_ch_type = fribidi_get_bidi_type (ch); |
388 | |
389 | if (!FRIBIDI_IS_STRONG (fribidi_ch_type)) |
390 | return PANGO_DIRECTION_NEUTRAL; |
391 | else if (FRIBIDI_IS_RTL (fribidi_ch_type)) |
392 | return PANGO_DIRECTION_RTL; |
393 | else |
394 | return PANGO_DIRECTION_LTR; |
395 | } |
396 | |
397 | PangoDirection |
398 | gdk_find_base_dir (const char *text, |
399 | int length) |
400 | { |
401 | PangoDirection dir = PANGO_DIRECTION_NEUTRAL; |
402 | const char *p; |
403 | |
404 | g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL); |
405 | |
406 | p = text; |
407 | while ((length < 0 || p < text + length) && *p) |
408 | { |
409 | gunichar wc = g_utf8_get_char (p); |
410 | |
411 | dir = gdk_unichar_direction (ch: wc); |
412 | |
413 | if (dir != PANGO_DIRECTION_NEUTRAL) |
414 | break; |
415 | |
416 | p = g_utf8_next_char (p); |
417 | } |
418 | |
419 | return dir; |
420 | } |
421 | |
422 | void |
423 | gdk_source_set_static_name_by_id (guint tag, |
424 | const char *name) |
425 | { |
426 | GSource *source; |
427 | |
428 | g_return_if_fail (tag > 0); |
429 | |
430 | source = g_main_context_find_source_by_id (NULL, source_id: tag); |
431 | if (source == NULL) |
432 | return; |
433 | |
434 | g_source_set_static_name (source, name); |
435 | } |
436 | |