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 (&register_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