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