| /* GTK - The GIMP Toolkit |
| * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald |
| * |
| * This library is free software; you can redistribute it and/or |
| * modify it under the terms of the GNU Lesser General Public |
| * License as published by the Free Software Foundation; either |
| * version 2 of the License, or (at your option) any later version. |
| * |
| * This library is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| * Lesser General Public License for more details. |
| * |
| * You should have received a copy of the GNU Lesser General Public |
| * License along with this library. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| /* |
| * Modified by the GTK+ Team and others 1997-2000. See the AUTHORS |
| * file for a list of people on the GTK+ Team. See the ChangeLog |
| * files for a list of changes. These files are distributed with |
| * GTK+ at ftp://ftp.gtk.org/pub/gtk/. |
| */ |
| |
| /** |
| * SECTION:gtkmain |
| * @Short_description: Library initialization, main event loop, and events |
| * @Title: Main loop and Events |
| * @See_also:See the GLib manual, especially #GMainLoop and signal-related |
| * functions such as g_signal_connect() |
| * |
| * Before using GTK+, you need to initialize it; initialization connects to the |
| * window system display, and parses some standard command line arguments. The |
| * gtk_init() macro initializes GTK+. gtk_init() exits the application if errors |
| * occur; to avoid this, use gtk_init_check(). gtk_init_check() allows you to |
| * recover from a failed GTK+ initialization - you might start up your |
| * application in text mode instead. |
| * |
| * Like all GUI toolkits, GTK+ uses an event-driven programming model. When the |
| * user is doing nothing, GTK+ sits in the <firstterm>main loop</firstterm> and |
| * waits for input. If the user performs some action - say, a mouse click - then |
| * the main loop "wakes up" and delivers an event to GTK+. GTK+ forwards the |
| * event to one or more widgets. |
| * |
| * When widgets receive an event, they frequently emit one or more |
| * <firstterm>signals</firstterm>. Signals notify your program that "something |
| * interesting happened" by invoking functions you've connected to the signal |
| * with g_signal_connect(). Functions connected to a signal are often termed |
| * <firstterm>callbacks</firstterm>. |
| * |
| * When your callbacks are invoked, you would typically take some action - for |
| * example, when an Open button is clicked you might display a |
| * #GtkFileChooserDialog. After a callback finishes, GTK+ will return to the |
| * main loop and await more user input. |
| * </para> |
| * <example> |
| * <title>Typical <function>main()</function> function for a GTK+ application</title> |
| * <programlisting> |
| * int |
| * main (int argc, char **argv) |
| * { |
| * /* Initialize i18n support */ |
| * gtk_set_locale (); |
| * |
| * /* Initialize the widget set */ |
| * gtk_init (&argc, &argv); |
| * |
| * /* Create the main window */ |
| * mainwin = gtk_window_new (GTK_WINDOW_TOPLEVEL); |
| * |
| * /* Set up our GUI elements */ |
| * ... |
| * |
| * /* Show the application window */ |
| * gtk_widget_show_all (mainwin); |
| * |
| * /* Enter the main event loop, and wait for user interaction */ |
| * gtk_main (); |
| * |
| * /* The user lost interest */ |
| * return 0; |
| * } |
| * </programlisting> |
| * </example> |
| * <para> |
| * It's OK to use the GLib main loop directly instead of gtk_main(), though it |
| * involves slightly more typing. See #GMainLoop in the GLib documentation. |
| */ |
| |
| #include "config.h" |
| |
| #include "gdk/gdk.h" |
| |
| #include <locale.h> |
| |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #ifdef HAVE_UNISTD_H |
| #include <unistd.h> |
| #endif |
| #include <sys/types.h> /* For uid_t, gid_t */ |
| |
| #ifdef G_OS_WIN32 |
| #define STRICT |
| #include <windows.h> |
| #undef STRICT |
| #endif |
| |
| #include "gtkintl.h" |
| |
| #include "gtkaccelmapprivate.h" |
| #include "gtkbox.h" |
| #include "gtkclipboard.h" |
| #include "gtkdebug.h" |
| #include "gtkdnd.h" |
| #include "gtkmain.h" |
| #include "gtkmenu.h" |
| #include "gtkmodules.h" |
| #include "gtkmodulesprivate.h" |
| #include "gtkprivate.h" |
| #include "gtkrecentmanager.h" |
| #include "gtkresources.h" |
| #include "gtkselectionprivate.h" |
| #include "gtksettingsprivate.h" |
| #include "gtktooltip.h" |
| #include "gtkversion.h" |
| #include "gtkwidgetprivate.h" |
| #include "gtkwindowprivate.h" |
| |
| #include "a11y/gail.h" |
| #include "a11y/gailutil.h" |
| |
| /* Private type definitions |
| */ |
| typedef struct _GtkKeySnooperData GtkKeySnooperData; |
| |
| struct _GtkKeySnooperData |
| { |
| GtkKeySnoopFunc func; |
| gpointer func_data; |
| guint id; |
| }; |
| |
| static gint gtk_invoke_key_snoopers (GtkWidget *grab_widget, |
| GdkEvent *event); |
| |
| static GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget); |
| |
| static guint gtk_main_loop_level = 0; |
| static gint pre_initialized = FALSE; |
| static gint gtk_initialized = FALSE; |
| static GList *current_events = NULL; |
| |
| static GSList *main_loops = NULL; /* stack of currently executing main loops */ |
| |
| static GSList *key_snoopers = NULL; |
| |
| static guint debug_flags = 0; /* Global GTK debug flag */ |
| |
| #ifdef G_ENABLE_DEBUG |
| static const GDebugKey gtk_debug_keys[] = { |
| {"misc", GTK_DEBUG_MISC}, |
| {"plugsocket", GTK_DEBUG_PLUGSOCKET}, |
| {"text", GTK_DEBUG_TEXT}, |
| {"tree", GTK_DEBUG_TREE}, |
| {"updates", GTK_DEBUG_UPDATES}, |
| {"keybindings", GTK_DEBUG_KEYBINDINGS}, |
| {"multihead", GTK_DEBUG_MULTIHEAD}, |
| {"modules", GTK_DEBUG_MODULES}, |
| {"geometry", GTK_DEBUG_GEOMETRY}, |
| {"icontheme", GTK_DEBUG_ICONTHEME}, |
| {"printing", GTK_DEBUG_PRINTING}, |
| {"builder", GTK_DEBUG_BUILDER}, |
| {"size-request", GTK_DEBUG_SIZE_REQUEST}, |
| {"no-css-cache", GTK_DEBUG_NO_CSS_CACHE} |
| }; |
| #endif /* G_ENABLE_DEBUG */ |
| |
| /** |
| * gtk_get_major_version: |
| * |
| * Returns the major version number of the GTK+ library. |
| * (e.g. in GTK+ version 3.1.5 this is 3.) |
| * |
| * This function is in the library, so it represents the GTK+ library |
| * your code is running against. Contrast with the #GTK_MAJOR_VERSION |
| * macro, which represents the major version of the GTK+ headers you |
| * have included when compiling your code. |
| * |
| * Returns: the major version number of the GTK+ library |
| * |
| * Since: 3.0 |
| */ |
| guint |
| gtk_get_major_version (void) |
| { |
| return GTK_MAJOR_VERSION; |
| } |
| |
| /** |
| * gtk_get_minor_version: |
| * |
| * Returns the minor version number of the GTK+ library. |
| * (e.g. in GTK+ version 3.1.5 this is 1.) |
| * |
| * This function is in the library, so it represents the GTK+ library |
| * your code is are running against. Contrast with the |
| * #GTK_MINOR_VERSION macro, which represents the minor version of the |
| * GTK+ headers you have included when compiling your code. |
| * |
| * Returns: the minor version number of the GTK+ library |
| * |
| * Since: 3.0 |
| */ |
| guint |
| gtk_get_minor_version (void) |
| { |
| return GTK_MINOR_VERSION; |
| } |
| |
| /** |
| * gtk_get_micro_version: |
| * |
| * Returns the micro version number of the GTK+ library. |
| * (e.g. in GTK+ version 3.1.5 this is 5.) |
| * |
| * This function is in the library, so it represents the GTK+ library |
| * your code is are running against. Contrast with the |
| * #GTK_MICRO_VERSION macro, which represents the micro version of the |
| * GTK+ headers you have included when compiling your code. |
| * |
| * Returns: the micro version number of the GTK+ library |
| * |
| * Since: 3.0 |
| */ |
| guint |
| gtk_get_micro_version (void) |
| { |
| return GTK_MICRO_VERSION; |
| } |
| |
| /** |
| * gtk_get_binary_age: |
| * |
| * Returns the binary age as passed to <application>libtool</application> |
| * when building the GTK+ library the process is running against. |
| * If <application>libtool</application> means nothing to you, don't |
| * worry about it. |
| * |
| * Returns: the binary age of the GTK+ library |
| * |
| * Since: 3.0 |
| */ |
| guint |
| gtk_get_binary_age (void) |
| { |
| return GTK_BINARY_AGE; |
| } |
| |
| /** |
| * gtk_get_interface_age: |
| * |
| * Returns the interface age as passed to <application>libtool</application> |
| * when building the GTK+ library the process is running against. |
| * If <application>libtool</application> means nothing to you, don't |
| * worry about it. |
| * |
| * Returns: the interface age of the GTK+ library |
| * |
| * Since: 3.0 |
| */ |
| guint |
| gtk_get_interface_age (void) |
| { |
| return GTK_INTERFACE_AGE; |
| } |
| |
| /** |
| * gtk_check_version: |
| * @required_major: the required major version |
| * @required_minor: the required minor version |
| * @required_micro: the required micro version |
| * |
| * Checks that the GTK+ library in use is compatible with the |
| * given version. Generally you would pass in the constants |
| * #GTK_MAJOR_VERSION, #GTK_MINOR_VERSION, #GTK_MICRO_VERSION |
| * as the three arguments to this function; that produces |
| * a check that the library in use is compatible with |
| * the version of GTK+ the application or module was compiled |
| * against. |
| * |
| * Compatibility is defined by two things: first the version |
| * of the running library is newer than the version |
| * @required_major.required_minor.@required_micro. Second |
| * the running library must be binary compatible with the |
| * version @required_major.required_minor.@required_micro |
| * (same major version.) |
| * |
| * This function is primarily for GTK+ modules; the module |
| * can call this function to check that it wasn't loaded |
| * into an incompatible version of GTK+. However, such a |
| * check isn't completely reliable, since the module may be |
| * linked against an old version of GTK+ and calling the |
| * old version of gtk_check_version(), but still get loaded |
| * into an application using a newer version of GTK+. |
| * |
| * Return value: %NULL if the GTK+ library is compatible with the |
| * given version, or a string describing the version mismatch. |
| * The returned string is owned by GTK+ and should not be modified |
| * or freed. |
| */ |
| const gchar* |
| gtk_check_version (guint required_major, |
| guint required_minor, |
| guint required_micro) |
| { |
| gint gtk_effective_micro = 100 * GTK_MINOR_VERSION + GTK_MICRO_VERSION; |
| gint required_effective_micro = 100 * required_minor + required_micro; |
| |
| if (required_major > GTK_MAJOR_VERSION) |
| return "GTK+ version too old (major mismatch)"; |
| if (required_major < GTK_MAJOR_VERSION) |
| return "GTK+ version too new (major mismatch)"; |
| if (required_effective_micro < gtk_effective_micro - GTK_BINARY_AGE) |
| return "GTK+ version too new (micro mismatch)"; |
| if (required_effective_micro > gtk_effective_micro) |
| return "GTK+ version too old (micro mismatch)"; |
| return NULL; |
| } |
| |
| /* This checks to see if the process is running suid or sgid |
| * at the current time. If so, we don't allow GTK+ to be initialized. |
| * This is meant to be a mild check - we only error out if we |
| * can prove the programmer is doing something wrong, not if |
| * they could be doing something wrong. For this reason, we |
| * don't use issetugid() on BSD or prctl (PR_GET_DUMPABLE). |
| */ |
| static gboolean |
| check_setugid (void) |
| { |
| /* this isn't at all relevant on MS Windows and doesn't compile ... --hb */ |
| #ifndef G_OS_WIN32 |
| uid_t ruid, euid, suid; /* Real, effective and saved user ID's */ |
| gid_t rgid, egid, sgid; /* Real, effective and saved group ID's */ |
| |
| #ifdef HAVE_GETRESUID |
| /* These aren't in the header files, so we prototype them here. |
| */ |
| int getresuid(uid_t *ruid, uid_t *euid, uid_t *suid); |
| int getresgid(gid_t *rgid, gid_t *egid, gid_t *sgid); |
| |
| if (getresuid (&ruid, &euid, &suid) != 0 || |
| getresgid (&rgid, &egid, &sgid) != 0) |
| #endif /* HAVE_GETRESUID */ |
| { |
| suid = ruid = getuid (); |
| sgid = rgid = getgid (); |
| euid = geteuid (); |
| egid = getegid (); |
| } |
| |
| if (ruid != euid || ruid != suid || |
| rgid != egid || rgid != sgid) |
| { |
| g_warning ("This process is currently running setuid or setgid.\n" |
| "This is not a supported use of GTK+. You must create a helper\n" |
| "program instead. For further details, see:\n\n" |
| " http://www.gtk.org/setuid.html\n\n" |
| "Refusing to initialize GTK+."); |
| exit (1); |
| } |
| #endif |
| return TRUE; |
| } |
| |
| static gboolean do_setlocale = TRUE; |
| |
| /** |
| * gtk_disable_setlocale: |
| * |
| * Prevents gtk_init(), gtk_init_check(), gtk_init_with_args() and |
| * gtk_parse_args() from automatically |
| * calling <literal>setlocale (LC_ALL, "")</literal>. You would |
| * want to use this function if you wanted to set the locale for |
| * your program to something other than the user's locale, or if |
| * you wanted to set different values for different locale categories. |
| * |
| * Most programs should not need to call this function. |
| **/ |
| void |
| gtk_disable_setlocale (void) |
| { |
| if (pre_initialized) |
| g_warning ("gtk_disable_setlocale() must be called before gtk_init()"); |
| |
| do_setlocale = FALSE; |
| } |
| |
| #ifdef G_PLATFORM_WIN32 |
| #undef gtk_init_check |
| #endif |
| |
| static GString *gtk_modules_string = NULL; |
| static gboolean g_fatal_warnings = FALSE; |
| |
| #ifdef G_ENABLE_DEBUG |
| static gboolean |
| gtk_arg_debug_cb (const char *key, const char *value, gpointer user_data) |
| { |
| debug_flags |= g_parse_debug_string (value, |
| gtk_debug_keys, |
| G_N_ELEMENTS (gtk_debug_keys)); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| gtk_arg_no_debug_cb (const char *key, const char *value, gpointer user_data) |
| { |
| debug_flags &= ~g_parse_debug_string (value, |
| gtk_debug_keys, |
| G_N_ELEMENTS (gtk_debug_keys)); |
| |
| return TRUE; |
| } |
| #endif /* G_ENABLE_DEBUG */ |
| |
| static gboolean |
| gtk_arg_module_cb (const char *key, const char *value, gpointer user_data) |
| { |
| if (value && *value) |
| { |
| if (gtk_modules_string) |
| g_string_append_c (gtk_modules_string, G_SEARCHPATH_SEPARATOR); |
| else |
| gtk_modules_string = g_string_new (NULL); |
| |
| g_string_append (gtk_modules_string, value); |
| } |
| |
| return TRUE; |
| } |
| |
| static const GOptionEntry gtk_args[] = { |
| { "gtk-module", 0, 0, G_OPTION_ARG_CALLBACK, gtk_arg_module_cb, |
| /* Description of --gtk-module=MODULES in --help output */ N_("Load additional GTK+ modules"), |
| /* Placeholder in --gtk-module=MODULES in --help output */ N_("MODULES") }, |
| { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, |
| /* Description of --g-fatal-warnings in --help output */ N_("Make all warnings fatal"), NULL }, |
| #ifdef G_ENABLE_DEBUG |
| { "gtk-debug", 0, 0, G_OPTION_ARG_CALLBACK, gtk_arg_debug_cb, |
| /* Description of --gtk-debug=FLAGS in --help output */ N_("GTK+ debugging flags to set"), |
| /* Placeholder in --gtk-debug=FLAGS in --help output */ N_("FLAGS") }, |
| { "gtk-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, gtk_arg_no_debug_cb, |
| /* Description of --gtk-no-debug=FLAGS in --help output */ N_("GTK+ debugging flags to unset"), |
| /* Placeholder in --gtk-no-debug=FLAGS in --help output */ N_("FLAGS") }, |
| #endif |
| { NULL } |
| }; |
| |
| #ifdef G_OS_WIN32 |
| |
| static char *iso639_to_check = NULL; |
| static char *iso3166_to_check = NULL; |
| static char *script_to_check = NULL; |
| static gboolean setlocale_called = FALSE; |
| |
| static BOOL CALLBACK |
| enum_locale_proc (LPTSTR locale) |
| { |
| LCID lcid; |
| char iso639[10]; |
| char iso3166[10]; |
| char *endptr; |
| |
| |
| lcid = strtoul (locale, &endptr, 16); |
| if (*endptr == '\0' && |
| GetLocaleInfo (lcid, LOCALE_SISO639LANGNAME, iso639, sizeof (iso639)) && |
| GetLocaleInfo (lcid, LOCALE_SISO3166CTRYNAME, iso3166, sizeof (iso3166))) |
| { |
| if (strcmp (iso639, iso639_to_check) == 0 && |
| ((iso3166_to_check != NULL && |
| strcmp (iso3166, iso3166_to_check) == 0) || |
| (iso3166_to_check == NULL && |
| SUBLANGID (LANGIDFROMLCID (lcid)) == SUBLANG_DEFAULT))) |
| { |
| char language[100], country[100]; |
| char locale[300]; |
| |
| if (script_to_check != NULL) |
| { |
| /* If lcid is the "other" script for this language, |
| * return TRUE, i.e. continue looking. |
| */ |
| if (strcmp (script_to_check, "Latn") == 0) |
| { |
| switch (LANGIDFROMLCID (lcid)) |
| { |
| case MAKELANGID (LANG_AZERI, SUBLANG_AZERI_CYRILLIC): |
| return TRUE; |
| case MAKELANGID (LANG_UZBEK, SUBLANG_UZBEK_CYRILLIC): |
| return TRUE; |
| case MAKELANGID (LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC): |
| return TRUE; |
| case MAKELANGID (LANG_SERBIAN, 0x07): |
| /* Serbian in Bosnia and Herzegovina, Cyrillic */ |
| return TRUE; |
| } |
| } |
| else if (strcmp (script_to_check, "Cyrl") == 0) |
| { |
| switch (LANGIDFROMLCID (lcid)) |
| { |
| case MAKELANGID (LANG_AZERI, SUBLANG_AZERI_LATIN): |
| return TRUE; |
| case MAKELANGID (LANG_UZBEK, SUBLANG_UZBEK_LATIN): |
| return TRUE; |
| case MAKELANGID (LANG_SERBIAN, SUBLANG_SERBIAN_LATIN): |
| return TRUE; |
| case MAKELANGID (LANG_SERBIAN, 0x06): |
| /* Serbian in Bosnia and Herzegovina, Latin */ |
| return TRUE; |
| } |
| } |
| } |
| |
| SetThreadLocale (lcid); |
| |
| if (GetLocaleInfo (lcid, LOCALE_SENGLANGUAGE, language, sizeof (language)) && |
| GetLocaleInfo (lcid, LOCALE_SENGCOUNTRY, country, sizeof (country))) |
| { |
| strcpy (locale, language); |
| strcat (locale, "_"); |
| strcat (locale, country); |
| |
| if (setlocale (LC_ALL, locale) != NULL) |
| setlocale_called = TRUE; |
| } |
| |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| #endif |
| |
| static void |
| setlocale_initialization (void) |
| { |
| static gboolean initialized = FALSE; |
| |
| if (initialized) |
| return; |
| initialized = TRUE; |
| |
| if (do_setlocale) |
| { |
| #ifdef G_OS_WIN32 |
| /* If some of the POSIXish environment variables are set, set |
| * the Win32 thread locale correspondingly. |
| */ |
| char *p = getenv ("LC_ALL"); |
| if (p == NULL) |
| p = getenv ("LANG"); |
| |
| if (p != NULL) |
| { |
| p = g_strdup (p); |
| if (strcmp (p, "C") == 0) |
| SetThreadLocale (LOCALE_SYSTEM_DEFAULT); |
| else |
| { |
| /* Check if one of the supported locales match the |
| * environment variable. If so, use that locale. |
| */ |
| iso639_to_check = p; |
| iso3166_to_check = strchr (iso639_to_check, '_'); |
| if (iso3166_to_check != NULL) |
| { |
| *iso3166_to_check++ = '\0'; |
| |
| script_to_check = strchr (iso3166_to_check, '@'); |
| if (script_to_check != NULL) |
| *script_to_check++ = '\0'; |
| |
| /* Handle special cases. */ |
| |
| /* The standard code for Serbia and Montenegro was |
| * "CS", but MSFT uses for some reason "SP". By now |
| * (October 2006), SP has split into two, "RS" and |
| * "ME", but don't bother trying to handle those |
| * yet. Do handle the even older "YU", though. |
| */ |
| if (strcmp (iso3166_to_check, "CS") == 0 || |
| strcmp (iso3166_to_check, "YU") == 0) |
| iso3166_to_check = "SP"; |
| } |
| else |
| { |
| script_to_check = strchr (iso639_to_check, '@'); |
| if (script_to_check != NULL) |
| *script_to_check++ = '\0'; |
| /* LANG_SERBIAN == LANG_CROATIAN, recognize just "sr" */ |
| if (strcmp (iso639_to_check, "sr") == 0) |
| iso3166_to_check = "SP"; |
| } |
| |
| EnumSystemLocales (enum_locale_proc, LCID_SUPPORTED); |
| } |
| g_free (p); |
| } |
| if (!setlocale_called) |
| setlocale (LC_ALL, ""); |
| #else |
| if (!setlocale (LC_ALL, "")) |
| g_warning ("Locale not supported by C library.\n\tUsing the fallback 'C' locale."); |
| #endif |
| } |
| } |
| |
| static void |
| do_pre_parse_initialization (int *argc, |
| char ***argv) |
| { |
| const gchar *env_string; |
| |
| if (pre_initialized) |
| return; |
| |
| pre_initialized = TRUE; |
| |
| if (_gtk_module_has_mixed_deps (NULL)) |
| g_error ("GTK+ 2.x symbols detected. Using GTK+ 2.x and GTK+ 3 in the same process is not supported"); |
| |
| gdk_pre_parse_libgtk_only (); |
| gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL); |
| |
| #ifdef G_ENABLE_DEBUG |
| env_string = g_getenv ("GTK_DEBUG"); |
| if (env_string != NULL) |
| { |
| debug_flags = g_parse_debug_string (env_string, |
| gtk_debug_keys, |
| G_N_ELEMENTS (gtk_debug_keys)); |
| env_string = NULL; |
| } |
| #endif /* G_ENABLE_DEBUG */ |
| |
| env_string = g_getenv ("GTK_MODULES"); |
| if (env_string) |
| gtk_modules_string = g_string_new (env_string); |
| } |
| |
| static void |
| gettext_initialization (void) |
| { |
| setlocale_initialization (); |
| |
| #ifdef ENABLE_NLS |
| bindtextdomain (GETTEXT_PACKAGE, _gtk_get_localedir ()); |
| bindtextdomain (GETTEXT_PACKAGE "-properties", _gtk_get_localedir ()); |
| # ifdef HAVE_BIND_TEXTDOMAIN_CODESET |
| bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); |
| bind_textdomain_codeset (GETTEXT_PACKAGE "-properties", "UTF-8"); |
| # endif |
| #endif |
| } |
| |
| static void |
| do_post_parse_initialization (int *argc, |
| char ***argv) |
| { |
| if (gtk_initialized) |
| return; |
| |
| gettext_initialization (); |
| |
| #ifdef SIGPIPE |
| signal (SIGPIPE, SIG_IGN); |
| #endif |
| |
| if (g_fatal_warnings) |
| { |
| GLogLevelFlags fatal_mask; |
| |
| fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); |
| fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; |
| g_log_set_always_fatal (fatal_mask); |
| } |
| |
| if (debug_flags & GTK_DEBUG_UPDATES) |
| gdk_window_set_debug_updates (TRUE); |
| |
| { |
| /* Translate to default:RTL if you want your widgets |
| * to be RTL, otherwise translate to default:LTR. |
| * Do *not* translate it to "predefinito:LTR", if it |
| * it isn't default:LTR or default:RTL it will not work |
| */ |
| char *e = _("default:LTR"); |
| if (strcmp (e, "default:RTL")==0) |
| gtk_widget_set_default_direction (GTK_TEXT_DIR_RTL); |
| else if (strcmp (e, "default:LTR")) |
| g_warning ("Whoever translated default:LTR did so wrongly.\n"); |
| } |
| |
| _gtk_register_resource (); |
| |
| _gtk_accel_map_init (); |
| |
| /* Set the 'initialized' flag. |
| */ |
| gtk_initialized = TRUE; |
| |
| /* load gtk modules */ |
| if (gtk_modules_string) |
| { |
| _gtk_modules_init (argc, argv, gtk_modules_string->str); |
| g_string_free (gtk_modules_string, TRUE); |
| } |
| else |
| { |
| _gtk_modules_init (argc, argv, NULL); |
| } |
| |
| _gtk_accessibility_init (); |
| } |
| |
| |
| typedef struct |
| { |
| gboolean open_default_display; |
| } OptionGroupInfo; |
| |
| static gboolean |
| pre_parse_hook (GOptionContext *context, |
| GOptionGroup *group, |
| gpointer data, |
| GError **error) |
| { |
| do_pre_parse_initialization (NULL, NULL); |
| |
| return TRUE; |
| } |
| |
| static gboolean |
| post_parse_hook (GOptionContext *context, |
| GOptionGroup *group, |
| gpointer data, |
| GError **error) |
| { |
| OptionGroupInfo *info = data; |
| |
| |
| do_post_parse_initialization (NULL, NULL); |
| |
| if (info->open_default_display) |
| { |
| if (gdk_display_open_default_libgtk_only () == NULL) |
| { |
| const char *display_name = gdk_get_display_arg_name (); |
| g_set_error (error, |
| G_OPTION_ERROR, |
| G_OPTION_ERROR_FAILED, |
| _("Cannot open display: %s"), |
| display_name ? display_name : "" ); |
| |
| return FALSE; |
| } |
| } |
| |
| return TRUE; |
| } |
| |
| |
| /** |
| * gtk_get_debug_flags: |
| * |
| * Returns the GTK+ debug flags. |
| * |
| * This function is intended for GTK+ modules that want |
| * to adjust their debug output based on GTK+ debug flags. |
| * |
| * Returns: the GTK+ debug flags. |
| */ |
| guint |
| gtk_get_debug_flags (void) |
| { |
| return debug_flags; |
| } |
| |
| /** |
| * gtk_set_debug_flags: |
| * |
| * Sets the GTK+ debug flags. |
| */ |
| void |
| gtk_set_debug_flags (guint flags) |
| { |
| debug_flags = flags; |
| } |
| |
| /** |
| * gtk_get_option_group: (skip) |
| * @open_default_display: whether to open the default display |
| * when parsing the commandline arguments |
| * |
| * Returns a #GOptionGroup for the commandline arguments recognized |
| * by GTK+ and GDK. |
| * |
| * You should add this group to your #GOptionContext |
| * with g_option_context_add_group(), if you are using |
| * g_option_context_parse() to parse your commandline arguments. |
| * |
| * Returns: a #GOptionGroup for the commandline arguments recognized |
| * by GTK+ |
| * |
| * Since: 2.6 |
| */ |
| GOptionGroup * |
| gtk_get_option_group (gboolean open_default_display) |
| { |
| GOptionGroup *group; |
| OptionGroupInfo *info; |
| |
| gettext_initialization (); |
| |
| info = g_new0 (OptionGroupInfo, 1); |
| info->open_default_display = open_default_display; |
| |
| group = g_option_group_new ("gtk", _("GTK+ Options"), _("Show GTK+ Options"), info, g_free); |
| g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook); |
| |
| gdk_add_option_entries_libgtk_only (group); |
| g_option_group_add_entries (group, gtk_args); |
| g_option_group_set_translation_domain (group, GETTEXT_PACKAGE); |
| |
| return group; |
| } |
| |
| /** |
| * gtk_init_with_args: |
| * @argc: (inout): Address of the <parameter>argc</parameter> parameter of |
| * your main() function (or 0 if @argv is %NULL). This will be changed if |
| * any arguments were handled. |
| * @argv: (array length=argc) (inout) (allow-none): Address of the |
| * <parameter>argv</parameter> parameter of main(), or %NULL. Any options |
| * understood by GTK+ are stripped before return. |
| * @parameter_string: a string which is displayed in |
| * the first line of <option>--help</option> output, after |
| * <literal><replaceable>programname</replaceable> [OPTION...]</literal> |
| * @entries: (array zero-terminated=1): a %NULL-terminated array |
| * of #GOptionEntrys describing the options of your program |
| * @translation_domain: a translation domain to use for translating |
| * the <option>--help</option> output for the options in @entries |
| * and the @parameter_string with gettext(), or %NULL |
| * @error: a return location for errors |
| * |
| * This function does the same work as gtk_init_check(). |
| * Additionally, it allows you to add your own commandline options, |
| * and it automatically generates nicely formatted |
| * <option>--help</option> output. Note that your program will |
| * be terminated after writing out the help output. |
| * |
| * Returns: %TRUE if the windowing system has been successfully |
| * initialized, %FALSE otherwise |
| * |
| * Since: 2.6 |
| */ |
| gboolean |
| gtk_init_with_args (gint *argc, |
| gchar ***argv, |
| const gchar *parameter_string, |
| const GOptionEntry *entries, |
| const gchar *translation_domain, |
| GError **error) |
| { |
| GOptionContext *context; |
| GOptionGroup *gtk_group; |
| gboolean retval; |
| |
| if (gtk_initialized) |
| return gdk_display_open_default_libgtk_only () != NULL; |
| |
| gettext_initialization (); |
| |
| if (!check_setugid ()) |
| return FALSE; |
| |
| gtk_group = gtk_get_option_group (TRUE); |
| |
| context = g_option_context_new (parameter_string); |
| g_option_context_add_group (context, gtk_group); |
| g_option_context_set_translation_domain (context, translation_domain); |
| |
| if (entries) |
| g_option_context_add_main_entries (context, entries, translation_domain); |
| retval = g_option_context_parse (context, argc, argv, error); |
| |
| g_option_context_free (context); |
| |
| return retval; |
| } |
| |
| |
| /** |
| * gtk_parse_args: |
| * @argc: (inout): a pointer to the number of command line arguments |
| * @argv: (array length=argc) (inout): a pointer to the array of |
| * command line arguments |
| * |
| * Parses command line arguments, and initializes global |
| * attributes of GTK+, but does not actually open a connection |
| * to a display. (See gdk_display_open(), gdk_get_display_arg_name()) |
| * |
| * Any arguments used by GTK+ or GDK are removed from the array and |
| * @argc and @argv are updated accordingly. |
| * |
| * There is no need to call this function explicitely if you are using |
| * gtk_init(), or gtk_init_check(). |
| * |
| * Return value: %TRUE if initialization succeeded, otherwise %FALSE |
| */ |
| gboolean |
| gtk_parse_args (int *argc, |
| char ***argv) |
| { |
| GOptionContext *option_context; |
| GOptionGroup *gtk_group; |
| GError *error = NULL; |
| |
| if (gtk_initialized) |
| return TRUE; |
| |
| gettext_initialization (); |
| |
| if (!check_setugid ()) |
| return FALSE; |
| |
| option_context = g_option_context_new (NULL); |
| g_option_context_set_ignore_unknown_options (option_context, TRUE); |
| g_option_context_set_help_enabled (option_context, FALSE); |
| gtk_group = gtk_get_option_group (FALSE); |
| g_option_context_set_main_group (option_context, gtk_group); |
| if (!g_option_context_parse (option_context, argc, argv, &error)) |
| { |
| g_warning ("%s", error->message); |
| g_error_free (error); |
| } |
| |
| g_option_context_free (option_context); |
| |
| return TRUE; |
| } |
| |
| #ifdef G_PLATFORM_WIN32 |
| #undef gtk_init_check |
| #endif |
| |
| /** |
| * gtk_init_check: |
| * @argc: (inout): Address of the <parameter>argc</parameter> parameter of |
| * your main() function (or 0 if @argv is %NULL). This will be changed if |
| * any arguments were handled. |
| * @argv: (array length=argc) (inout) (allow-none): Address of the |
| * <parameter>argv</parameter> parameter of main(), or %NULL. Any options |
| * understood by GTK+ are stripped before return. |
| * |
| * This function does the same work as gtk_init() with only a single |
| * change: It does not terminate the program if the windowing system |
| * can't be initialized. Instead it returns %FALSE on failure. |
| * |
| * This way the application can fall back to some other means of |
| * communication with the user - for example a curses or command line |
| * interface. |
| * |
| * Return value: %TRUE if the windowing system has been successfully |
| * initialized, %FALSE otherwise |
| */ |
| gboolean |
| gtk_init_check (int *argc, |
| char ***argv) |
| { |
| if (!gtk_parse_args (argc, argv)) |
| return FALSE; |
| |
| return gdk_display_open_default_libgtk_only () != NULL; |
| } |
| |
| #ifdef G_PLATFORM_WIN32 |
| #undef gtk_init |
| #endif |
| |
| /** |
| * gtk_init: |
| * @argc: (inout): Address of the <parameter>argc</parameter> parameter of |
| * your main() function (or 0 if @argv is %NULL). This will be changed if |
| * any arguments were handled. |
| * @argv: (array length=argc) (inout) (allow-none): Address of the |
| * <parameter>argv</parameter> parameter of main(), or %NULL. Any options |
| * understood by GTK+ are stripped before return. |
| * |
| * Call this function before using any other GTK+ functions in your GUI |
| * applications. It will initialize everything needed to operate the |
| * toolkit and parses some standard command line options. |
| * |
| * Although you are expected to pass the @argc, @argv parameters from main() to |
| * this function, it is possible to pass %NULL if @argv is not available or |
| * commandline handling is not required. |
| * |
| * @argc and @argv are adjusted accordingly so your own code will |
| * never see those standard arguments. |
| * |
| * Note that there are some alternative ways to initialize GTK+: |
| * if you are calling gtk_parse_args(), gtk_init_check(), |
| * gtk_init_with_args() or g_option_context_parse() with |
| * the option group returned by gtk_get_option_group(), |
| * you <emphasis>don't</emphasis> have to call gtk_init(). |
| * |
| * <note><para> |
| * This function will terminate your program if it was unable to |
| * initialize the windowing system for some reason. If you want |
| * your program to fall back to a textual interface you want to |
| * call gtk_init_check() instead. |
| * </para></note> |
| * |
| * <note><para> |
| * Since 2.18, GTK+ calls <literal>signal (SIGPIPE, SIG_IGN)</literal> |
| * during initialization, to ignore SIGPIPE signals, since these are |
| * almost never wanted in graphical applications. If you do need to |
| * handle SIGPIPE for some reason, reset the handler after gtk_init(), |
| * but notice that other libraries (e.g. libdbus or gvfs) might do |
| * similar things. |
| * </para></note> |
| */ |
| void |
| gtk_init (int *argc, char ***argv) |
| { |
| if (!gtk_init_check (argc, argv)) |
| { |
| const char *display_name_arg = gdk_get_display_arg_name (); |
| if (display_name_arg == NULL) |
| display_name_arg = getenv("DISPLAY"); |
| g_warning ("cannot open display: %s", display_name_arg ? display_name_arg : ""); |
| exit (1); |
| } |
| } |
| |
| #ifdef G_OS_WIN32 |
| |
| /* This is relevant when building with gcc for Windows (MinGW), |
| * where we want to be struct packing compatible with MSVC, |
| * i.e. use the -mms-bitfields switch. |
| * For Cygwin there should be no need to be compatible with MSVC, |
| * so no need to use G_PLATFORM_WIN32. |
| */ |
| |
| static void |
| check_sizeof_GtkWindow (size_t sizeof_GtkWindow) |
| { |
| if (sizeof_GtkWindow != sizeof (GtkWindow)) |
| g_error ("Incompatible build!\n" |
| "The code using GTK+ thinks GtkWindow is of different\n" |
| "size than it actually is in this build of GTK+.\n" |
| "On Windows, this probably means that you have compiled\n" |
| "your code with gcc without the -mms-bitfields switch,\n" |
| "or that you are using an unsupported compiler."); |
| } |
| |
| /* In GTK+ 2.0 the GtkWindow struct actually is the same size in |
| * gcc-compiled code on Win32 whether compiled with -fnative-struct or |
| * not. Unfortunately this wan't noticed until after GTK+ 2.0.1. So, |
| * from GTK+ 2.0.2 on, check some other struct, too, where the use of |
| * -fnative-struct still matters. GtkBox is one such. |
| */ |
| static void |
| check_sizeof_GtkBox (size_t sizeof_GtkBox) |
| { |
| if (sizeof_GtkBox != sizeof (GtkBox)) |
| g_error ("Incompatible build!\n" |
| "The code using GTK+ thinks GtkBox is of different\n" |
| "size than it actually is in this build of GTK+.\n" |
| "On Windows, this probably means that you have compiled\n" |
| "your code with gcc without the -mms-bitfields switch,\n" |
| "or that you are using an unsupported compiler."); |
| } |
| |
| /* These two functions might get more checks added later, thus pass |
| * in the number of extra args. |
| */ |
| void |
| gtk_init_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow, size_t sizeof_GtkBox) |
| { |
| check_sizeof_GtkWindow (sizeof_GtkWindow); |
| if (num_checks >= 2) |
| check_sizeof_GtkBox (sizeof_GtkBox); |
| gtk_init (argc, argv); |
| } |
| |
| gboolean |
| gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof_GtkWindow, size_t sizeof_GtkBox) |
| { |
| check_sizeof_GtkWindow (sizeof_GtkWindow); |
| if (num_checks >= 2) |
| check_sizeof_GtkBox (sizeof_GtkBox); |
| return gtk_init_check (argc, argv); |
| } |
| |
| #endif |
| |
| /** |
| * gtk_get_default_language: |
| * |
| * Returns the #PangoLanguage for the default language currently in |
| * effect. (Note that this can change over the life of an |
| * application.) The default language is derived from the current |
| * locale. It determines, for example, whether GTK+ uses the |
| * right-to-left or left-to-right text direction. |
| * |
| * This function is equivalent to pango_language_get_default(). |
| * See that function for details. |
| * |
| * Return value: the default language as a #PangoLanguage, |
| * must not be freed |
| */ |
| PangoLanguage * |
| gtk_get_default_language (void) |
| { |
| return pango_language_get_default (); |
| } |
| |
| /** |
| * gtk_main: |
| * |
| * Runs the main loop until gtk_main_quit() is called. |
| * |
| * You can nest calls to gtk_main(). In that case gtk_main_quit() |
| * will make the innermost invocation of the main loop return. |
| */ |
| void |
| gtk_main (void) |
| { |
| GMainLoop *loop; |
| |
| gtk_main_loop_level++; |
| |
| loop = g_main_loop_new (NULL, TRUE); |
| main_loops = g_slist_prepend (main_loops, loop); |
| |
| if (g_main_loop_is_running (main_loops->data)) |
| { |
| gdk_threads_leave (); |
| g_main_loop_run (loop); |
| gdk_threads_enter (); |
| gdk_flush (); |
| } |
| |
| main_loops = g_slist_remove (main_loops, loop); |
| |
| g_main_loop_unref (loop); |
| |
| gtk_main_loop_level--; |
| |
| if (gtk_main_loop_level == 0) |
| { |
| /* Keep this section in sync with gtk_application_shutdown() */ |
| |
| /* Try storing all clipboard data we have */ |
| _gtk_clipboard_store_all (); |
| |
| /* Synchronize the recent manager singleton */ |
| _gtk_recent_manager_sync (); |
| |
| _gtk_accessibility_shutdown (); |
| } |
| } |
| |
| /** |
| * gtk_main_level: |
| * |
| * Asks for the current nesting level of the main loop. |
| * |
| * Returns: the nesting level of the current invocation |
| * of the main loop |
| */ |
| guint |
| gtk_main_level (void) |
| { |
| return gtk_main_loop_level; |
| } |
| |
| /** |
| * gtk_main_quit: |
| * |
| * Makes the innermost invocation of the main loop return |
| * when it regains control. |
| */ |
| void |
| gtk_main_quit (void) |
| { |
| g_return_if_fail (main_loops != NULL); |
| |
| g_main_loop_quit (main_loops->data); |
| } |
| |
| /** |
| * gtk_events_pending: |
| * |
| * Checks if any events are pending. |
| * |
| * This can be used to update the UI and invoke timeouts etc. |
| * while doing some time intensive computation. |
| * |
| * <example> |
| * <title>Updating the UI during a long computation</title> |
| * <programlisting> |
| * /* computation going on... */ |
| * |
| * while (gtk_events_pending ()) |
| * gtk_main_iteration (); |
| * |
| * /* ...computation continued */ |
| * </programlisting> |
| * </example> |
| * |
| * Returns: %TRUE if any events are pending, %FALSE otherwise |
| */ |
| gboolean |
| gtk_events_pending (void) |
| { |
| gboolean result; |
| |
| gdk_threads_leave (); |
| result = g_main_context_pending (NULL); |
| gdk_threads_enter (); |
| |
| return result; |
| } |
| |
| /** |
| * gtk_main_iteration: |
| * |
| * Runs a single iteration of the mainloop. |
| * |
| * If no events are waiting to be processed GTK+ will block |
| * until the next event is noticed. If you don't want to block |
| * look at gtk_main_iteration_do() or check if any events are |
| * pending with gtk_events_pending() first. |
| * |
| * Returns: %TRUE if gtk_main_quit() has been called for the |
| * innermost mainloop |
| */ |
| gboolean |
| gtk_main_iteration (void) |
| { |
| gdk_threads_leave (); |
| g_main_context_iteration (NULL, TRUE); |
| gdk_threads_enter (); |
| |
| if (main_loops) |
| return !g_main_loop_is_running (main_loops->data); |
| else |
| return TRUE; |
| } |
| |
| /** |
| * gtk_main_iteration_do: |
| * @blocking: %TRUE if you want GTK+ to block if no events are pending |
| * |
| * Runs a single iteration of the mainloop. |
| * If no events are available either return or block depending on |
| * the value of @blocking. |
| * |
| * Returns: %TRUE if gtk_main_quit() has been called for the |
| * innermost mainloop |
| */ |
| gboolean |
| gtk_main_iteration_do (gboolean blocking) |
| { |
| gdk_threads_leave (); |
| g_main_context_iteration (NULL, blocking); |
| gdk_threads_enter (); |
| |
| if (main_loops) |
| return !g_main_loop_is_running (main_loops->data); |
| else |
| return TRUE; |
| } |
| |
| /* private libgtk to libgdk interfaces */ |
| gboolean gdk_device_grab_info_libgtk_only (GdkDisplay *display, |
| GdkDevice *device, |
| GdkWindow **grab_window, |
| gboolean *owner_events); |
| |
| static void |
| rewrite_events_translate (GdkWindow *old_window, |
| GdkWindow *new_window, |
| gdouble *x, |
| gdouble *y) |
| { |
| gint old_origin_x, old_origin_y; |
| gint new_origin_x, new_origin_y; |
| |
| gdk_window_get_origin (old_window, &old_origin_x, &old_origin_y); |
| gdk_window_get_origin (new_window, &new_origin_x, &new_origin_y); |
| |
| *x += old_origin_x - new_origin_x; |
| *y += old_origin_y - new_origin_y; |
| } |
| |
| static GdkEvent * |
| rewrite_event_for_window (GdkEvent *event, |
| GdkWindow *new_window) |
| { |
| event = gdk_event_copy (event); |
| |
| switch (event->type) |
| { |
| case GDK_SCROLL: |
| rewrite_events_translate (event->any.window, |
| new_window, |
| &event->scroll.x, &event->scroll.y); |
| break; |
| case GDK_BUTTON_PRESS: |
| case GDK_2BUTTON_PRESS: |
| case GDK_3BUTTON_PRESS: |
| case GDK_BUTTON_RELEASE: |
| rewrite_events_translate (event->any.window, |
| new_window, |
| &event->button.x, &event->button.y); |
| break; |
| case GDK_MOTION_NOTIFY: |
| rewrite_events_translate (event->any.window, |
| new_window, |
| &event->motion.x, &event->motion.y); |
| break; |
| case GDK_TOUCH_BEGIN: |
| case GDK_TOUCH_UPDATE: |
| case GDK_TOUCH_END: |
| case GDK_TOUCH_CANCEL: |
| rewrite_events_translate (event->any.window, |
| new_window, |
| &event->touch.x, &event->touch.y); |
| break; |
| case GDK_KEY_PRESS: |
| case GDK_KEY_RELEASE: |
| case GDK_PROXIMITY_IN: |
| case GDK_PROXIMITY_OUT: |
| break; |
| |
| default: |
| return event; |
| } |
| |
| g_object_unref (event->any.window); |
| event->any.window = g_object_ref (new_window); |
| |
| return event; |
| } |
| |
| /* If there is a pointer or keyboard grab in effect with owner_events = TRUE, |
| * then what X11 does is deliver the event normally if it was going to this |
| * client, otherwise, delivers it in terms of the grab window. This function |
| * rewrites events to the effect that events going to the same window group |
| * are delivered normally, otherwise, the event is delivered in terms of the |
| * grab window. |
| */ |
| static GdkEvent * |
| rewrite_event_for_grabs (GdkEvent *event) |
| { |
| GdkWindow *grab_window; |
| GtkWidget *event_widget, *grab_widget; |
| gpointer grab_widget_ptr; |
| gboolean owner_events; |
| GdkDisplay *display; |
| GdkDevice *device; |
| |
| switch (event->type) |
| { |
| case GDK_SCROLL: |
| case GDK_BUTTON_PRESS: |
| case GDK_2BUTTON_PRESS: |
| case GDK_3BUTTON_PRESS: |
| case GDK_BUTTON_RELEASE: |
| case GDK_MOTION_NOTIFY: |
| case GDK_PROXIMITY_IN: |
| case GDK_PROXIMITY_OUT: |
| case GDK_KEY_PRESS: |
| case GDK_KEY_RELEASE: |
| case GDK_TOUCH_BEGIN: |
| case GDK_TOUCH_UPDATE: |
| case GDK_TOUCH_END: |
| case GDK_TOUCH_CANCEL: |
| display = gdk_window_get_display (event->any.window); |
| device = gdk_event_get_device (event); |
| |
| if (!gdk_device_grab_info_libgtk_only (display, device, &grab_window, &owner_events) || |
| !owner_events) |
| return NULL; |
| break; |
| default: |
| return NULL; |
| } |
| |
| event_widget = gtk_get_event_widget (event); |
| gdk_window_get_user_data (grab_window, &grab_widget_ptr); |
| grab_widget = grab_widget_ptr; |
| |
| if (grab_widget && |
| gtk_main_get_window_group (grab_widget) != gtk_main_get_window_group (event_widget)) |
| return rewrite_event_for_window (event, grab_window); |
| else |
| return NULL; |
| } |
| |
| /** |
| * gtk_main_do_event: |
| * @event: An event to process (normally passed by GDK) |
| * |
| * Processes a single GDK event. |
| * |
| * This is public only to allow filtering of events between GDK and GTK+. |
| * You will not usually need to call this function directly. |
| * |
| * While you should not call this function directly, you might want to |
| * know how exactly events are handled. So here is what this function |
| * does with the event: |
| * |
| * <orderedlist> |
| * <listitem><para> |
| * Compress enter/leave notify events. If the event passed build an |
| * enter/leave pair together with the next event (peeked from GDK), both |
| * events are thrown away. This is to avoid a backlog of (de-)highlighting |
| * widgets crossed by the pointer. |
| * </para></listitem> |
| * <listitem><para> |
| * Find the widget which got the event. If the widget can't be determined |
| * the event is thrown away unless it belongs to a INCR transaction. |
| * </para></listitem> |
| * <listitem><para> |
| * Then the event is pushed onto a stack so you can query the currently |
| * handled event with gtk_get_current_event(). |
| * </para></listitem> |
| * <listitem><para> |
| * The event is sent to a widget. If a grab is active all events for widgets |
| * that are not in the contained in the grab widget are sent to the latter |
| * with a few exceptions: |
| * <itemizedlist> |
| * <listitem><para> |
| * Deletion and destruction events are still sent to the event widget for |
| * obvious reasons. |
| * </para></listitem> |
| * <listitem><para> |
| * Events which directly relate to the visual representation of the event |
| * widget. |
| * </para></listitem> |
| * <listitem><para> |
| * Leave events are delivered to the event widget if there was an enter |
| * event delivered to it before without the paired leave event. |
| * </para></listitem> |
| * <listitem><para> |
| * Drag events are not redirected because it is unclear what the semantics |
| * of that would be. |
| * </para></listitem> |
| * </itemizedlist> |
| * Another point of interest might be that all key events are first passed |
| * through the key snooper functions if there are any. Read the description |
| * of gtk_key_snooper_install() if you need this feature. |
| * </para></listitem> |
| * <listitem><para> |
| * After finishing the delivery the event is popped from the event stack. |
| * </para></listitem> |
| * </orderedlist> |
| */ |
| void |
| gtk_main_do_event (GdkEvent *event) |
| { |
| GtkWidget *event_widget; |
| GtkWidget *grab_widget = NULL; |
| GtkWidget *topmost_widget = NULL; |
| GtkWindowGroup *window_group; |
| GdkEvent *rewritten_event = NULL; |
| GdkDevice *device; |
| GList *tmp_list; |
| |
| if (event->type == GDK_SETTING) |
| { |
| _gtk_settings_handle_event (&event->setting); |
| return; |
| } |
| |
| if (event->type == GDK_OWNER_CHANGE) |
| { |
| _gtk_clipboard_handle_event (&event->owner_change); |
| return; |
| } |
| |
| /* Find the widget which got the event. We store the widget |
| * in the user_data field of GdkWindow's. Ignore the event |
| * if we don't have a widget for it, except for GDK_PROPERTY_NOTIFY |
| * events which are handled specially. Though this happens rarely, |
| * bogus events can occur for e.g. destroyed GdkWindows. |
| */ |
| event_widget = gtk_get_event_widget (event); |
| if (!event_widget) |
| { |
| /* To handle selection INCR transactions, we select |
| * PropertyNotify events on the requestor window and create |
| * a corresponding (fake) GdkWindow so that events get here. |
| * There won't be a widget though, so we have to handle |
| * them specially |
| */ |
| if (event->type == GDK_PROPERTY_NOTIFY) |
| _gtk_selection_incr_event (event->any.window, |
| &event->property); |
| |
| return; |
| } |
| |
| /* If pointer or keyboard grabs are in effect, munge the events |
| * so that each window group looks like a separate app. |
| */ |
| rewritten_event = rewrite_event_for_grabs (event); |
| if (rewritten_event) |
| { |
| event = rewritten_event; |
| event_widget = gtk_get_event_widget (event); |
| } |
| |
| window_group = gtk_main_get_window_group (event_widget); |
| device = gdk_event_get_device (event); |
| |
| /* check whether there is a (device) grab in effect... */ |
| if (device) |
| grab_widget = gtk_window_group_get_current_device_grab (window_group, device); |
| |
| if (!grab_widget) |
| grab_widget = gtk_window_group_get_current_grab (window_group); |
| |
| /* Find out the topmost widget where captured event propagation |
| * should start, which is the widget holding the GTK+ grab |
| * if any, otherwise it's left NULL and events are emitted |
| * from the toplevel (or topmost parentless parent). |
| */ |
| if (grab_widget) |
| topmost_widget = grab_widget; |
| |
| /* If the grab widget is an ancestor of the event widget |
| * then we send the event to the original event widget. |
| * This is the key to implementing modality. |
| */ |
| if (!grab_widget || |
| (gtk_widget_is_sensitive (event_widget) && |
| gtk_widget_is_ancestor (event_widget, grab_widget))) |
| grab_widget = event_widget; |
| |
| /* If the widget receiving events is actually blocked by another |
| * device GTK+ grab |
| */ |
| if (device && |
| _gtk_window_group_widget_is_blocked_for_device (window_group, grab_widget, device)) |
| { |
| if (rewritten_event) |
| gdk_event_free (rewritten_event); |
| |
| return; |
| } |
| |
| /* Push the event onto a stack of current events for |
| * gtk_current_event_get(). |
| */ |
| current_events = g_list_prepend (current_events, event); |
| |
| /* Not all events get sent to the grabbing widget. |
| * The delete, destroy, expose, focus change and resize |
| * events still get sent to the event widget because |
| * 1) these events have no meaning for the grabbing widget |
| * and 2) redirecting these events to the grabbing widget |
| * could cause the display to be messed up. |
| * |
| * Drag events are also not redirected, since it isn't |
| * clear what the semantics of that would be. |
| */ |
| switch (event->type) |
| { |
| case GDK_NOTHING: |
| break; |
| |
| case GDK_DELETE: |
| g_object_ref (event_widget); |
| if ((!gtk_window_group_get_current_grab (window_group) || gtk_widget_get_toplevel (gtk_window_group_get_current_grab (window_group)) == event_widget) && |
| !gtk_widget_event (event_widget, event)) |
| gtk_widget_destroy (event_widget); |
| g_object_unref (event_widget); |
| break; |
| |
| case GDK_DESTROY: |
| /* Unexpected GDK_DESTROY from the outside, ignore for |
| * child windows, handle like a GDK_DELETE for toplevels |
| */ |
| if (!gtk_widget_get_parent (event_widget)) |
| { |
| g_object_ref (event_widget); |
| if (!gtk_widget_event (event_widget, event) && |
| gtk_widget_get_realized (event_widget)) |
| gtk_widget_destroy (event_widget); |
| g_object_unref (event_widget); |
| } |
| break; |
| |
| case GDK_EXPOSE: |
| if (event->any.window && gtk_widget_get_double_buffered (event_widget)) |
| { |
| gdk_window_begin_paint_region (event->any.window, event->expose.region); |
| gtk_widget_send_expose (event_widget, event); |
| gdk_window_end_paint (event->any.window); |
| } |
| else |
| { |
| /* The app may paint with a previously allocated cairo_t, |
| * which will draw directly to the window. We can't catch cairo |
| * draw operations to automatically flush the window, thus we |
| * need to explicitly flush any outstanding moves or double |
| * buffering |
| */ |
| gdk_window_flush (event->any.window); |
| gtk_widget_send_expose (event_widget, event); |
| } |
| break; |
| |
| case GDK_PROPERTY_NOTIFY: |
| case GDK_FOCUS_CHANGE: |
| case GDK_CONFIGURE: |
| case GDK_MAP: |
| case GDK_UNMAP: |
| case GDK_SELECTION_CLEAR: |
| case GDK_SELECTION_REQUEST: |
| case GDK_SELECTION_NOTIFY: |
| case GDK_CLIENT_EVENT: |
| case GDK_VISIBILITY_NOTIFY: |
| case GDK_WINDOW_STATE: |
| case GDK_GRAB_BROKEN: |
| case GDK_DAMAGE: |
| if (!_gtk_widget_captured_event (event_widget, event)) |
| gtk_widget_event (event_widget, event); |
| break; |
| |
| case GDK_SCROLL: |
| case GDK_BUTTON_PRESS: |
| case GDK_2BUTTON_PRESS: |
| case GDK_3BUTTON_PRESS: |
| case GDK_TOUCH_BEGIN: |
| if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) |
| gtk_propagate_event (grab_widget, event); |
| break; |
| |
| case GDK_KEY_PRESS: |
| case GDK_KEY_RELEASE: |
| if (gtk_invoke_key_snoopers (grab_widget, event)) |
| break; |
| |
| /* make focus visible in a window that receives a key event */ |
| { |
| GtkWidget *window; |
| GtkPolicyType visible_focus; |
| |
| window = gtk_widget_get_toplevel (grab_widget); |
| g_object_get (gtk_widget_get_settings (grab_widget), "gtk-visible-focus", &visible_focus, NULL); |
| if (GTK_IS_WINDOW (window) && visible_focus != GTK_POLICY_NEVER) |
| gtk_window_set_focus_visible (GTK_WINDOW (window), TRUE); |
| } |
| |
| /* Catch alt press to enable auto-mnemonics; |
| * menus are handled elsewhere |
| * FIXME: this does not work with mnemonic modifiers other than Alt |
| */ |
| if ((event->key.keyval == GDK_KEY_Alt_L || event->key.keyval == GDK_KEY_Alt_R) && |
| ((event->key.state & (gtk_accelerator_get_default_mod_mask ()) & ~(GDK_RELEASE_MASK|GDK_MOD1_MASK)) == 0) && |
| !GTK_IS_MENU_SHELL (grab_widget)) |
| { |
| gboolean auto_mnemonics; |
| |
| g_object_get (gtk_widget_get_settings (grab_widget), |
| "gtk-auto-mnemonics", &auto_mnemonics, NULL); |
| |
| if (auto_mnemonics) |
| { |
| gboolean mnemonics_visible; |
| GtkWidget *window; |
| |
| mnemonics_visible = (event->type == GDK_KEY_PRESS); |
| |
| window = gtk_widget_get_toplevel (grab_widget); |
| if (GTK_IS_WINDOW (window)) |
| { |
| if (mnemonics_visible) |
| _gtk_window_set_auto_mnemonics_visible (GTK_WINDOW (window)); |
| else |
| gtk_window_set_mnemonics_visible (GTK_WINDOW (window), FALSE); |
| } |
| } |
| } |
| /* else fall through */ |
| case GDK_MOTION_NOTIFY: |
| case GDK_BUTTON_RELEASE: |
| case GDK_PROXIMITY_IN: |
| case GDK_PROXIMITY_OUT: |
| case GDK_TOUCH_UPDATE: |
| case GDK_TOUCH_END: |
| case GDK_TOUCH_CANCEL: |
| if (!_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) |
| gtk_propagate_event (grab_widget, event); |
| break; |
| |
| case GDK_ENTER_NOTIFY: |
| if (event->crossing.detail != GDK_NOTIFY_VIRTUAL && |
| event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL) |
| _gtk_widget_set_device_window (event_widget, |
| gdk_event_get_device (event), |
| event->any.window); |
| if (gtk_widget_is_sensitive (grab_widget) && |
| !_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) |
| gtk_widget_event (grab_widget, event); |
| break; |
| |
| case GDK_LEAVE_NOTIFY: |
| if (event->crossing.detail != GDK_NOTIFY_VIRTUAL && |
| event->crossing.detail != GDK_NOTIFY_NONLINEAR_VIRTUAL) |
| _gtk_widget_set_device_window (event_widget, |
| gdk_event_get_device (event), |
| NULL); |
| if (gtk_widget_is_sensitive (grab_widget) && |
| !_gtk_propagate_captured_event (grab_widget, event, topmost_widget)) |
| gtk_widget_event (grab_widget, event); |
| break; |
| |
| case GDK_DRAG_STATUS: |
| case GDK_DROP_FINISHED: |
| _gtk_drag_source_handle_event (event_widget, event); |
| break; |
| case GDK_DRAG_ENTER: |
| case GDK_DRAG_LEAVE: |
| case GDK_DRAG_MOTION: |
| case GDK_DROP_START: |
| _gtk_drag_dest_handle_event (event_widget, event); |
| break; |
| default: |
| g_assert_not_reached (); |
| break; |
| } |
| |
| if (event->type == GDK_ENTER_NOTIFY |
| || event->type == GDK_LEAVE_NOTIFY |
| || event->type == GDK_BUTTON_PRESS |
| || event->type == GDK_2BUTTON_PRESS |
| || event->type == GDK_3BUTTON_PRESS |
| || event->type == GDK_KEY_PRESS |
| || event->type == GDK_DRAG_ENTER |
| || event->type == GDK_GRAB_BROKEN |
| || event->type == GDK_MOTION_NOTIFY |
| || event->type == GDK_TOUCH_UPDATE |
| || event->type == GDK_SCROLL) |
| { |
| _gtk_tooltip_handle_event (event); |
| } |
| |
| tmp_list = current_events; |
| current_events = g_list_remove_link (current_events, tmp_list); |
| g_list_free_1 (tmp_list); |
| |
| if (rewritten_event) |
| gdk_event_free (rewritten_event); |
| } |
| |
| /** |
| * gtk_true: |
| * |
| * All this function does it to return %TRUE. |
| * |
| * This can be useful for example if you want to inhibit the deletion |
| * of a window. Of course you should not do this as the user expects |
| * a reaction from clicking the close icon of the window... |
| * |
| * <example> |
| * <title>A persistent window</title> |
| * <programlisting> |
| * #include <gtk/gtk.h>< |
| * |
| * int |
| * main (int argc, char **argv) |
| * { |
| * GtkWidget *win, *but; |
| * |
| * gtk_init (&argc, &argv); |
| * |
| * win = gtk_window_new (GTK_WINDOW_TOPLEVEL); |
| * g_signal_connect (win, "delete-event", |
| * G_CALLBACK (gtk_true), NULL); |
| * g_signal_connect (win, "destroy", |
| * G_CALLBACK (gtk_main_quit), NULL); |
| * |
| * but = gtk_button_new_with_label ("Close yourself. I mean it!"); |
| * g_signal_connect_swapped (but, "clicked", |
| * G_CALLBACK (gtk_object_destroy), win); |
| * gtk_container_add (GTK_CONTAINER (win), but); |
| * |
| * gtk_widget_show_all (win); |
| * |
| * gtk_main (); |
| * |
| * return 0; |
| * } |
| * </programlisting> |
| * </example> |
| * |
| * Returns: %TRUE |
| */ |
| gboolean |
| gtk_true (void) |
| { |
| return TRUE; |
| } |
| |
| /** |
| * gtk_false: |
| * |
| * Analogical to gtk_true(), this function does nothing |
| * but always returns %FALSE. |
| * |
| * Returns: %FALSE |
| */ |
| gboolean |
| gtk_false (void) |
| { |
| return FALSE; |
| } |
| |
| static GtkWindowGroup * |
| gtk_main_get_window_group (GtkWidget *widget) |
| { |
| GtkWidget *toplevel = NULL; |
| |
| if (widget) |
| toplevel = gtk_widget_get_toplevel (widget); |
| |
| if (GTK_IS_WINDOW (toplevel)) |
| return gtk_window_get_group (GTK_WINDOW (toplevel)); |
| else |
| return gtk_window_get_group (NULL); |
| } |
| |
| typedef struct |
| { |
| GtkWidget *old_grab_widget; |
| GtkWidget *new_grab_widget; |
| gboolean was_grabbed; |
| gboolean is_grabbed; |
| gboolean from_grab; |
| GList *notified_windows; |
| GdkDevice *device; |
| } GrabNotifyInfo; |
| |
| static void |
| synth_crossing_for_grab_notify (GtkWidget *from, |
| GtkWidget *to, |
| GrabNotifyInfo *info, |
| GList *devices, |
| GdkCrossingMode mode) |
| { |
| while (devices) |
| { |
| GdkDevice *device = devices->data; |
| GdkWindow *from_window, *to_window; |
| |
| /* Do not propagate events more than once to |
| * the same windows if non-multidevice aware. |
| */ |
| if (!from) |
| from_window = NULL; |
| else |
| { |
| from_window = _gtk_widget_get_device_window (from, device); |
| |
| if (from_window && |
| !gdk_window_get_support_multidevice (from_window) && |
| g_list_find (info->notified_windows, from_window)) |
| from_window = NULL; |
| } |
| |
| if (!to) |
| to_window = NULL; |
| else |
| { |
| to_window = _gtk_widget_get_device_window (to, device); |
| |
| if (to_window && |
| !gdk_window_get_support_multidevice (to_window) && |
| g_list_find (info->notified_windows, to_window)) |
| to_window = NULL; |
| } |
| |
| if (from_window || to_window) |
| { |
| _gtk_widget_synthesize_crossing ((from_window) ? from : NULL, |
| (to_window) ? to : NULL, |
| device, mode); |
| |
| if (from_window) |
| info->notified_windows = g_list_prepend (info->notified_windows, from_window); |
| |
| if (to_window) |
| info->notified_windows = g_list_prepend (info->notified_windows, to_window); |
| } |
| |
| devices = devices->next; |
| } |
| } |
| |
| static void |
| gtk_grab_notify_foreach (GtkWidget *child, |
| gpointer data) |
| { |
| GrabNotifyInfo *info = data; |
| gboolean was_grabbed, is_grabbed, was_shadowed, is_shadowed; |
| GList *devices; |
| |
| was_grabbed = info->was_grabbed; |
| is_grabbed = info->is_grabbed; |
| |
| info->was_grabbed = info->was_grabbed || (child == info->old_grab_widget); |
| info->is_grabbed = info->is_grabbed || (child == info->new_grab_widget); |
| |
| was_shadowed = info->old_grab_widget && !info->was_grabbed; |
| is_shadowed = info->new_grab_widget && !info->is_grabbed; |
| |
| g_object_ref (child); |
| |
| if ((was_shadowed || is_shadowed) && GTK_IS_CONTAINER (child)) |
| gtk_container_forall (GTK_CONTAINER (child), gtk_grab_notify_foreach, info); |
| |
| if (info->device && |
| _gtk_widget_get_device_window (child, info->device)) |
| { |
| /* Device specified and is on widget */ |
| devices = g_list_prepend (NULL, info->device); |
| } |
| else |
| devices = _gtk_widget_list_devices (child); |
| |
| if (is_shadowed) |
| { |
| _gtk_widget_set_shadowed (child, TRUE); |
| if (!was_shadowed && devices && |
| gtk_widget_is_sensitive (child)) |
| synth_crossing_for_grab_notify (child, info->new_grab_widget, |
| info, devices, |
| GDK_CROSSING_GTK_GRAB); |
| } |
| else |
| { |
| _gtk_widget_set_shadowed (child, FALSE); |
| if (was_shadowed && devices && |
| gtk_widget_is_sensitive (child)) |
| synth_crossing_for_grab_notify (info->old_grab_widget, child, |
| info, devices, |
| info->from_grab ? GDK_CROSSING_GTK_GRAB : |
| GDK_CROSSING_GTK_UNGRAB); |
| } |
| |
| if (was_shadowed != is_shadowed) |
| _gtk_widget_grab_notify (child, was_shadowed); |
| |
| g_object_unref (child); |
| g_list_free (devices); |
| |
| info->was_grabbed = was_grabbed; |
| info->is_grabbed = is_grabbed; |
| } |
| |
| static void |
| gtk_grab_notify (GtkWindowGroup *group, |
| GdkDevice *device, |
| GtkWidget *old_grab_widget, |
| GtkWidget *new_grab_widget, |
| gboolean from_grab) |
| { |
| GList *toplevels; |
| GrabNotifyInfo info = { 0 }; |
| |
| if (old_grab_widget == new_grab_widget) |
| return; |
| |
| info.old_grab_widget = old_grab_widget; |
| info.new_grab_widget = new_grab_widget; |
| info.from_grab = from_grab; |
| info.device = device; |
| |
| g_object_ref (group); |
| |
| toplevels = gtk_window_list_toplevels (); |
| g_list_foreach (toplevels, (GFunc)g_object_ref, NULL); |
| |
| while (toplevels) |
| { |
| GtkWindow *toplevel = toplevels->data; |
| toplevels = g_list_delete_link (toplevels, toplevels); |
| |
| info.was_grabbed = FALSE; |
| info.is_grabbed = FALSE; |
| |
| if (group == gtk_window_get_group (toplevel)) |
| gtk_grab_notify_foreach (GTK_WIDGET (toplevel), &info); |
| g_object_unref (toplevel); |
| } |
| |
| g_list_free (info.notified_windows); |
| g_object_unref (group); |
| } |
| |
| /** |
| * gtk_grab_add: (method) |
| * @widget: The widget that grabs keyboard and pointer events |
| * |
| * Makes @widget the current grabbed widget. |
| * |
| * This means that interaction with other widgets in the same |
| * application is blocked and mouse as well as keyboard events |
| * are delivered to this widget. |
| * |
| * If @widget is not sensitive, it is not set as the current |
| * grabbed widget and this function does nothing. |
| */ |
| void |
| gtk_grab_add (GtkWidget *widget) |
| { |
| GtkWindowGroup *group; |
| GtkWidget *old_grab_widget; |
| |
| g_return_if_fail (widget != NULL); |
| |
| if (!gtk_widget_has_grab (widget) && gtk_widget_is_sensitive (widget)) |
| { |
| _gtk_widget_set_has_grab (widget, TRUE); |
| |
| group = gtk_main_get_window_group (widget); |
| |
| old_grab_widget = gtk_window_group_get_current_grab (group); |
| |
| g_object_ref (widget); |
| _gtk_window_group_add_grab (group, widget); |
| |
| gtk_grab_notify (group, NULL, old_grab_widget, widget, TRUE); |
| } |
| } |
| |
| /** |
| * gtk_grab_get_current: |
| * |
| * Queries the current grab of the default window group. |
| * |
| * Return value: (transfer none): The widget which currently |
| * has the grab or %NULL if no grab is active |
| */ |
| GtkWidget* |
| gtk_grab_get_current (void) |
| { |
| GtkWindowGroup *group; |
| |
| group = gtk_main_get_window_group (NULL); |
| |
| return gtk_window_group_get_current_grab (group); |
| } |
| |
| /** |
| * gtk_grab_remove: (method) |
| * @widget: The widget which gives up the grab |
| * |
| * Removes the grab from the given widget. |
| * |
| * You have to pair calls to gtk_grab_add() and gtk_grab_remove(). |
| * |
| * If @widget does not have the grab, this function does nothing. |
| */ |
| void |
| gtk_grab_remove (GtkWidget *widget) |
| { |
| GtkWindowGroup *group; |
| GtkWidget *new_grab_widget; |
| |
| g_return_if_fail (widget != NULL); |
| |
| if (gtk_widget_has_grab (widget)) |
| { |
| _gtk_widget_set_has_grab (widget, FALSE); |
| |
| group = gtk_main_get_window_group (widget); |
| _gtk_window_group_remove_grab (group, widget); |
| new_grab_widget = gtk_window_group_get_current_grab (group); |
| |
| gtk_grab_notify (group, NULL, widget, new_grab_widget, FALSE); |
| |
| g_object_unref (widget); |
| } |
| } |
| |
| /** |
| * gtk_device_grab_add: |
| * @widget: a #GtkWidget |
| * @device: a #GdkDevice to grab on. |
| * @block_others: %TRUE to prevent other devices to interact with @widget. |
| * |
| * Adds a GTK+ grab on @device, so all the events on @device and its |
| * associated pointer or keyboard (if any) are delivered to @widget. |
| * If the @block_others parameter is %TRUE, any other devices will be |
| * unable to interact with @widget during the grab. |
| * |
| * Since: 3.0 |
| */ |
| void |
| gtk_device_grab_add (GtkWidget *widget, |
| GdkDevice *device, |
| gboolean block_others) |
| { |
| GtkWindowGroup *group; |
| GtkWidget *old_grab_widget; |
| |
| g_return_if_fail (GTK_IS_WIDGET (widget)); |
| g_return_if_fail (GDK_IS_DEVICE (device)); |
| |
| group = gtk_main_get_window_group (widget); |
| old_grab_widget = gtk_window_group_get_current_device_grab (group, device); |
| |
| if (old_grab_widget != widget) |
| _gtk_window_group_add_device_grab (group, widget, device, block_others); |
| |
| gtk_grab_notify (group, device, old_grab_widget, widget, TRUE); |
| } |
| |
| /** |
| * gtk_device_grab_remove: |
| * @widget: a #GtkWidget |
| * @device: a #GdkDevice |
| * |
| * Removes a device grab from the given widget. |
| * |
| * You have to pair calls to gtk_device_grab_add() and |
| * gtk_device_grab_remove(). |
| * |
| * Since: 3.0 |
| */ |
| void |
| gtk_device_grab_remove (GtkWidget *widget, |
| GdkDevice *device) |
| { |
| GtkWindowGroup *group; |
| GtkWidget *new_grab_widget; |
| |
| g_return_if_fail (GTK_IS_WIDGET (widget)); |
| g_return_if_fail (GDK_IS_DEVICE (device)); |
| |
| group = gtk_main_get_window_group (widget); |
| _gtk_window_group_remove_device_grab (group, widget, device); |
| new_grab_widget = gtk_window_group_get_current_device_grab (group, device); |
| |
| gtk_grab_notify (group, device, widget, new_grab_widget, FALSE); |
| } |
| |
| /** |
| * gtk_key_snooper_install: (skip) |
| * @snooper: a #GtkKeySnoopFunc |
| * @func_data: data to pass to @snooper |
| * |
| * Installs a key snooper function, which will get called on all |
| * key events before delivering them normally. |
| * |
| * Returns: a unique id for this key snooper for use with |
| * gtk_key_snooper_remove(). |
| * |
| * Deprecated: 3.4: Key snooping should not be done. Events should |
| * be handled by widgets. |
| */ |
| guint |
| gtk_key_snooper_install (GtkKeySnoopFunc snooper, |
| gpointer func_data) |
| { |
| GtkKeySnooperData *data; |
| static guint snooper_id = 1; |
| |
| g_return_val_if_fail (snooper != NULL, 0); |
| |
| data = g_new (GtkKeySnooperData, 1); |
| data->func = snooper; |
| data->func_data = func_data; |
| data->id = snooper_id++; |
| key_snoopers = g_slist_prepend (key_snoopers, data); |
| |
| return data->id; |
| } |
| |
| /** |
| * gtk_key_snooper_remove: |
| * @snooper_handler_id: Identifies the key snooper to remove |
| * |
| * Removes the key snooper function with the given id. |
| * |
| * Deprecated: 3.4: Key snooping should not be done. Events should |
| * be handled by widgets. |
| */ |
| void |
| gtk_key_snooper_remove (guint snooper_id) |
| { |
| GtkKeySnooperData *data = NULL; |
| GSList *slist; |
| |
| slist = key_snoopers; |
| while (slist) |
| { |
| data = slist->data; |
| if (data->id == snooper_id) |
| break; |
| |
| slist = slist->next; |
| data = NULL; |
| } |
| if (data) |
| { |
| key_snoopers = g_slist_remove (key_snoopers, data); |
| g_free (data); |
| } |
| } |
| |
| static gint |
| gtk_invoke_key_snoopers (GtkWidget *grab_widget, |
| GdkEvent *event) |
| { |
| GSList *slist; |
| gint return_val = FALSE; |
| |
| return_val = _gail_util_key_snooper (grab_widget, (GdkEventKey *) event); |
| |
| slist = key_snoopers; |
| while (slist && !return_val) |
| { |
| GtkKeySnooperData *data; |
| |
| data = slist->data; |
| slist = slist->next; |
| return_val = (*data->func) (grab_widget, (GdkEventKey*) event, data->func_data); |
| } |
| |
| return return_val; |
| } |
| |
| /** |
| * gtk_get_current_event: |
| * |
| * Obtains a copy of the event currently being processed by GTK+. |
| * |
| * For example, if you are handling a #GtkButton::clicked signal, |
| * the current event will be the #GdkEventButton that triggered |
| * the ::clicked signal. |
| * |
| * Return value: (transfer full): a copy of the current event, or |
| * %NULL if there is no current event. The returned event must be |
| * freed with gdk_event_free(). |
| */ |
| GdkEvent* |
| gtk_get_current_event (void) |
| { |
| if (current_events) |
| return gdk_event_copy (current_events->data); |
| else |
| return NULL; |
| } |
| |
| /** |
| * gtk_get_current_event_time: |
| * |
| * If there is a current event and it has a timestamp, |
| * return that timestamp, otherwise return %GDK_CURRENT_TIME. |
| * |
| * Return value: the timestamp from the current event, |
| * or %GDK_CURRENT_TIME. |
| */ |
| guint32 |
| gtk_get_current_event_time (void) |
| { |
| if (current_events) |
| return gdk_event_get_time (current_events->data); |
| else |
| return GDK_CURRENT_TIME; |
| } |
| |
| /** |
| * gtk_get_current_event_state: |
| * @state: (out): a location to store the state of the current event |
| * |
| * If there is a current event and it has a state field, place |
| * that state field in @state and return %TRUE, otherwise return |
| * %FALSE. |
| * |
| * Return value: %TRUE if there was a current event and it |
| * had a state field |
| */ |
| gboolean |
| gtk_get_current_event_state (GdkModifierType *state) |
| { |
| g_return_val_if_fail (state != NULL, FALSE); |
| |
| if (current_events) |
| return gdk_event_get_state (current_events->data, state); |
| else |
| { |
| *state = 0; |
| return FALSE; |
| } |
| } |
| |
| /** |
| * gtk_get_current_event_device: |
| * |
| * If there is a current event and it has a device, return that |
| * device, otherwise return %NULL. |
| * |
| * Returns: (transfer none): a #GdkDevice, or %NULL |
| */ |
| GdkDevice * |
| gtk_get_current_event_device (void) |
| { |
| if (current_events) |
| return gdk_event_get_device (current_events->data); |
| else |
| return NULL; |
| } |
| |
| /** |
| * gtk_get_event_widget: |
| * @event: a #GdkEvent |
| * |
| * If @event is %NULL or the event was not associated with any widget, |
| * returns %NULL, otherwise returns the widget that received the event |
| * originally. |
| * |
| * Return value: (transfer none): the widget that originally |
| * received @event, or %NULL |
| */ |
| GtkWidget* |
| gtk_get_event_widget (GdkEvent *event) |
| { |
| GtkWidget *widget; |
| gpointer widget_ptr; |
| |
| widget = NULL; |
| if (event && event->any.window && |
| (event->type == GDK_DESTROY || !gdk_window_is_destroyed (event->any.window))) |
| { |
| gdk_window_get_user_data (event->any.window, &widget_ptr); |
| widget = widget_ptr; |
| } |
| |
| return widget; |
| } |
| |
| static gboolean |
| propagate_event_up (GtkWidget *widget, |
| GdkEvent *event, |
| GtkWidget *topmost) |
| { |
| gboolean handled_event = FALSE; |
| |
| /* Propagate event up the widget tree so that |
| * parents can see the button and motion |
| * events of the children. |
| */ |
| while (TRUE) |
| { |
| GtkWidget *tmp; |
| |
| g_object_ref (widget); |
| |
| /* Scroll events are special cased here because it |
| * feels wrong when scrolling a GtkViewport, say, |
| * to have children of the viewport eat the scroll |
| * event |
| */ |
| if (!gtk_widget_is_sensitive (widget)) |
| handled_event = event->type != GDK_SCROLL; |
| else |
| handled_event = gtk_widget_event (widget, event); |
| |
| tmp = gtk_widget_get_parent (widget); |
| g_object_unref (widget); |
| |
| if (widget == topmost) |
| break; |
| |
| widget = tmp; |
| |
| if (handled_event || !widget) |
| break; |
| } |
| |
| return handled_event; |
| } |
| |
| static gboolean |
| propagate_event_down (GtkWidget *widget, |
| GdkEvent *event, |
| GtkWidget *topmost) |
| { |
| gint handled_event = FALSE; |
| GList *widgets = NULL; |
| GList *l; |
| |
| widgets = g_list_prepend (widgets, g_object_ref (widget)); |
| while (widget && widget != topmost) |
| { |
| widget = gtk_widget_get_parent (widget); |
| if (!widget) |
| break; |
| |
| widgets = g_list_prepend (widgets, g_object_ref (widget)); |
| |
| if (widget == topmost) |
| break; |
| } |
| |
| for (l = widgets; l && !handled_event; l = g_list_next (l)) |
| { |
| widget = (GtkWidget *)l->data; |
| |
| if (!gtk_widget_is_sensitive (widget)) |
| { |
| /* stop propagating on SCROLL, but don't handle the event, so it |
| * can propagate up again and reach its handling widget |
| */ |
| if (event->type == GDK_SCROLL) |
| break; |
| else |
| handled_event = TRUE; |
| } |
| else |
| handled_event = _gtk_widget_captured_event (widget, event); |
| } |
| g_list_free_full (widgets, (GDestroyNotify)g_object_unref); |
| |
| return handled_event; |
| } |
| |
| static gboolean |
| propagate_event (GtkWidget *widget, |
| GdkEvent *event, |
| gboolean captured, |
| GtkWidget *topmost) |
| { |
| gboolean handled_event = FALSE; |
| gboolean (* propagate_func) (GtkWidget *widget, GdkEvent *event); |
| |
| propagate_func = captured ? _gtk_widget_captured_event : gtk_widget_event; |
| |
| if (event->type == GDK_KEY_PRESS || event->type == GDK_KEY_RELEASE) |
| { |
| /* Only send key events within Window widgets to the Window |
| * The Window widget will in turn pass the |
| * key event on to the currently focused widget |
| * for that window. |
| */ |
| GtkWidget *window; |
| |
| window = gtk_widget_get_toplevel (widget); |
| if (GTK_IS_WINDOW (window)) |
| { |
| g_object_ref (widget); |
| /* If there is a grab within the window, give the grab widget |
| * a first crack at the key event |
| */ |
| if (widget != window && gtk_widget_has_grab (widget)) |
| handled_event = propagate_func (widget, event); |
| |
| if (!handled_event) |
| { |
| window = gtk_widget_get_toplevel (widget); |
| if (GTK_IS_WINDOW (window)) |
| { |
| if (gtk_widget_is_sensitive (window)) |
| handled_event = propagate_func (window, event); |
| } |
| } |
| |
| g_object_unref (widget); |
| return handled_event; |
| } |
| } |
| |
| /* Other events get propagated up/down the widget tree */ |
| return captured ? |
| propagate_event_down (widget, event, topmost) : |
| propagate_event_up (widget, event, topmost); |
| } |
| |
| /** |
| * gtk_propagate_event: |
| * @widget: a #GtkWidget |
| * @event: an event |
| * |
| * Sends an event to a widget, propagating the event to parent widgets |
| * if the event remains unhandled. |
| * |
| * Events received by GTK+ from GDK normally begin in gtk_main_do_event(). |
| * Depending on the type of event, existence of modal dialogs, grabs, etc., |
| * the event may be propagated; if so, this function is used. |
| * |
| * gtk_propagate_event() calls gtk_widget_event() on each widget it |
| * decides to send the event to. So gtk_widget_event() is the lowest-level |
| * function; it simply emits the #GtkWidget::event and possibly an |
| * event-specific signal on a widget. gtk_propagate_event() is a bit |
| * higher-level, and gtk_main_do_event() is the highest level. |
| * |
| * All that said, you most likely don't want to use any of these |
| * functions; synthesizing events is rarely needed. There are almost |
| * certainly better ways to achieve your goals. For example, use |
| * gdk_window_invalidate_rect() or gtk_widget_queue_draw() instead |
| * of making up expose events. |
| */ |
| void |
| gtk_propagate_event (GtkWidget *widget, |
| GdkEvent *event) |
| { |
| g_return_if_fail (GTK_IS_WIDGET (widget)); |
| g_return_if_fail (event != NULL); |
| |
| propagate_event (widget, event, FALSE, NULL); |
| } |
| |
| gboolean |
| _gtk_propagate_captured_event (GtkWidget *widget, |
| GdkEvent *event, |
| GtkWidget *topmost) |
| { |
| return propagate_event (widget, event, TRUE, topmost); |
| } |