1 | /* GTK - The GIMP Toolkit |
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 "gdk/gdk.h" |
28 | #include "gdk/gdk-private.h" |
29 | #include "gdk/gdkprofilerprivate.h" |
30 | #include "gsk/gskprivate.h" |
31 | #include "gsk/gskrendernodeprivate.h" |
32 | #include "gtknative.h" |
33 | |
34 | #include <locale.h> |
35 | |
36 | #include <stdio.h> |
37 | #include <stdlib.h> |
38 | #include <string.h> |
39 | #ifdef HAVE_UNISTD_H |
40 | #include <unistd.h> |
41 | #endif |
42 | #include <sys/types.h> /* For uid_t, gid_t */ |
43 | |
44 | #ifdef G_OS_WIN32 |
45 | #define STRICT |
46 | #include <windows.h> |
47 | #undef STRICT |
48 | #endif |
49 | |
50 | #include "gtkintl.h" |
51 | |
52 | #include "gtkbox.h" |
53 | #include "gtkdebug.h" |
54 | #include "gtkdropprivate.h" |
55 | #include "gtkmain.h" |
56 | #include "gtkmediafileprivate.h" |
57 | #include "gtkmodulesprivate.h" |
58 | #include "gtkprivate.h" |
59 | #include "gtkrecentmanager.h" |
60 | #include "gtksettingsprivate.h" |
61 | #include "gtktooltipprivate.h" |
62 | #include "gtkwidgetprivate.h" |
63 | #include "gtkwindowprivate.h" |
64 | #include "gtkwindowgroup.h" |
65 | #include "gtkprintbackendprivate.h" |
66 | #include "gtkimmoduleprivate.h" |
67 | #include "gtkroot.h" |
68 | #include "gtknative.h" |
69 | #include "gtkpopcountprivate.h" |
70 | |
71 | #include "inspector/init.h" |
72 | #include "inspector/window.h" |
73 | |
74 | #include "gdk/gdkeventsprivate.h" |
75 | #include "gdk/gdksurfaceprivate.h" |
76 | |
77 | #define GDK_ARRAY_ELEMENT_TYPE GtkWidget * |
78 | #define GDK_ARRAY_TYPE_NAME GtkWidgetStack |
79 | #define GDK_ARRAY_NAME gtk_widget_stack |
80 | #define GDK_ARRAY_FREE_FUNC g_object_unref |
81 | #define GDK_ARRAY_PREALLOC 16 |
82 | #include "gdk/gdkarrayimpl.c" |
83 | |
84 | static GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget); |
85 | |
86 | static int pre_initialized = FALSE; |
87 | static int gtk_initialized = FALSE; |
88 | static GList *current_events = NULL; |
89 | |
90 | typedef struct { |
91 | GdkDisplay *display; |
92 | guint flags; |
93 | } DisplayDebugFlags; |
94 | |
95 | #define N_DEBUG_DISPLAYS 4 |
96 | |
97 | DisplayDebugFlags debug_flags[N_DEBUG_DISPLAYS]; |
98 | /* This is a flag to speed up development builds. We set it to TRUE when |
99 | * any of the debug displays has debug flags >0, but we never set it back |
100 | * to FALSE. This way we don't need to call gtk_widget_get_display() in |
101 | * hot paths. */ |
102 | gboolean any_display_debug_flags_set = FALSE; |
103 | |
104 | GtkDebugFlags |
105 | gtk_get_display_debug_flags (GdkDisplay *display) |
106 | { |
107 | int i; |
108 | |
109 | if (display == NULL) |
110 | display = gdk_display_get_default (); |
111 | |
112 | for (i = 0; i < N_DEBUG_DISPLAYS; i++) |
113 | { |
114 | if (debug_flags[i].display == display) |
115 | return (GtkDebugFlags)debug_flags[i].flags; |
116 | } |
117 | |
118 | return 0; |
119 | } |
120 | |
121 | gboolean |
122 | gtk_get_any_display_debug_flag_set (void) |
123 | { |
124 | return any_display_debug_flags_set; |
125 | } |
126 | |
127 | void |
128 | gtk_set_display_debug_flags (GdkDisplay *display, |
129 | GtkDebugFlags flags) |
130 | { |
131 | int i; |
132 | |
133 | for (i = 0; i < N_DEBUG_DISPLAYS; i++) |
134 | { |
135 | if (debug_flags[i].display == NULL) |
136 | debug_flags[i].display = display; |
137 | |
138 | if (debug_flags[i].display == display) |
139 | { |
140 | debug_flags[i].flags = flags; |
141 | if (flags > 0) |
142 | any_display_debug_flags_set = TRUE; |
143 | |
144 | return; |
145 | } |
146 | } |
147 | } |
148 | |
149 | /** |
150 | * gtk_get_debug_flags: |
151 | * |
152 | * Returns the GTK debug flags that are currently active. |
153 | * |
154 | * This function is intended for GTK modules that want |
155 | * to adjust their debug output based on GTK debug flags. |
156 | * |
157 | * Returns: the GTK debug flags. |
158 | */ |
159 | GtkDebugFlags |
160 | gtk_get_debug_flags (void) |
161 | { |
162 | if (gtk_get_any_display_debug_flag_set ()) |
163 | return gtk_get_display_debug_flags (display: gdk_display_get_default ()); |
164 | |
165 | return 0; |
166 | } |
167 | |
168 | /** |
169 | * gtk_set_debug_flags: |
170 | * @flags: the debug flags to set |
171 | * |
172 | * Sets the GTK debug flags. |
173 | */ |
174 | void |
175 | gtk_set_debug_flags (GtkDebugFlags flags) |
176 | { |
177 | gtk_set_display_debug_flags (display: gdk_display_get_default (), flags); |
178 | } |
179 | |
180 | gboolean |
181 | gtk_simulate_touchscreen (void) |
182 | { |
183 | return (gtk_get_debug_flags () & GTK_DEBUG_TOUCHSCREEN) != 0; |
184 | } |
185 | |
186 | static const GdkDebugKey gtk_debug_keys[] = { |
187 | { "keybindings" , GTK_DEBUG_KEYBINDINGS, "Information about keyboard shortcuts" }, |
188 | { "modules" , GTK_DEBUG_MODULES, "Information about modules and extensions" }, |
189 | { "icontheme" , GTK_DEBUG_ICONTHEME, "Information about icon themes" }, |
190 | { "printing" , GTK_DEBUG_PRINTING, "Information about printing" }, |
191 | { "geometry" , GTK_DEBUG_GEOMETRY, "Information about size allocation" }, |
192 | { "size-request" , GTK_DEBUG_SIZE_REQUEST, "Information about size requests" }, |
193 | { "actions" , GTK_DEBUG_ACTIONS, "Information about actions and menu models" }, |
194 | { "constraints" , GTK_DEBUG_CONSTRAINTS, "Information from the constraints solver" }, |
195 | { "text" , GTK_DEBUG_TEXT, "Information about GtkTextView" }, |
196 | { "tree" , GTK_DEBUG_TREE, "Information about GtkTreeView" }, |
197 | { "layout" , GTK_DEBUG_LAYOUT, "Information from layout managers" }, |
198 | { "builder" , GTK_DEBUG_BUILDER, "Trace GtkBuilder operation" }, |
199 | { "builder-objects" , GTK_DEBUG_BUILDER_OBJECTS, "Log unused GtkBuilder objects" }, |
200 | { "no-css-cache" , GTK_DEBUG_NO_CSS_CACHE, "Disable style property cache" }, |
201 | { "interactive" , GTK_DEBUG_INTERACTIVE, "Enable the GTK inspector" , TRUE }, |
202 | { "touchscreen" , GTK_DEBUG_TOUCHSCREEN, "Pretend the pointer is a touchscreen" }, |
203 | { "snapshot" , GTK_DEBUG_SNAPSHOT, "Generate debug render nodes" }, |
204 | { "accessibility" , GTK_DEBUG_A11Y, "Information about accessibility state changes" }, |
205 | { "iconfallback" , GTK_DEBUG_ICONFALLBACK, "Information about icon fallback" }, |
206 | }; |
207 | |
208 | /* This checks to see if the process is running suid or sgid |
209 | * at the current time. If so, we don’t allow GTK to be initialized. |
210 | * This is meant to be a mild check - we only error out if we |
211 | * can prove the programmer is doing something wrong, not if |
212 | * they could be doing something wrong. For this reason, we |
213 | * don’t use issetugid() on BSD or prctl (PR_GET_DUMPABLE). |
214 | */ |
215 | static gboolean |
216 | check_setugid (void) |
217 | { |
218 | /* this isn't at all relevant on MS Windows and doesn't compile ... --hb */ |
219 | #ifndef G_OS_WIN32 |
220 | uid_t ruid, euid, suid; /* Real, effective and saved user ID's */ |
221 | gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */ |
222 | |
223 | #ifdef HAVE_GETRESUID |
224 | if (getresuid (ruid: &ruid, euid: &euid, suid: &suid) != 0 || |
225 | getresgid (rgid: &rgid, egid: &egid, sgid: &sgid) != 0) |
226 | #endif /* HAVE_GETRESUID */ |
227 | { |
228 | suid = ruid = getuid (); |
229 | sgid = rgid = getgid (); |
230 | euid = geteuid (); |
231 | egid = getegid (); |
232 | } |
233 | |
234 | if (ruid != euid || ruid != suid || |
235 | rgid != egid || rgid != sgid) |
236 | { |
237 | g_warning ("This process is currently running setuid or setgid.\n" |
238 | "This is not a supported use of GTK. You must create a helper\n" |
239 | "program instead. For further details, see:\n\n" |
240 | " http://www.gtk.org/setuid.html\n\n" |
241 | "Refusing to initialize GTK." ); |
242 | exit (status: 1); |
243 | } |
244 | #endif |
245 | return TRUE; |
246 | } |
247 | |
248 | static gboolean do_setlocale = TRUE; |
249 | |
250 | /** |
251 | * gtk_disable_setlocale: |
252 | * |
253 | * Prevents [id@gtk_init] and [id@gtk_init_check] from automatically calling |
254 | * `setlocale (LC_ALL, "")`. |
255 | * |
256 | * You would want to use this function if you wanted to set the locale for |
257 | * your program to something other than the user’s locale, or if |
258 | * you wanted to set different values for different locale categories. |
259 | * |
260 | * Most programs should not need to call this function. |
261 | **/ |
262 | void |
263 | gtk_disable_setlocale (void) |
264 | { |
265 | if (pre_initialized) |
266 | g_warning ("gtk_disable_setlocale() must be called before gtk_init()" ); |
267 | |
268 | do_setlocale = FALSE; |
269 | } |
270 | |
271 | #ifdef G_PLATFORM_WIN32 |
272 | #undef gtk_init_check |
273 | #endif |
274 | |
275 | #ifdef G_OS_WIN32 |
276 | |
277 | static char *iso639_to_check = NULL; |
278 | static char *iso3166_to_check = NULL; |
279 | static char *script_to_check = NULL; |
280 | static gboolean setlocale_called = FALSE; |
281 | |
282 | static BOOL CALLBACK |
283 | enum_locale_proc (LPTSTR locale) |
284 | { |
285 | LCID lcid; |
286 | char iso639[10]; |
287 | char iso3166[10]; |
288 | char *endptr; |
289 | |
290 | |
291 | lcid = strtoul (locale, &endptr, 16); |
292 | if (*endptr == '\0' && |
293 | GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) && |
294 | GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) |
295 | { |
296 | if (strcmp (iso639, iso639_to_check) == 0 && |
297 | ((iso3166_to_check != NULL && |
298 | strcmp (iso3166, iso3166_to_check) == 0) || |
299 | (iso3166_to_check == NULL && |
300 | SUBLANGID (LANGIDFROMLCID (lcid)) == SUBLANG_DEFAULT))) |
301 | { |
302 | char language[100], country[100]; |
303 | |
304 | if (script_to_check != NULL) |
305 | { |
306 | /* If lcid is the "other" script for this language, |
307 | * return TRUE, i.e. continue looking. |
308 | */ |
309 | if (strcmp (script_to_check, "Latn" ) == 0) |
310 | { |
311 | switch (LANGIDFROMLCID (lcid)) |
312 | { |
313 | case MAKELANGID (LANG_AZERI, SUBLANG_AZERI_CYRILLIC): |
314 | return TRUE; |
315 | case MAKELANGID (LANG_UZBEK, SUBLANG_UZBEK_CYRILLIC): |
316 | return TRUE; |
317 | case MAKELANGID (LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC): |
318 | return TRUE; |
319 | case MAKELANGID (LANG_SERBIAN, 0x07): |
320 | /* Serbian in Bosnia and Herzegovina, Cyrillic */ |
321 | return TRUE; |
322 | default: |
323 | break; |
324 | } |
325 | } |
326 | else if (strcmp (script_to_check, "Cyrl" ) == 0) |
327 | { |
328 | switch (LANGIDFROMLCID (lcid)) |
329 | { |
330 | case MAKELANGID (LANG_AZERI, SUBLANG_AZERI_LATIN): |
331 | return TRUE; |
332 | case MAKELANGID (LANG_UZBEK, SUBLANG_UZBEK_LATIN): |
333 | return TRUE; |
334 | case MAKELANGID (LANG_SERBIAN, SUBLANG_SERBIAN_LATIN): |
335 | return TRUE; |
336 | case MAKELANGID (LANG_SERBIAN, 0x06): |
337 | /* Serbian in Bosnia and Herzegovina, Latin */ |
338 | return TRUE; |
339 | default: |
340 | break; |
341 | } |
342 | } |
343 | } |
344 | |
345 | SetThreadLocale (lcid); |
346 | |
347 | if (GetLocaleInfo (lcid, LOCALE_SENGLANGUAGE, language, sizeof (language)) && |
348 | GetLocaleInfo (lcid, LOCALE_SENGCOUNTRY, country, sizeof (country))) |
349 | { |
350 | char str[300]; |
351 | |
352 | strcpy (str, language); |
353 | strcat (str, "_" ); |
354 | strcat (str, country); |
355 | |
356 | if (setlocale (LC_ALL, str) != NULL) |
357 | setlocale_called = TRUE; |
358 | } |
359 | |
360 | return FALSE; |
361 | } |
362 | } |
363 | |
364 | return TRUE; |
365 | } |
366 | |
367 | #endif |
368 | |
369 | void |
370 | setlocale_initialization (void) |
371 | { |
372 | static gboolean initialized = FALSE; |
373 | |
374 | if (initialized) |
375 | return; |
376 | initialized = TRUE; |
377 | |
378 | if (do_setlocale) |
379 | { |
380 | #ifdef G_OS_WIN32 |
381 | /* If some of the POSIXish environment variables are set, set |
382 | * the Win32 thread locale correspondingly. |
383 | */ |
384 | char *p = getenv ("LC_ALL" ); |
385 | if (p == NULL) |
386 | p = getenv ("LANG" ); |
387 | |
388 | if (p != NULL) |
389 | { |
390 | p = g_strdup (p); |
391 | if (strcmp (p, "C" ) == 0) |
392 | SetThreadLocale (LOCALE_SYSTEM_DEFAULT); |
393 | else |
394 | { |
395 | /* Check if one of the supported locales match the |
396 | * environment variable. If so, use that locale. |
397 | */ |
398 | iso639_to_check = p; |
399 | iso3166_to_check = strchr (iso639_to_check, '_'); |
400 | if (iso3166_to_check != NULL) |
401 | { |
402 | *iso3166_to_check++ = '\0'; |
403 | |
404 | script_to_check = strchr (iso3166_to_check, '@'); |
405 | if (script_to_check != NULL) |
406 | *script_to_check++ = '\0'; |
407 | |
408 | /* Handle special cases. */ |
409 | |
410 | /* The standard code for Serbia and Montenegro was |
411 | * "CS", but MSFT uses for some reason "SP". By now |
412 | * (October 2006), SP has split into two, "RS" and |
413 | * "ME", but don't bother trying to handle those |
414 | * yet. Do handle the even older "YU", though. |
415 | */ |
416 | if (strcmp (iso3166_to_check, "CS" ) == 0 || |
417 | strcmp (iso3166_to_check, "YU" ) == 0) |
418 | iso3166_to_check = (char *) "SP" ; |
419 | } |
420 | else |
421 | { |
422 | script_to_check = strchr (iso639_to_check, '@'); |
423 | if (script_to_check != NULL) |
424 | *script_to_check++ = '\0'; |
425 | /* LANG_SERBIAN == LANG_CROATIAN, recognize just "sr" */ |
426 | if (strcmp (iso639_to_check, "sr" ) == 0) |
427 | iso3166_to_check = (char *) "SP" ; |
428 | } |
429 | |
430 | EnumSystemLocales (enum_locale_proc, LCID_SUPPORTED); |
431 | } |
432 | g_free (p); |
433 | } |
434 | if (!setlocale_called) |
435 | setlocale (LC_ALL, "" ); |
436 | #else |
437 | if (!setlocale (LC_ALL, locale: "" )) |
438 | g_warning ("Locale not supported by C library.\n\tUsing the fallback 'C' locale." ); |
439 | #endif |
440 | } |
441 | } |
442 | |
443 | /* Return TRUE if module_to_check causes version conflicts. |
444 | * If module_to_check is NULL, check the main module. |
445 | */ |
446 | static gboolean |
447 | _gtk_module_has_mixed_deps (GModule *module_to_check) |
448 | { |
449 | GModule *module; |
450 | gpointer func; |
451 | gboolean result; |
452 | |
453 | if (!module_to_check) |
454 | module = g_module_open (NULL, flags: 0); |
455 | else |
456 | module = module_to_check; |
457 | |
458 | if (g_module_symbol (module, symbol_name: "gtk_progress_get_type" , symbol: &func)) |
459 | result = TRUE; |
460 | else if (g_module_symbol (module, symbol_name: "gtk_misc_get_type" , symbol: &func)) |
461 | result = TRUE; |
462 | else |
463 | result = FALSE; |
464 | |
465 | if (!module_to_check) |
466 | g_module_close (module); |
467 | |
468 | return result; |
469 | } |
470 | |
471 | static void |
472 | do_pre_parse_initialization (void) |
473 | { |
474 | const char *env_string; |
475 | double slowdown; |
476 | |
477 | if (pre_initialized) |
478 | return; |
479 | |
480 | pre_initialized = TRUE; |
481 | |
482 | if (_gtk_module_has_mixed_deps (NULL)) |
483 | g_error ("GTK 2/3 symbols detected. Using GTK 2/3 and GTK 4 in the same process is not supported" ); |
484 | |
485 | gdk_pre_parse (); |
486 | |
487 | debug_flags[0].flags = gdk_parse_debug_var (variable: "GTK_DEBUG" , |
488 | keys: gtk_debug_keys, |
489 | G_N_ELEMENTS (gtk_debug_keys)); |
490 | any_display_debug_flags_set = debug_flags[0].flags > 0; |
491 | |
492 | env_string = g_getenv (variable: "GTK_SLOWDOWN" ); |
493 | if (env_string) |
494 | { |
495 | slowdown = g_ascii_strtod (nptr: env_string, NULL); |
496 | _gtk_set_slowdown (slowdown_factor: slowdown); |
497 | } |
498 | |
499 | /* Trigger fontconfig initialization early */ |
500 | pango_cairo_font_map_get_default (); |
501 | } |
502 | |
503 | static void |
504 | gettext_initialization (void) |
505 | { |
506 | setlocale_initialization (); |
507 | |
508 | bindtextdomain (GETTEXT_PACKAGE, dirname: _gtk_get_localedir ()); |
509 | bindtextdomain (GETTEXT_PACKAGE "-properties" , dirname: _gtk_get_localedir ()); |
510 | #ifdef HAVE_BIND_TEXTDOMAIN_CODESET |
511 | bind_textdomain_codeset (GETTEXT_PACKAGE, codeset: "UTF-8" ); |
512 | bind_textdomain_codeset (GETTEXT_PACKAGE "-properties" , codeset: "UTF-8" ); |
513 | #endif |
514 | } |
515 | |
516 | static void |
517 | default_display_notify_cb (GdkDisplayManager *dm) |
518 | { |
519 | debug_flags[0].display = gdk_display_get_default (); |
520 | } |
521 | |
522 | static void |
523 | do_post_parse_initialization (void) |
524 | { |
525 | GdkDisplayManager *display_manager; |
526 | gint64 before G_GNUC_UNUSED; |
527 | |
528 | if (gtk_initialized) |
529 | return; |
530 | |
531 | before = GDK_PROFILER_CURRENT_TIME; |
532 | |
533 | gettext_initialization (); |
534 | |
535 | #ifdef SIGPIPE |
536 | signal (SIGPIPE, SIG_IGN); |
537 | #endif |
538 | |
539 | gtk_widget_set_default_direction (dir: gtk_get_locale_direction ()); |
540 | |
541 | gdk_event_init_types (); |
542 | |
543 | gsk_ensure_resources (); |
544 | gsk_render_node_init_types (); |
545 | _gtk_ensure_resources (); |
546 | |
547 | gdk_profiler_end_mark (before, "basic initialization" , NULL); |
548 | |
549 | gtk_initialized = TRUE; |
550 | |
551 | before = GDK_PROFILER_CURRENT_TIME; |
552 | #ifdef G_OS_UNIX |
553 | gtk_print_backends_init (); |
554 | #endif |
555 | gtk_im_modules_init (); |
556 | gtk_media_file_extension_init (); |
557 | gdk_profiler_end_mark (before, "init modules" , NULL); |
558 | |
559 | before = GDK_PROFILER_CURRENT_TIME; |
560 | display_manager = gdk_display_manager_get (); |
561 | if (gdk_display_manager_get_default_display (manager: display_manager) != NULL) |
562 | default_display_notify_cb (dm: display_manager); |
563 | gdk_profiler_end_mark (before, "create display" , NULL); |
564 | |
565 | g_signal_connect (display_manager, "notify::default-display" , |
566 | G_CALLBACK (default_display_notify_cb), |
567 | NULL); |
568 | |
569 | gtk_inspector_register_extension (); |
570 | } |
571 | |
572 | #ifdef G_PLATFORM_WIN32 |
573 | #undef gtk_init_check |
574 | #endif |
575 | |
576 | /** |
577 | * gtk_init_check: |
578 | * |
579 | * This function does the same work as gtk_init() with only a single |
580 | * change: It does not terminate the program if the windowing system |
581 | * can’t be initialized. Instead it returns %FALSE on failure. |
582 | * |
583 | * This way the application can fall back to some other means of |
584 | * communication with the user - for example a curses or command line |
585 | * interface. |
586 | * |
587 | * Returns: %TRUE if the windowing system has been successfully |
588 | * initialized, %FALSE otherwise |
589 | */ |
590 | gboolean |
591 | gtk_init_check (void) |
592 | { |
593 | gboolean ret; |
594 | |
595 | if (gtk_initialized) |
596 | return TRUE; |
597 | |
598 | if (gdk_profiler_is_running ()) |
599 | g_info ("Profiling is active" ); |
600 | |
601 | gettext_initialization (); |
602 | |
603 | if (!check_setugid ()) |
604 | return FALSE; |
605 | |
606 | do_pre_parse_initialization (); |
607 | do_post_parse_initialization (); |
608 | |
609 | ret = gdk_display_open_default () != NULL; |
610 | |
611 | if (ret && (gtk_get_debug_flags () & GTK_DEBUG_INTERACTIVE)) |
612 | gtk_window_set_interactive_debugging (TRUE); |
613 | |
614 | return ret; |
615 | } |
616 | |
617 | #ifdef G_PLATFORM_WIN32 |
618 | #undef gtk_init |
619 | #endif |
620 | |
621 | /** |
622 | * gtk_init: |
623 | * |
624 | * Call this function before using any other GTK functions in your GUI |
625 | * applications. It will initialize everything needed to operate the |
626 | * toolkit. |
627 | * |
628 | * If you are using `GtkApplication`, you don't have to call gtk_init() |
629 | * or gtk_init_check(); the `GApplication::startup` handler |
630 | * does it for you. |
631 | * |
632 | * This function will terminate your program if it was unable to |
633 | * initialize the windowing system for some reason. If you want |
634 | * your program to fall back to a textual interface you want to |
635 | * call gtk_init_check() instead. |
636 | * |
637 | * GTK calls `signal (SIGPIPE, SIG_IGN)` |
638 | * during initialization, to ignore SIGPIPE signals, since these are |
639 | * almost never wanted in graphical applications. If you do need to |
640 | * handle SIGPIPE for some reason, reset the handler after gtk_init(), |
641 | * but notice that other libraries (e.g. libdbus or gvfs) might do |
642 | * similar things. |
643 | */ |
644 | void |
645 | gtk_init (void) |
646 | { |
647 | if (!gtk_init_check ()) |
648 | { |
649 | const char *display_name_arg = NULL; |
650 | if (display_name_arg == NULL) |
651 | display_name_arg = getenv (name: "DISPLAY" ); |
652 | g_warning ("cannot open display: %s" , display_name_arg ? display_name_arg : "" ); |
653 | exit (status: 1); |
654 | } |
655 | } |
656 | |
657 | #ifdef G_OS_WIN32 |
658 | |
659 | /* This is relevant when building with gcc for Windows (MinGW), |
660 | * where we want to be struct packing compatible with MSVC, |
661 | * i.e. use the -mms-bitfields switch. |
662 | * For Cygwin there should be no need to be compatible with MSVC, |
663 | * so no need to use G_PLATFORM_WIN32. |
664 | */ |
665 | |
666 | static void |
667 | check_sizeof_GtkWindow (size_t sizeof_GtkWindow) |
668 | { |
669 | if (sizeof_GtkWindow != sizeof (GtkWindow)) |
670 | g_error ("Incompatible build!\n" |
671 | "The code using GTK thinks GtkWindow is of different\n" |
672 | "size than it actually is in this build of GTK.\n" |
673 | "On Windows, this probably means that you have compiled\n" |
674 | "your code with gcc without the -mms-bitfields switch,\n" |
675 | "or that you are using an unsupported compiler." ); |
676 | } |
677 | |
678 | /* In GTK 2.0 the GtkWindow struct actually is the same size in |
679 | * gcc-compiled code on Win32 whether compiled with -fnative-struct or |
680 | * not. Unfortunately this wan’t noticed until after GTK 2.0.1. So, |
681 | * from GTK 2.0.2 on, check some other struct, too, where the use of |
682 | * -fnative-struct still matters. GtkBox is one such. |
683 | */ |
684 | static void |
685 | check_sizeof_GtkBox (size_t sizeof_GtkBox) |
686 | { |
687 | if (sizeof_GtkBox != sizeof (GtkBox)) |
688 | g_error ("Incompatible build!\n" |
689 | "The code using GTK thinks GtkBox is of different\n" |
690 | "size than it actually is in this build of GTK.\n" |
691 | "On Windows, this probably means that you have compiled\n" |
692 | "your code with gcc without the -mms-bitfields switch,\n" |
693 | "or that you are using an unsupported compiler." ); |
694 | } |
695 | |
696 | /* These two functions might get more checks added later, thus pass |
697 | * in the number of extra args. |
698 | */ |
699 | void |
700 | gtk_init_abi_check (int num_checks, size_t sizeof_GtkWindow, size_t sizeof_GtkBox) |
701 | { |
702 | check_sizeof_GtkWindow (sizeof_GtkWindow); |
703 | if (num_checks >= 2) |
704 | check_sizeof_GtkBox (sizeof_GtkBox); |
705 | gtk_init (); |
706 | } |
707 | |
708 | gboolean |
709 | gtk_init_check_abi_check (int num_checks, size_t sizeof_GtkWindow, size_t sizeof_GtkBox) |
710 | { |
711 | check_sizeof_GtkWindow (sizeof_GtkWindow); |
712 | if (num_checks >= 2) |
713 | check_sizeof_GtkBox (sizeof_GtkBox); |
714 | return gtk_init_check (); |
715 | } |
716 | |
717 | #endif |
718 | |
719 | /** |
720 | * gtk_is_initialized: |
721 | * |
722 | * Use this function to check if GTK has been initialized with gtk_init() |
723 | * or gtk_init_check(). |
724 | * |
725 | * Returns: the initialization status |
726 | */ |
727 | gboolean |
728 | gtk_is_initialized (void) |
729 | { |
730 | return gtk_initialized; |
731 | } |
732 | |
733 | |
734 | /** |
735 | * gtk_get_locale_direction: |
736 | * |
737 | * Get the direction of the current locale. This is the expected |
738 | * reading direction for text and UI. |
739 | * |
740 | * This function depends on the current locale being set with |
741 | * setlocale() and will default to setting the %GTK_TEXT_DIR_LTR |
742 | * direction otherwise. %GTK_TEXT_DIR_NONE will never be returned. |
743 | * |
744 | * GTK sets the default text direction according to the locale |
745 | * during gtk_init(), and you should normally use |
746 | * gtk_widget_get_direction() or gtk_widget_get_default_direction() |
747 | * to obtain the current direction. |
748 | * |
749 | * This function is only needed rare cases when the locale is |
750 | * changed after GTK has already been initialized. In this case, |
751 | * you can use it to update the default text direction as follows: |
752 | * |
753 | * |[<!-- language="C" --> |
754 | * #include <locale.h> |
755 | * |
756 | * static void |
757 | * update_locale (const char *new_locale) |
758 | * { |
759 | * setlocale (LC_ALL, new_locale); |
760 | * GtkTextDirection direction = gtk_get_locale_direction (); |
761 | * gtk_widget_set_default_direction (direction); |
762 | * } |
763 | * ]| |
764 | * |
765 | * Returns: the `GtkTextDirection` of the current locale |
766 | */ |
767 | GtkTextDirection |
768 | gtk_get_locale_direction (void) |
769 | { |
770 | /* Translate to default:RTL if you want your widgets |
771 | * to be RTL, otherwise translate to default:LTR. |
772 | * Do *not* translate it to "predefinito:LTR", if it |
773 | * it isn't default:LTR or default:RTL it will not work |
774 | */ |
775 | char *e = _("default:LTR" ); |
776 | GtkTextDirection dir = GTK_TEXT_DIR_LTR; |
777 | |
778 | if (g_strcmp0 (str1: e, str2: "default:RTL" ) == 0) |
779 | dir = GTK_TEXT_DIR_RTL; |
780 | else if (g_strcmp0 (str1: e, str2: "default:LTR" ) != 0) |
781 | g_warning ("Whoever translated default:LTR did so wrongly. Defaulting to LTR." ); |
782 | |
783 | return dir; |
784 | } |
785 | |
786 | /** |
787 | * gtk_get_default_language: |
788 | * |
789 | * Returns the `PangoLanguage` for the default language |
790 | * currently in effect. |
791 | * |
792 | * Note that this can change over the life of an |
793 | * application. |
794 | * |
795 | * The default language is derived from the current |
796 | * locale. It determines, for example, whether GTK uses |
797 | * the right-to-left or left-to-right text direction. |
798 | * |
799 | * This function is equivalent to |
800 | * [func@Pango.Language.get_default]. |
801 | * See that function for details. |
802 | * |
803 | * Returns: (transfer none): the default language as a |
804 | * `PangoLanguage` |
805 | */ |
806 | PangoLanguage * |
807 | gtk_get_default_language (void) |
808 | { |
809 | return pango_language_get_default (); |
810 | } |
811 | |
812 | typedef struct { |
813 | GMainLoop *store_loop; |
814 | guint n_clipboards; |
815 | guint timeout_id; |
816 | } ClipboardStore; |
817 | |
818 | static void |
819 | clipboard_store_finished (GObject *source, |
820 | GAsyncResult *result, |
821 | gpointer data) |
822 | { |
823 | ClipboardStore *store; |
824 | GError *error = NULL; |
825 | |
826 | if (!gdk_clipboard_store_finish (GDK_CLIPBOARD (source), result, error: &error)) |
827 | { |
828 | if (g_error_matches (error, G_IO_ERROR, code: G_IO_ERROR_CANCELLED)) |
829 | { |
830 | g_error_free (error); |
831 | return; |
832 | } |
833 | |
834 | g_error_free (error); |
835 | } |
836 | |
837 | store = data; |
838 | store->n_clipboards--; |
839 | if (store->n_clipboards == 0) |
840 | g_main_loop_quit (loop: store->store_loop); |
841 | } |
842 | |
843 | static gboolean |
844 | sync_timed_out_cb (ClipboardStore *store) |
845 | { |
846 | store->timeout_id = 0; |
847 | g_main_loop_quit (loop: store->store_loop); |
848 | return G_SOURCE_REMOVE; |
849 | } |
850 | |
851 | void |
852 | gtk_main_sync (void) |
853 | { |
854 | ClipboardStore store = { NULL, }; |
855 | GSList *displays, *l; |
856 | GCancellable *cancel; |
857 | |
858 | /* Try storing all clipboard data we have */ |
859 | displays = gdk_display_manager_list_displays (manager: gdk_display_manager_get ()); |
860 | if (displays == NULL) |
861 | return; |
862 | |
863 | cancel = g_cancellable_new (); |
864 | |
865 | for (l = displays; l; l = l->next) |
866 | { |
867 | GdkDisplay *display = l->data; |
868 | GdkClipboard *clipboard = gdk_display_get_clipboard (display); |
869 | |
870 | gdk_clipboard_store_async (clipboard, |
871 | G_PRIORITY_HIGH, |
872 | cancellable: cancel, |
873 | callback: clipboard_store_finished, |
874 | user_data: &store); |
875 | store.n_clipboards++; |
876 | } |
877 | g_slist_free (list: displays); |
878 | |
879 | store.store_loop = g_main_loop_new (NULL, TRUE); |
880 | store.timeout_id = g_timeout_add_seconds (interval: 10, function: (GSourceFunc) sync_timed_out_cb, data: &store); |
881 | gdk_source_set_static_name_by_id (tag: store.timeout_id, name: "[gtk] gtk_main_sync clipboard store timeout" ); |
882 | |
883 | if (g_main_loop_is_running (loop: store.store_loop)) |
884 | g_main_loop_run (loop: store.store_loop); |
885 | |
886 | g_cancellable_cancel (cancellable: cancel); |
887 | g_object_unref (object: cancel); |
888 | g_clear_handle_id (&store.timeout_id, g_source_remove); |
889 | g_clear_pointer (&store.store_loop, g_main_loop_unref); |
890 | |
891 | /* Synchronize the recent manager singleton */ |
892 | _gtk_recent_manager_sync (); |
893 | } |
894 | |
895 | static GdkEvent * |
896 | rewrite_event_for_surface (GdkEvent *event, |
897 | GdkSurface *new_surface) |
898 | { |
899 | GdkEventType type; |
900 | double x, y; |
901 | double dx, dy; |
902 | |
903 | type = gdk_event_get_event_type (event); |
904 | |
905 | switch ((guint) type) |
906 | { |
907 | case GDK_BUTTON_PRESS: |
908 | case GDK_BUTTON_RELEASE: |
909 | case GDK_MOTION_NOTIFY: |
910 | case GDK_TOUCH_BEGIN: |
911 | case GDK_TOUCH_UPDATE: |
912 | case GDK_TOUCH_END: |
913 | case GDK_TOUCH_CANCEL: |
914 | case GDK_TOUCHPAD_SWIPE: |
915 | case GDK_TOUCHPAD_PINCH: |
916 | case GDK_TOUCHPAD_HOLD: |
917 | gdk_event_get_position (event, x: &x, y: &y); |
918 | gdk_surface_translate_coordinates (from: gdk_event_get_surface (event), to: new_surface, x: &x, y: &y); |
919 | break; |
920 | default: |
921 | x = y = 0; |
922 | break; |
923 | } |
924 | |
925 | switch ((guint) type) |
926 | { |
927 | case GDK_BUTTON_PRESS: |
928 | case GDK_BUTTON_RELEASE: |
929 | return gdk_button_event_new (type, |
930 | surface: new_surface, |
931 | device: gdk_event_get_device (event), |
932 | tool: gdk_event_get_device_tool (event), |
933 | time: gdk_event_get_time (event), |
934 | state: gdk_event_get_modifier_state (event), |
935 | button: gdk_button_event_get_button (event), |
936 | x, y, |
937 | axes: gdk_event_dup_axes (event)); |
938 | case GDK_MOTION_NOTIFY: |
939 | return gdk_motion_event_new (surface: new_surface, |
940 | device: gdk_event_get_device (event), |
941 | tool: gdk_event_get_device_tool (event), |
942 | time: gdk_event_get_time (event), |
943 | state: gdk_event_get_modifier_state (event), |
944 | x, y, |
945 | axes: gdk_event_dup_axes (event)); |
946 | case GDK_TOUCH_BEGIN: |
947 | case GDK_TOUCH_UPDATE: |
948 | case GDK_TOUCH_END: |
949 | case GDK_TOUCH_CANCEL: |
950 | return gdk_touch_event_new (type, |
951 | sequence: gdk_event_get_event_sequence (event), |
952 | surface: new_surface, |
953 | device: gdk_event_get_device (event), |
954 | time: gdk_event_get_time (event), |
955 | state: gdk_event_get_modifier_state (event), |
956 | x, y, |
957 | axes: gdk_event_dup_axes (event), |
958 | emulating: gdk_touch_event_get_emulating_pointer (event)); |
959 | case GDK_TOUCHPAD_SWIPE: |
960 | gdk_touchpad_event_get_deltas (event, dx: &dx, dy: &dy); |
961 | return gdk_touchpad_event_new_swipe (surface: new_surface, |
962 | sequence: gdk_event_get_event_sequence (event), |
963 | device: gdk_event_get_device (event), |
964 | time: gdk_event_get_time (event), |
965 | state: gdk_event_get_modifier_state (event), |
966 | phase: gdk_touchpad_event_get_gesture_phase (event), |
967 | x, y, |
968 | n_fingers: gdk_touchpad_event_get_n_fingers (event), |
969 | dx, dy); |
970 | case GDK_TOUCHPAD_PINCH: |
971 | gdk_touchpad_event_get_deltas (event, dx: &dx, dy: &dy); |
972 | return gdk_touchpad_event_new_pinch (surface: new_surface, |
973 | sequence: gdk_event_get_event_sequence (event), |
974 | device: gdk_event_get_device (event), |
975 | time: gdk_event_get_time (event), |
976 | state: gdk_event_get_modifier_state (event), |
977 | phase: gdk_touchpad_event_get_gesture_phase (event), |
978 | x, y, |
979 | n_fingers: gdk_touchpad_event_get_n_fingers (event), |
980 | dx, dy, |
981 | scale: gdk_touchpad_event_get_pinch_scale (event), |
982 | angle_delta: gdk_touchpad_event_get_pinch_angle_delta (event)); |
983 | case GDK_TOUCHPAD_HOLD: |
984 | return gdk_touchpad_event_new_hold (surface: new_surface, |
985 | device: gdk_event_get_device (event), |
986 | time: gdk_event_get_time (event), |
987 | state: gdk_event_get_modifier_state (event), |
988 | phase: gdk_touchpad_event_get_gesture_phase (event), |
989 | x, y, |
990 | n_fingers: gdk_touchpad_event_get_n_fingers (event)); |
991 | default: |
992 | break; |
993 | } |
994 | |
995 | return NULL; |
996 | } |
997 | |
998 | /* If there is a pointer or keyboard grab in effect with owner_events = TRUE, |
999 | * then what X11 does is deliver the event normally if it was going to this |
1000 | * client, otherwise, delivers it in terms of the grab surface. This function |
1001 | * rewrites events to the effect that events going to the same window group |
1002 | * are delivered normally, otherwise, the event is delivered in terms of the |
1003 | * grab window. |
1004 | */ |
1005 | static GdkEvent * |
1006 | rewrite_event_for_grabs (GdkEvent *event) |
1007 | { |
1008 | GdkSurface *grab_surface; |
1009 | GtkWidget *event_widget, *grab_widget; |
1010 | gboolean owner_events; |
1011 | GdkDisplay *display; |
1012 | GdkDevice *device; |
1013 | |
1014 | switch ((guint) gdk_event_get_event_type (event)) |
1015 | { |
1016 | case GDK_SCROLL: |
1017 | case GDK_BUTTON_PRESS: |
1018 | case GDK_BUTTON_RELEASE: |
1019 | case GDK_MOTION_NOTIFY: |
1020 | case GDK_PROXIMITY_IN: |
1021 | case GDK_PROXIMITY_OUT: |
1022 | case GDK_KEY_PRESS: |
1023 | case GDK_KEY_RELEASE: |
1024 | case GDK_TOUCH_BEGIN: |
1025 | case GDK_TOUCH_UPDATE: |
1026 | case GDK_TOUCH_END: |
1027 | case GDK_TOUCH_CANCEL: |
1028 | case GDK_TOUCHPAD_SWIPE: |
1029 | case GDK_TOUCHPAD_PINCH: |
1030 | case GDK_TOUCHPAD_HOLD: |
1031 | display = gdk_event_get_display (event); |
1032 | device = gdk_event_get_device (event); |
1033 | |
1034 | if (!gdk_device_grab_info (display, device, grab_surface: &grab_surface, owner_events: &owner_events) || |
1035 | !owner_events) |
1036 | return NULL; |
1037 | break; |
1038 | default: |
1039 | return NULL; |
1040 | } |
1041 | |
1042 | event_widget = gtk_get_event_widget (event); |
1043 | grab_widget = GTK_WIDGET (gtk_native_get_for_surface (grab_surface)); |
1044 | |
1045 | if (grab_widget && |
1046 | gtk_main_get_window_group (widget: grab_widget) != gtk_main_get_window_group (widget: event_widget)) |
1047 | return rewrite_event_for_surface (event, new_surface: grab_surface); |
1048 | else |
1049 | return NULL; |
1050 | } |
1051 | |
1052 | static GdkEvent * |
1053 | rewrite_event_for_toplevel (GdkEvent *event) |
1054 | { |
1055 | GdkSurface *surface; |
1056 | GdkEventType event_type; |
1057 | GdkTranslatedKey *key, *key_no_lock; |
1058 | |
1059 | surface = gdk_event_get_surface (event); |
1060 | if (!surface->parent) |
1061 | return NULL; |
1062 | |
1063 | event_type = gdk_event_get_event_type (event); |
1064 | if (event_type != GDK_KEY_PRESS && |
1065 | event_type != GDK_KEY_RELEASE) |
1066 | return NULL; |
1067 | |
1068 | while (surface->parent) |
1069 | surface = surface->parent; |
1070 | |
1071 | key = gdk_key_event_get_translated_key (event, FALSE); |
1072 | key_no_lock = gdk_key_event_get_translated_key (event, TRUE); |
1073 | |
1074 | return gdk_key_event_new (type: gdk_event_get_event_type (event), |
1075 | surface, |
1076 | device: gdk_event_get_device (event), |
1077 | time: gdk_event_get_time (event), |
1078 | keycode: gdk_key_event_get_keycode (event), |
1079 | modifiers: gdk_event_get_modifier_state (event), |
1080 | is_modifier: gdk_key_event_is_modifier (event), |
1081 | translated: key, no_lock: key_no_lock); |
1082 | } |
1083 | |
1084 | static gboolean |
1085 | translate_coordinates (double event_x, |
1086 | double event_y, |
1087 | double *x, |
1088 | double *y, |
1089 | GtkWidget *widget) |
1090 | { |
1091 | GtkNative *native; |
1092 | graphene_point_t p; |
1093 | |
1094 | *x = *y = 0; |
1095 | native = gtk_widget_get_native (widget); |
1096 | |
1097 | if (!gtk_widget_compute_point (GTK_WIDGET (native), |
1098 | target: widget, |
1099 | point: &GRAPHENE_POINT_INIT (event_x, event_y), |
1100 | out_point: &p)) |
1101 | return FALSE; |
1102 | |
1103 | *x = p.x; |
1104 | *y = p.y; |
1105 | |
1106 | return TRUE; |
1107 | } |
1108 | |
1109 | void |
1110 | gtk_synthesize_crossing_events (GtkRoot *toplevel, |
1111 | GtkCrossingType crossing_type, |
1112 | GtkWidget *old_target, |
1113 | GtkWidget *new_target, |
1114 | double surface_x, |
1115 | double surface_y, |
1116 | GdkCrossingMode mode, |
1117 | GdkDrop *drop) |
1118 | { |
1119 | GtkCrossingData crossing; |
1120 | GtkWidget *ancestor; |
1121 | GtkWidget *widget; |
1122 | double x, y; |
1123 | GtkWidget *prev; |
1124 | gboolean seen_ancestor; |
1125 | GtkWidgetStack target_array; |
1126 | int i; |
1127 | |
1128 | if (old_target == new_target) |
1129 | return; |
1130 | |
1131 | if (old_target && new_target) |
1132 | ancestor = gtk_widget_common_ancestor (widget_a: old_target, widget_b: new_target); |
1133 | else |
1134 | ancestor = NULL; |
1135 | |
1136 | crossing.type = crossing_type; |
1137 | crossing.mode = mode; |
1138 | crossing.old_target = old_target ? g_object_ref (old_target) : NULL; |
1139 | crossing.old_descendent = NULL; |
1140 | crossing.new_target = new_target ? g_object_ref (new_target) : NULL; |
1141 | crossing.new_descendent = NULL; |
1142 | crossing.drop = drop; |
1143 | |
1144 | crossing.direction = GTK_CROSSING_OUT; |
1145 | |
1146 | prev = NULL; |
1147 | seen_ancestor = FALSE; |
1148 | widget = old_target; |
1149 | while (widget) |
1150 | { |
1151 | crossing.old_descendent = prev; |
1152 | if (seen_ancestor) |
1153 | { |
1154 | crossing.new_descendent = new_target ? prev : NULL; |
1155 | } |
1156 | else if (widget == ancestor) |
1157 | { |
1158 | GtkWidget *w; |
1159 | |
1160 | crossing.new_descendent = NULL; |
1161 | for (w = new_target; w != ancestor; w = _gtk_widget_get_parent (widget: w)) |
1162 | crossing.new_descendent = w; |
1163 | |
1164 | seen_ancestor = TRUE; |
1165 | } |
1166 | else |
1167 | { |
1168 | crossing.new_descendent = NULL; |
1169 | } |
1170 | check_crossing_invariants (widget, crossing: &crossing); |
1171 | translate_coordinates (event_x: surface_x, event_y: surface_y, x: &x, y: &y, widget); |
1172 | gtk_widget_handle_crossing (widget, crossing: &crossing, x, y); |
1173 | if (crossing_type == GTK_CROSSING_POINTER) |
1174 | gtk_widget_unset_state_flags (widget, flags: GTK_STATE_FLAG_PRELIGHT); |
1175 | prev = widget; |
1176 | widget = _gtk_widget_get_parent (widget); |
1177 | } |
1178 | |
1179 | gtk_widget_stack_init (self: &target_array); |
1180 | for (widget = new_target; widget; widget = _gtk_widget_get_parent (widget)) |
1181 | gtk_widget_stack_append (self: &target_array, g_object_ref (widget)); |
1182 | |
1183 | crossing.direction = GTK_CROSSING_IN; |
1184 | |
1185 | seen_ancestor = FALSE; |
1186 | for (i = gtk_widget_stack_get_size (self: &target_array) - 1; i >= 0; i--) |
1187 | { |
1188 | widget = gtk_widget_stack_get (self: &target_array, pos: i); |
1189 | |
1190 | if (i < gtk_widget_stack_get_size (self: &target_array) - 1) |
1191 | crossing.new_descendent = gtk_widget_stack_get (self: &target_array, pos: i + 1); |
1192 | else |
1193 | crossing.new_descendent = NULL; |
1194 | |
1195 | if (seen_ancestor) |
1196 | { |
1197 | crossing.old_descendent = NULL; |
1198 | } |
1199 | else if (widget == ancestor) |
1200 | { |
1201 | GtkWidget *w; |
1202 | |
1203 | crossing.old_descendent = NULL; |
1204 | for (w = old_target; w != ancestor; w = _gtk_widget_get_parent (widget: w)) |
1205 | crossing.old_descendent = w; |
1206 | |
1207 | seen_ancestor = TRUE; |
1208 | } |
1209 | else |
1210 | { |
1211 | crossing.old_descendent = old_target ? crossing.new_descendent : NULL; |
1212 | } |
1213 | |
1214 | translate_coordinates (event_x: surface_x, event_y: surface_y, x: &x, y: &y, widget); |
1215 | gtk_widget_handle_crossing (widget, crossing: &crossing, x, y); |
1216 | if (crossing_type == GTK_CROSSING_POINTER) |
1217 | gtk_widget_set_state_flags (widget, flags: GTK_STATE_FLAG_PRELIGHT, FALSE); |
1218 | } |
1219 | |
1220 | g_clear_object (&crossing.old_target); |
1221 | g_clear_object (&crossing.new_target); |
1222 | |
1223 | gtk_widget_stack_clear (self: &target_array); |
1224 | } |
1225 | |
1226 | static GtkWidget * |
1227 | update_pointer_focus_state (GtkWindow *toplevel, |
1228 | GdkEvent *event, |
1229 | GtkWidget *new_target) |
1230 | { |
1231 | GtkWidget *old_target = NULL; |
1232 | GdkEventSequence *sequence; |
1233 | GdkDevice *device; |
1234 | double x, y; |
1235 | double nx, ny; |
1236 | |
1237 | device = gdk_event_get_device (event); |
1238 | sequence = gdk_event_get_event_sequence (event); |
1239 | old_target = gtk_window_lookup_pointer_focus_widget (window: toplevel, device, sequence); |
1240 | if (old_target == new_target) |
1241 | return old_target; |
1242 | |
1243 | gdk_event_get_position (event, x: &x, y: &y); |
1244 | gtk_native_get_surface_transform (self: GTK_NATIVE (ptr: toplevel), x: &nx, y: &ny); |
1245 | x -= nx; |
1246 | y -= ny; |
1247 | |
1248 | gtk_window_update_pointer_focus (window: toplevel, device, sequence, |
1249 | target: new_target, x, y); |
1250 | |
1251 | return old_target; |
1252 | } |
1253 | |
1254 | static gboolean |
1255 | is_pointing_event (GdkEvent *event) |
1256 | { |
1257 | switch ((guint) gdk_event_get_event_type (event)) |
1258 | { |
1259 | case GDK_MOTION_NOTIFY: |
1260 | case GDK_ENTER_NOTIFY: |
1261 | case GDK_LEAVE_NOTIFY: |
1262 | case GDK_BUTTON_PRESS: |
1263 | case GDK_BUTTON_RELEASE: |
1264 | case GDK_SCROLL: |
1265 | case GDK_TOUCH_BEGIN: |
1266 | case GDK_TOUCH_UPDATE: |
1267 | case GDK_TOUCH_END: |
1268 | case GDK_TOUCH_CANCEL: |
1269 | case GDK_TOUCHPAD_PINCH: |
1270 | case GDK_TOUCHPAD_SWIPE: |
1271 | case GDK_TOUCHPAD_HOLD: |
1272 | case GDK_DRAG_ENTER: |
1273 | case GDK_DRAG_LEAVE: |
1274 | case GDK_DRAG_MOTION: |
1275 | case GDK_DROP_START: |
1276 | return TRUE; |
1277 | |
1278 | case GDK_GRAB_BROKEN: |
1279 | return gdk_device_get_source (device: gdk_event_get_device (event)) != GDK_SOURCE_KEYBOARD; |
1280 | |
1281 | default: |
1282 | return FALSE; |
1283 | } |
1284 | } |
1285 | |
1286 | static gboolean |
1287 | is_key_event (GdkEvent *event) |
1288 | { |
1289 | switch ((guint) gdk_event_get_event_type (event)) |
1290 | { |
1291 | case GDK_KEY_PRESS: |
1292 | case GDK_KEY_RELEASE: |
1293 | return TRUE; |
1294 | break; |
1295 | case GDK_GRAB_BROKEN: |
1296 | return gdk_device_get_source (device: gdk_event_get_device (event)) == GDK_SOURCE_KEYBOARD; |
1297 | default: |
1298 | return FALSE; |
1299 | } |
1300 | } |
1301 | |
1302 | static inline void |
1303 | set_widget_active_state (GtkWidget *target, |
1304 | const gboolean is_active) |
1305 | { |
1306 | GtkWidget *w; |
1307 | |
1308 | w = target; |
1309 | while (w) |
1310 | { |
1311 | gtk_widget_set_active_state (widget: w, active: is_active); |
1312 | w = _gtk_widget_get_parent (widget: w); |
1313 | } |
1314 | } |
1315 | |
1316 | static GtkWidget * |
1317 | handle_pointing_event (GdkEvent *event) |
1318 | { |
1319 | GtkWidget *target = NULL, *old_target = NULL, *event_widget; |
1320 | GtkWindow *toplevel; |
1321 | GdkEventSequence *sequence; |
1322 | GdkDevice *device; |
1323 | double x, y; |
1324 | double native_x, native_y; |
1325 | GtkWidget *native; |
1326 | GdkEventType type; |
1327 | gboolean has_implicit; |
1328 | GdkModifierType modifiers; |
1329 | |
1330 | event_widget = gtk_get_event_widget (event); |
1331 | device = gdk_event_get_device (event); |
1332 | gdk_event_get_position (event, x: &x, y: &y); |
1333 | |
1334 | toplevel = GTK_WINDOW (gtk_widget_get_root (event_widget)); |
1335 | native = GTK_WIDGET (gtk_widget_get_native (event_widget)); |
1336 | |
1337 | gtk_native_get_surface_transform (self: GTK_NATIVE (ptr: native), x: &native_x, y: &native_y); |
1338 | x -= native_x; |
1339 | y -= native_y; |
1340 | |
1341 | type = gdk_event_get_event_type (event); |
1342 | sequence = gdk_event_get_event_sequence (event); |
1343 | |
1344 | if (type == GDK_SCROLL && |
1345 | (gdk_device_get_source (device) == GDK_SOURCE_TOUCHPAD || |
1346 | gdk_device_get_source (device) == GDK_SOURCE_TRACKPOINT || |
1347 | gdk_device_get_source (device) == GDK_SOURCE_MOUSE)) |
1348 | { |
1349 | /* A bit of a kludge, resolve target lookups for scrolling devices |
1350 | * on the seat pointer. |
1351 | */ |
1352 | device = gdk_seat_get_pointer (seat: gdk_event_get_seat (event)); |
1353 | } |
1354 | else if (type == GDK_TOUCHPAD_PINCH || |
1355 | type == GDK_TOUCHPAD_SWIPE || |
1356 | type == GDK_TOUCHPAD_HOLD) |
1357 | { |
1358 | /* Another bit of a kludge, touchpad gesture sequences do not |
1359 | * reflect on the pointer focus lookup. |
1360 | */ |
1361 | sequence = NULL; |
1362 | } |
1363 | |
1364 | switch ((guint) type) |
1365 | { |
1366 | case GDK_LEAVE_NOTIFY: |
1367 | if (gdk_crossing_event_get_mode (event) == GDK_CROSSING_GRAB) |
1368 | { |
1369 | GtkWidget *grab_widget; |
1370 | |
1371 | grab_widget = |
1372 | gtk_window_lookup_pointer_focus_implicit_grab (window: toplevel, |
1373 | device, |
1374 | sequence); |
1375 | if (grab_widget) |
1376 | set_widget_active_state (target: grab_widget, FALSE); |
1377 | } |
1378 | |
1379 | old_target = update_pointer_focus_state (toplevel, event, NULL); |
1380 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: toplevel), crossing_type: GTK_CROSSING_POINTER, old_target, NULL, |
1381 | surface_x: x, surface_y: y, mode: gdk_crossing_event_get_mode (event), NULL); |
1382 | break; |
1383 | case GDK_TOUCH_END: |
1384 | case GDK_TOUCH_CANCEL: |
1385 | old_target = update_pointer_focus_state (toplevel, event, NULL); |
1386 | set_widget_active_state (target: old_target, FALSE); |
1387 | break; |
1388 | case GDK_DRAG_LEAVE: |
1389 | { |
1390 | GdkDrop *drop = gdk_dnd_event_get_drop (event); |
1391 | old_target = update_pointer_focus_state (toplevel, event, NULL); |
1392 | gtk_drop_begin_event (drop, event_type: GDK_DRAG_LEAVE); |
1393 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: toplevel), crossing_type: GTK_CROSSING_DROP, old_target, NULL, |
1394 | surface_x: x, surface_y: y, mode: GDK_CROSSING_NORMAL, drop); |
1395 | gtk_drop_end_event (drop); |
1396 | } |
1397 | break; |
1398 | case GDK_ENTER_NOTIFY: |
1399 | case GDK_DRAG_ENTER: |
1400 | case GDK_DRAG_MOTION: |
1401 | case GDK_DROP_START: |
1402 | case GDK_TOUCH_BEGIN: |
1403 | case GDK_TOUCH_UPDATE: |
1404 | case GDK_MOTION_NOTIFY: |
1405 | target = gtk_window_lookup_pointer_focus_implicit_grab (window: toplevel, device, sequence); |
1406 | |
1407 | if (!target) |
1408 | target = gtk_widget_pick (widget: native, x, y, flags: GTK_PICK_DEFAULT); |
1409 | |
1410 | if (!target) |
1411 | target = GTK_WIDGET (native); |
1412 | |
1413 | old_target = update_pointer_focus_state (toplevel, event, new_target: target); |
1414 | |
1415 | if (type == GDK_MOTION_NOTIFY || type == GDK_ENTER_NOTIFY) |
1416 | { |
1417 | if (!gtk_window_lookup_pointer_focus_implicit_grab (window: toplevel, device, |
1418 | sequence)) |
1419 | { |
1420 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: toplevel), crossing_type: GTK_CROSSING_POINTER, old_target, new_target: target, |
1421 | surface_x: x, surface_y: y, mode: GDK_CROSSING_NORMAL, NULL); |
1422 | } |
1423 | |
1424 | gtk_window_maybe_update_cursor (window: toplevel, NULL, device); |
1425 | } |
1426 | else if ((old_target != target) && |
1427 | (type == GDK_DRAG_ENTER || type == GDK_DRAG_MOTION || type == GDK_DROP_START)) |
1428 | { |
1429 | GdkDrop *drop = gdk_dnd_event_get_drop (event); |
1430 | gtk_drop_begin_event (drop, event_type: type); |
1431 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: toplevel), crossing_type: GTK_CROSSING_DROP, old_target, new_target: target, |
1432 | surface_x: x, surface_y: y, mode: GDK_CROSSING_NORMAL, drop: gdk_dnd_event_get_drop (event)); |
1433 | gtk_drop_end_event (drop); |
1434 | } |
1435 | else if (type == GDK_TOUCH_BEGIN) |
1436 | { |
1437 | gtk_window_set_pointer_focus_grab (window: toplevel, device, sequence, grab_widget: target); |
1438 | set_widget_active_state (target, TRUE); |
1439 | } |
1440 | |
1441 | /* Let it take the effective pointer focus anyway, as it may change due |
1442 | * to implicit grabs. |
1443 | */ |
1444 | target = NULL; |
1445 | break; |
1446 | case GDK_BUTTON_PRESS: |
1447 | case GDK_BUTTON_RELEASE: |
1448 | target = gtk_window_lookup_effective_pointer_focus_widget (window: toplevel, |
1449 | device, |
1450 | sequence); |
1451 | has_implicit = |
1452 | gtk_window_lookup_pointer_focus_implicit_grab (window: toplevel, |
1453 | device, |
1454 | sequence) != NULL; |
1455 | modifiers = gdk_event_get_modifier_state (event); |
1456 | |
1457 | if (type == GDK_BUTTON_RELEASE && |
1458 | gtk_popcount (modifiers & (GDK_BUTTON1_MASK | |
1459 | GDK_BUTTON2_MASK | |
1460 | GDK_BUTTON3_MASK | |
1461 | GDK_BUTTON4_MASK | |
1462 | GDK_BUTTON5_MASK)) == 1) |
1463 | { |
1464 | GtkWidget *new_target = gtk_widget_pick (widget: native, x, y, flags: GTK_PICK_DEFAULT); |
1465 | |
1466 | gtk_window_set_pointer_focus_grab (window: toplevel, device, sequence, NULL); |
1467 | |
1468 | if (new_target == NULL) |
1469 | new_target = GTK_WIDGET (toplevel); |
1470 | |
1471 | gtk_synthesize_crossing_events (toplevel: GTK_ROOT (ptr: toplevel), crossing_type: GTK_CROSSING_POINTER, old_target: target, new_target, |
1472 | surface_x: x, surface_y: y, mode: GDK_CROSSING_UNGRAB, NULL); |
1473 | gtk_window_maybe_update_cursor (window: toplevel, NULL, device); |
1474 | update_pointer_focus_state (toplevel, event, new_target); |
1475 | } |
1476 | else if (type == GDK_BUTTON_PRESS) |
1477 | { |
1478 | gtk_window_set_pointer_focus_grab (window: toplevel, device, |
1479 | sequence, grab_widget: target); |
1480 | } |
1481 | |
1482 | if (type == GDK_BUTTON_PRESS) |
1483 | set_widget_active_state (target, TRUE); |
1484 | else if (has_implicit) |
1485 | set_widget_active_state (target, FALSE); |
1486 | |
1487 | break; |
1488 | case GDK_SCROLL: |
1489 | case GDK_TOUCHPAD_PINCH: |
1490 | case GDK_TOUCHPAD_SWIPE: |
1491 | case GDK_TOUCHPAD_HOLD: |
1492 | break; |
1493 | case GDK_GRAB_BROKEN: |
1494 | if (gdk_grab_broken_event_get_implicit (event)) |
1495 | { |
1496 | target = gtk_window_lookup_effective_pointer_focus_widget (window: toplevel, |
1497 | device, |
1498 | sequence); |
1499 | set_widget_active_state (target, FALSE); |
1500 | } |
1501 | break; |
1502 | default: |
1503 | g_assert_not_reached (); |
1504 | } |
1505 | |
1506 | if (!target) |
1507 | target = gtk_window_lookup_effective_pointer_focus_widget (window: toplevel, |
1508 | device, |
1509 | sequence); |
1510 | return target ? target : old_target; |
1511 | } |
1512 | |
1513 | static GtkWidget * |
1514 | handle_key_event (GdkEvent *event) |
1515 | { |
1516 | GtkWidget *event_widget; |
1517 | GtkWidget *focus_widget; |
1518 | |
1519 | event_widget = gtk_get_event_widget (event); |
1520 | |
1521 | focus_widget = gtk_root_get_focus (self: gtk_widget_get_root (widget: event_widget)); |
1522 | return focus_widget ? focus_widget : event_widget; |
1523 | } |
1524 | |
1525 | static gboolean |
1526 | is_transient_for (GtkWindow *child, |
1527 | GtkWindow *parent) |
1528 | { |
1529 | GtkWindow *transient_for; |
1530 | |
1531 | transient_for = gtk_window_get_transient_for (window: child); |
1532 | |
1533 | while (transient_for) |
1534 | { |
1535 | if (transient_for == parent) |
1536 | return TRUE; |
1537 | |
1538 | transient_for = gtk_window_get_transient_for (window: transient_for); |
1539 | } |
1540 | |
1541 | return FALSE; |
1542 | } |
1543 | |
1544 | void |
1545 | gtk_main_do_event (GdkEvent *event) |
1546 | { |
1547 | GtkWidget *event_widget; |
1548 | GtkWidget *target_widget; |
1549 | GtkWidget *grab_widget = NULL; |
1550 | GtkWindowGroup *window_group; |
1551 | GdkEvent *rewritten_event = NULL; |
1552 | GList *tmp_list; |
1553 | |
1554 | if (gtk_inspector_handle_event (event)) |
1555 | return; |
1556 | |
1557 | /* Find the widget which got the event. We store the widget |
1558 | * in the user_data field of GdkSurface's. Ignore the event |
1559 | * if we don't have a widget for it. |
1560 | */ |
1561 | event_widget = gtk_get_event_widget (event); |
1562 | if (!event_widget) |
1563 | return; |
1564 | |
1565 | target_widget = event_widget; |
1566 | |
1567 | /* We propagate key events from the root, even if they are |
1568 | * delivered to a popup surface. |
1569 | * |
1570 | * If pointer or keyboard grabs are in effect, munge the events |
1571 | * so that each window group looks like a separate app. |
1572 | */ |
1573 | if (is_key_event (event)) |
1574 | rewritten_event = rewrite_event_for_toplevel (event); |
1575 | else |
1576 | rewritten_event = rewrite_event_for_grabs (event); |
1577 | if (rewritten_event) |
1578 | { |
1579 | event = rewritten_event; |
1580 | target_widget = gtk_get_event_widget (event); |
1581 | } |
1582 | |
1583 | /* Push the event onto a stack of current events for |
1584 | * gtk_current_event_get(). |
1585 | */ |
1586 | current_events = g_list_prepend (list: current_events, data: event); |
1587 | |
1588 | if (is_pointing_event (event)) |
1589 | { |
1590 | target_widget = handle_pointing_event (event); |
1591 | } |
1592 | else if (is_key_event (event)) |
1593 | { |
1594 | target_widget = handle_key_event (event); |
1595 | } |
1596 | |
1597 | if (!target_widget) |
1598 | goto cleanup; |
1599 | |
1600 | window_group = gtk_main_get_window_group (widget: target_widget); |
1601 | |
1602 | /* check whether there is a grab in effect... */ |
1603 | grab_widget = gtk_window_group_get_current_grab (window_group); |
1604 | |
1605 | /* If the grab widget is an ancestor of the event widget |
1606 | * then we send the event to the original event widget. |
1607 | * This is the key to implementing modality. This also applies |
1608 | * across windows that are directly or indirectly transient-for |
1609 | * the modal one. |
1610 | */ |
1611 | if (!grab_widget || |
1612 | ((gtk_widget_is_sensitive (widget: target_widget) || gdk_event_get_event_type (event) == GDK_SCROLL) && |
1613 | gtk_widget_is_ancestor (widget: target_widget, ancestor: grab_widget)) || |
1614 | (GTK_IS_WINDOW (grab_widget) && |
1615 | GTK_IS_WINDOW (event_widget) && |
1616 | grab_widget != event_widget && |
1617 | is_transient_for (GTK_WINDOW (event_widget), GTK_WINDOW (grab_widget)))) |
1618 | grab_widget = target_widget; |
1619 | |
1620 | g_object_ref (target_widget); |
1621 | |
1622 | /* Not all events get sent to the grabbing widget. |
1623 | * The delete, destroy, expose, focus change and resize |
1624 | * events still get sent to the event widget because |
1625 | * 1) these events have no meaning for the grabbing widget |
1626 | * and 2) redirecting these events to the grabbing widget |
1627 | * could cause the display to be messed up. |
1628 | * |
1629 | * Drag events are also not redirected, since it isn't |
1630 | * clear what the semantics of that would be. |
1631 | */ |
1632 | switch ((guint)gdk_event_get_event_type (event)) |
1633 | { |
1634 | case GDK_DELETE: |
1635 | if (!gtk_window_group_get_current_grab (window_group) || |
1636 | GTK_WIDGET (gtk_widget_get_root (gtk_window_group_get_current_grab (window_group))) == target_widget) |
1637 | { |
1638 | if (GTK_IS_WINDOW (target_widget) && |
1639 | !gtk_window_emit_close_request (GTK_WINDOW (target_widget))) |
1640 | gtk_window_destroy (GTK_WINDOW (target_widget)); |
1641 | } |
1642 | break; |
1643 | |
1644 | case GDK_FOCUS_CHANGE: |
1645 | { |
1646 | GtkWidget *root = GTK_WIDGET (gtk_widget_get_root (target_widget)); |
1647 | if (!_gtk_widget_captured_event (widget: root, event, target: root)) |
1648 | gtk_widget_event (widget: root, event, target: root); |
1649 | } |
1650 | break; |
1651 | |
1652 | case GDK_KEY_PRESS: |
1653 | case GDK_KEY_RELEASE: |
1654 | case GDK_SCROLL: |
1655 | case GDK_BUTTON_PRESS: |
1656 | case GDK_TOUCH_BEGIN: |
1657 | case GDK_MOTION_NOTIFY: |
1658 | case GDK_BUTTON_RELEASE: |
1659 | case GDK_PROXIMITY_IN: |
1660 | case GDK_PROXIMITY_OUT: |
1661 | case GDK_TOUCH_UPDATE: |
1662 | case GDK_TOUCH_END: |
1663 | case GDK_TOUCH_CANCEL: |
1664 | case GDK_TOUCHPAD_SWIPE: |
1665 | case GDK_TOUCHPAD_PINCH: |
1666 | case GDK_TOUCHPAD_HOLD: |
1667 | case GDK_PAD_BUTTON_PRESS: |
1668 | case GDK_PAD_BUTTON_RELEASE: |
1669 | case GDK_PAD_RING: |
1670 | case GDK_PAD_STRIP: |
1671 | case GDK_PAD_GROUP_MODE: |
1672 | case GDK_GRAB_BROKEN: |
1673 | gtk_propagate_event (widget: grab_widget, event); |
1674 | break; |
1675 | |
1676 | case GDK_ENTER_NOTIFY: |
1677 | case GDK_LEAVE_NOTIFY: |
1678 | case GDK_DRAG_ENTER: |
1679 | case GDK_DRAG_LEAVE: |
1680 | /* Crossing event propagation happens during picking */ |
1681 | break; |
1682 | |
1683 | case GDK_DRAG_MOTION: |
1684 | case GDK_DROP_START: |
1685 | { |
1686 | GdkDrop *drop = gdk_dnd_event_get_drop (event); |
1687 | gtk_drop_begin_event (drop, event_type: gdk_event_get_event_type (event)); |
1688 | gtk_propagate_event (widget: target_widget, event); |
1689 | gtk_drop_end_event (drop); |
1690 | } |
1691 | break; |
1692 | |
1693 | case GDK_EVENT_LAST: |
1694 | default: |
1695 | g_assert_not_reached (); |
1696 | break; |
1697 | } |
1698 | |
1699 | _gtk_tooltip_handle_event (target: target_widget, event); |
1700 | |
1701 | g_object_unref (object: target_widget); |
1702 | |
1703 | cleanup: |
1704 | tmp_list = current_events; |
1705 | current_events = g_list_remove_link (list: current_events, llink: tmp_list); |
1706 | g_list_free_1 (list: tmp_list); |
1707 | |
1708 | if (rewritten_event) |
1709 | gdk_event_unref (event: rewritten_event); |
1710 | } |
1711 | |
1712 | static GtkWindowGroup * |
1713 | gtk_main_get_window_group (GtkWidget *widget) |
1714 | { |
1715 | GtkWidget *toplevel = NULL; |
1716 | |
1717 | if (widget) |
1718 | toplevel = GTK_WIDGET (gtk_widget_get_root (widget)); |
1719 | |
1720 | if (GTK_IS_WINDOW (toplevel)) |
1721 | return gtk_window_get_group (GTK_WINDOW (toplevel)); |
1722 | else |
1723 | return gtk_window_get_group (NULL); |
1724 | } |
1725 | |
1726 | static void |
1727 | gtk_grab_notify (GtkWindowGroup *group, |
1728 | GtkWidget *old_grab_widget, |
1729 | GtkWidget *new_grab_widget, |
1730 | gboolean from_grab) |
1731 | { |
1732 | GList *toplevels; |
1733 | |
1734 | if (old_grab_widget == new_grab_widget) |
1735 | return; |
1736 | |
1737 | g_object_ref (group); |
1738 | |
1739 | toplevels = gtk_window_list_toplevels (); |
1740 | g_list_foreach (list: toplevels, func: (GFunc)g_object_ref, NULL); |
1741 | |
1742 | while (toplevels) |
1743 | { |
1744 | GtkWindow *toplevel = toplevels->data; |
1745 | toplevels = g_list_delete_link (list: toplevels, link_: toplevels); |
1746 | |
1747 | gtk_window_grab_notify (window: toplevel, |
1748 | old_grab_widget, |
1749 | new_grab_widget, |
1750 | from_grab); |
1751 | g_object_unref (object: toplevel); |
1752 | } |
1753 | |
1754 | g_object_unref (object: group); |
1755 | } |
1756 | |
1757 | /** |
1758 | * gtk_grab_add: (method) |
1759 | * @widget: The widget that grabs keyboard and pointer events |
1760 | * |
1761 | * Makes @widget the current grabbed widget. |
1762 | * |
1763 | * This means that interaction with other widgets in the same |
1764 | * application is blocked and mouse as well as keyboard events |
1765 | * are delivered to this widget. |
1766 | * |
1767 | * If @widget is not sensitive, it is not set as the current |
1768 | * grabbed widget and this function does nothing. |
1769 | */ |
1770 | void |
1771 | gtk_grab_add (GtkWidget *widget) |
1772 | { |
1773 | GtkWindowGroup *group; |
1774 | GtkWidget *old_grab_widget; |
1775 | |
1776 | g_return_if_fail (widget != NULL); |
1777 | |
1778 | if (!gtk_widget_has_grab (widget) && gtk_widget_is_sensitive (widget)) |
1779 | { |
1780 | _gtk_widget_set_has_grab (widget, TRUE); |
1781 | |
1782 | group = gtk_main_get_window_group (widget); |
1783 | |
1784 | old_grab_widget = gtk_window_group_get_current_grab (window_group: group); |
1785 | |
1786 | g_object_ref (widget); |
1787 | _gtk_window_group_add_grab (window_group: group, widget); |
1788 | |
1789 | gtk_grab_notify (group, old_grab_widget, new_grab_widget: widget, TRUE); |
1790 | } |
1791 | } |
1792 | |
1793 | /** |
1794 | * gtk_grab_remove: (method) |
1795 | * @widget: The widget which gives up the grab |
1796 | * |
1797 | * Removes the grab from the given widget. |
1798 | * |
1799 | * You have to pair calls to gtk_grab_add() and gtk_grab_remove(). |
1800 | * |
1801 | * If @widget does not have the grab, this function does nothing. |
1802 | */ |
1803 | void |
1804 | gtk_grab_remove (GtkWidget *widget) |
1805 | { |
1806 | GtkWindowGroup *group; |
1807 | GtkWidget *new_grab_widget; |
1808 | |
1809 | g_return_if_fail (widget != NULL); |
1810 | |
1811 | if (gtk_widget_has_grab (widget)) |
1812 | { |
1813 | _gtk_widget_set_has_grab (widget, FALSE); |
1814 | |
1815 | group = gtk_main_get_window_group (widget); |
1816 | _gtk_window_group_remove_grab (window_group: group, widget); |
1817 | new_grab_widget = gtk_window_group_get_current_grab (window_group: group); |
1818 | |
1819 | gtk_grab_notify (group, old_grab_widget: widget, new_grab_widget, FALSE); |
1820 | |
1821 | g_object_unref (object: widget); |
1822 | } |
1823 | } |
1824 | |
1825 | guint32 |
1826 | gtk_get_current_event_time (void) |
1827 | { |
1828 | if (current_events) |
1829 | return gdk_event_get_time (event: current_events->data); |
1830 | else |
1831 | return GDK_CURRENT_TIME; |
1832 | } |
1833 | |
1834 | /** |
1835 | * gtk_get_event_widget: |
1836 | * @event: a `GdkEvent` |
1837 | * |
1838 | * If @event is %NULL or the event was not associated with any widget, |
1839 | * returns %NULL, otherwise returns the widget that received the event |
1840 | * originally. |
1841 | * |
1842 | * Returns: (transfer none) (nullable): the widget that originally |
1843 | * received @event |
1844 | */ |
1845 | GtkWidget * |
1846 | gtk_get_event_widget (GdkEvent *event) |
1847 | { |
1848 | GdkSurface *surface; |
1849 | |
1850 | surface = gdk_event_get_surface (event); |
1851 | if (surface && !gdk_surface_is_destroyed (surface)) |
1852 | return GTK_WIDGET (gtk_native_get_for_surface (surface)); |
1853 | |
1854 | return NULL; |
1855 | } |
1856 | |
1857 | gboolean |
1858 | gtk_propagate_event_internal (GtkWidget *widget, |
1859 | GdkEvent *event, |
1860 | GtkWidget *topmost) |
1861 | { |
1862 | int handled_event = FALSE; |
1863 | GtkWidget *target = widget; |
1864 | GtkWidgetStack widget_array; |
1865 | int i; |
1866 | |
1867 | /* First, propagate event down */ |
1868 | gtk_widget_stack_init (self: &widget_array); |
1869 | gtk_widget_stack_append (self: &widget_array, g_object_ref (widget)); |
1870 | |
1871 | for (;;) |
1872 | { |
1873 | widget = _gtk_widget_get_parent (widget); |
1874 | if (!widget) |
1875 | break; |
1876 | |
1877 | gtk_widget_stack_append (self: &widget_array, g_object_ref (widget)); |
1878 | |
1879 | if (widget == topmost) |
1880 | break; |
1881 | } |
1882 | |
1883 | i = gtk_widget_stack_get_size (self: &widget_array) - 1; |
1884 | for (;;) |
1885 | { |
1886 | widget = gtk_widget_stack_get (self: &widget_array, pos: i); |
1887 | |
1888 | if (!_gtk_widget_is_sensitive (widget)) |
1889 | { |
1890 | /* stop propagating on SCROLL, but don't handle the event, so it |
1891 | * can propagate up again and reach its handling widget |
1892 | */ |
1893 | if (gdk_event_get_event_type (event) == GDK_SCROLL) |
1894 | break; |
1895 | else |
1896 | handled_event = TRUE; |
1897 | } |
1898 | else if (_gtk_widget_get_realized (widget)) |
1899 | handled_event = _gtk_widget_captured_event (widget, event, target); |
1900 | |
1901 | handled_event |= !_gtk_widget_get_realized (widget); |
1902 | |
1903 | if (handled_event) |
1904 | break; |
1905 | |
1906 | if (i == 0) |
1907 | break; |
1908 | |
1909 | i--; |
1910 | } |
1911 | |
1912 | /* If not yet handled, also propagate down */ |
1913 | if (!handled_event) |
1914 | { |
1915 | /* Propagate event up the widget tree so that |
1916 | * parents can see the button and motion |
1917 | * events of the children. |
1918 | */ |
1919 | for (i = 0; i < gtk_widget_stack_get_size (self: &widget_array); i++) |
1920 | { |
1921 | widget = gtk_widget_stack_get (self: &widget_array, pos: i); |
1922 | |
1923 | /* Scroll events are special cased here because it |
1924 | * feels wrong when scrolling a GtkViewport, say, |
1925 | * to have children of the viewport eat the scroll |
1926 | * event |
1927 | */ |
1928 | if (!_gtk_widget_is_sensitive (widget)) |
1929 | handled_event = gdk_event_get_event_type (event) != GDK_SCROLL; |
1930 | else if (_gtk_widget_get_realized (widget)) |
1931 | handled_event = gtk_widget_event (widget, event, target); |
1932 | |
1933 | handled_event |= !_gtk_widget_get_realized (widget); |
1934 | |
1935 | if (handled_event) |
1936 | break; |
1937 | } |
1938 | } |
1939 | |
1940 | gtk_widget_stack_clear (self: &widget_array); |
1941 | return handled_event; |
1942 | } |
1943 | |
1944 | /** |
1945 | * gtk_propagate_event: |
1946 | * @widget: a `GtkWidget` |
1947 | * @event: an event |
1948 | * |
1949 | * Sends an event to a widget, propagating the event to parent widgets |
1950 | * if the event remains unhandled. This function will emit the event |
1951 | * through all the hierarchy of @widget through all propagation phases. |
1952 | * |
1953 | * Events received by GTK from GDK normally begin at a `GtkRoot` widget. |
1954 | * Depending on the type of event, existence of modal dialogs, grabs, etc., |
1955 | * the event may be propagated; if so, this function is used. |
1956 | * |
1957 | * All that said, you most likely don’t want to use any of these |
1958 | * functions; synthesizing events is rarely needed. There are almost |
1959 | * certainly better ways to achieve your goals. For example, use |
1960 | * gtk_widget_queue_draw() instead |
1961 | * of making up expose events. |
1962 | * |
1963 | * Returns: %TRUE if the event was handled |
1964 | */ |
1965 | gboolean |
1966 | gtk_propagate_event (GtkWidget *widget, |
1967 | GdkEvent *event) |
1968 | { |
1969 | GtkWindowGroup *window_group; |
1970 | GtkWidget *event_widget, *topmost = NULL; |
1971 | |
1972 | g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE); |
1973 | g_return_val_if_fail (event != NULL, FALSE); |
1974 | |
1975 | event_widget = gtk_get_event_widget (event); |
1976 | window_group = gtk_main_get_window_group (widget: event_widget); |
1977 | |
1978 | /* check whether there is a grab in effect... */ |
1979 | topmost = gtk_window_group_get_current_grab (window_group); |
1980 | |
1981 | return gtk_propagate_event_internal (widget, event, topmost); |
1982 | } |
1983 | |