X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=gtk%2Fgtkmain.c;h=55df71e992c22c3611a881e583df3993ae4cbea3;hb=fa966c6aa755e27c6d470402bf06e86cc5d2fdc8;hp=61e1556580c6b25110fe8cbda847839d3eb78e0a;hpb=c7826b5f63930c960cdb53aa4935ee1f8f7f3b18;p=~andy%2Fgtk diff --git a/gtk/gtkmain.c b/gtk/gtkmain.c index 61e155658..55df71e99 100644 --- a/gtk/gtkmain.c +++ b/gtk/gtkmain.c @@ -24,11 +24,11 @@ * GTK+ at ftp://ftp.gtk.org/pub/gtk/. */ -#ifdef GDK_WINDOWING_X11 -#include /* so we get the right setlocale */ -#else +#include + +#include "gdkconfig.h" + #include -#endif #ifdef HAVE_BIND_TEXTDOMAIN_CODESET #include @@ -38,19 +38,34 @@ #include #include #include +#ifdef G_OS_UNIX +#include +#include /* For uid_t, gid_t */ +#endif +#ifdef G_OS_WIN32 +#define STRICT +#include +#undef STRICT +#endif + +#include /* For pango_split_file_list */ + +#include "gtkalias.h" +#include "gtkintl.h" + +#include "gtkaccelmap.h" +#include "gtkbox.h" +#include "gtkclipboard.h" #include "gtkdnd.h" -#include "gtkcompat.h" +#include "gtkversion.h" #include "gtkmain.h" #include "gtkrc.h" #include "gtkselection.h" -#include "gtksignal.h" +#include "gtksettings.h" #include "gtkwidget.h" #include "gtkwindow.h" #include "gtkprivate.h" -#include "gdk/gdki18n.h" -#include "config.h" #include "gtkdebug.h" -#include "gtkintl.h" /* Private type definitions */ @@ -58,6 +73,7 @@ typedef struct _GtkInitFunction GtkInitFunction; typedef struct _GtkQuitFunction GtkQuitFunction; typedef struct _GtkClosure GtkClosure; typedef struct _GtkKeySnooperData GtkKeySnooperData; +typedef struct _GtkModuleInfo GtkModuleInfo; struct _GtkInitFunction { @@ -89,7 +105,12 @@ struct _GtkKeySnooperData guint id; }; -static void gtk_exit_func (void); +struct _GtkModuleInfo +{ + GtkModuleInitFunc init_func; + GtkModuleDisplayInitFunc display_init_func; +}; + static gint gtk_quit_invoke_function (GtkQuitFunction *quitf); static void gtk_quit_destroy (GtkQuitFunction *quitf); static gint gtk_invoke_key_snoopers (GtkWidget *grab_widget, @@ -108,21 +129,27 @@ static void gtk_message (gchar *str); static void gtk_print (gchar *str); #endif +static GtkWindowGroup *gtk_main_get_window_group (GtkWidget *widget); + const guint gtk_major_version = GTK_MAJOR_VERSION; const guint gtk_minor_version = GTK_MINOR_VERSION; const guint gtk_micro_version = GTK_MICRO_VERSION; const guint gtk_binary_age = GTK_BINARY_AGE; const guint gtk_interface_age = GTK_INTERFACE_AGE; +static GSList *gtk_modules; + +/* Saved argc,argv for delayed module initialization + */ +static gint gtk_argc = 0; +static gchar **gtk_argv = NULL; + static guint gtk_main_loop_level = 0; static gint gtk_initialized = FALSE; static GList *current_events = NULL; static GSList *main_loops = NULL; /* stack of currently executing main loops */ -static GSList *grabs = NULL; /* A stack of unique grabs. The grabbing - * widget is the first one on the list. - */ static GList *init_functions = NULL; /* A list of init functions. */ static GList *quit_functions = NULL; /* A list of quit functions. @@ -131,95 +158,557 @@ static GMemChunk *quit_mem_chunk = NULL; static GSList *key_snoopers = NULL; -static GdkVisual *gtk_visual; /* The visual to be used in creating new - * widgets. - */ -static GdkColormap *gtk_colormap; /* The colormap to be used in creating new - * widgets. - */ - guint gtk_debug_flags = 0; /* Global GTK debug flag */ #ifdef G_ENABLE_DEBUG static const GDebugKey gtk_debug_keys[] = { - {"objects", GTK_DEBUG_OBJECTS}, {"misc", GTK_DEBUG_MISC}, - {"signals", GTK_DEBUG_SIGNALS}, - {"dnd", GTK_DEBUG_DND}, {"plugsocket", GTK_DEBUG_PLUGSOCKET}, - {"text", GTK_DEBUG_TEXT} + {"text", GTK_DEBUG_TEXT}, + {"tree", GTK_DEBUG_TREE}, + {"updates", GTK_DEBUG_UPDATES}, + {"keybindings", GTK_DEBUG_KEYBINDINGS}, + {"multihead", GTK_DEBUG_MULTIHEAD} }; static const guint gtk_ndebug_keys = sizeof (gtk_debug_keys) / sizeof (GDebugKey); #endif /* G_ENABLE_DEBUG */ +/** + * gtk_check_version: + * @required_major: the required major version. + * @required_minor: the required major version. + * @required_micro: the required major 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 + * 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. + **/ 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_minor > GTK_MINOR_VERSION) - return "Gtk+ version too old (minor mismatch)"; - if (required_minor < GTK_MINOR_VERSION) - return "Gtk+ version too new (minor mismatch)"; - if (required_micro < GTK_MICRO_VERSION - GTK_BINARY_AGE) + if (required_effective_micro < gtk_effective_micro - GTK_BINARY_AGE) return "Gtk+ version too new (micro mismatch)"; - if (required_micro > GTK_MICRO_VERSION) + if (required_effective_micro > gtk_effective_micro) return "Gtk+ version too old (micro mismatch)"; return NULL; } -#ifdef __EMX__ -static gchar *add_dll_suffix(gchar *module_name) +/* 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) { - gchar *suffix = strrchr(module_name, '.'); - - if (!suffix || stricmp(suffix, ".dll")) +/* 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 */ { - gchar *old = module_name; - - module_name = g_strconcat (module_name, ".dll", NULL); - g_free (old); + 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); } - return (module_name); -} #endif + return TRUE; +} + +#ifdef G_OS_WIN32 + +G_WIN32_DLLMAIN_FOR_DLL_NAME(static, dll_name) + +const gchar * +_gtk_get_datadir (void) +{ + static char *gtk_datadir = NULL; + if (gtk_datadir == NULL) + gtk_datadir = g_win32_get_package_installation_subdirectory + (GETTEXT_PACKAGE, dll_name, "share"); + + return gtk_datadir; +} + +const gchar * +_gtk_get_libdir (void) +{ + static char *gtk_libdir = NULL; + if (gtk_libdir == NULL) + gtk_libdir = g_win32_get_package_installation_subdirectory + (GETTEXT_PACKAGE, dll_name, "lib"); + + return gtk_libdir; +} + +const gchar * +_gtk_get_localedir (void) +{ + static char *gtk_localedir = NULL; + if (gtk_localedir == NULL) + gtk_localedir = g_win32_get_package_installation_subdirectory + (GETTEXT_PACKAGE, dll_name, "lib\\locale"); + + return gtk_localedir; +} + +const gchar * +_gtk_get_sysconfdir (void) +{ + static char *gtk_sysconfdir = NULL; + if (gtk_sysconfdir == NULL) + gtk_sysconfdir = g_win32_get_package_installation_subdirectory + (GETTEXT_PACKAGE, dll_name, "etc"); + + return gtk_sysconfdir; +} + +const gchar * +_gtk_get_data_prefix (void) +{ + static char *gtk_data_prefix = NULL; + if (gtk_data_prefix == NULL) + gtk_data_prefix = g_win32_get_package_installation_directory + (GETTEXT_PACKAGE, dll_name); + return gtk_data_prefix; +} + +#endif /* G_OS_WIN32 */ + +static gchar ** +get_module_path (void) +{ + const gchar *module_path_env; + const gchar *exe_prefix; + const gchar *home_dir; + gchar *home_gtk_dir = NULL; + gchar *module_path; + gchar *default_dir; + static gchar **result = NULL; + + if (result) + return result; + + home_dir = g_get_home_dir(); + if (home_dir) + home_gtk_dir = g_build_filename (home_dir, ".gtk-2.0", NULL); + + module_path_env = g_getenv ("GTK_PATH"); + exe_prefix = g_getenv ("GTK_EXE_PREFIX"); + + if (exe_prefix) + default_dir = g_build_filename (exe_prefix, "lib", "gtk-2.0", NULL); + else + default_dir = g_build_filename (GTK_LIBDIR, "gtk-2.0", NULL); + + if (module_path_env && home_gtk_dir) + module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + module_path_env, home_gtk_dir, default_dir, NULL); + else if (module_path_env) + module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + module_path_env, default_dir, NULL); + else if (home_gtk_dir) + module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + home_gtk_dir, default_dir, NULL); + else + module_path = g_build_path (G_SEARCHPATH_SEPARATOR_S, + default_dir, NULL); + + g_free (home_gtk_dir); + g_free (default_dir); + + result = pango_split_file_list (module_path); + g_free (module_path); + + return result; +} + +/** + * _gtk_get_module_path: + * @type: the type of the module, for instance 'modules', 'engines', immodules' + * + * Determines the search path for a particular type of module. + * + * Return value: the search path for the module type. Free with g_strfreev(). + **/ +gchar ** +_gtk_get_module_path (const gchar *type) +{ + gchar **paths = get_module_path(); + gchar **path; + gchar **result; + gint count = 0; + + for (path = paths; *path; path++) + count++; + + result = g_new (gchar *, count * 4 + 1); + + count = 0; + for (path = get_module_path (); *path; path++) + { + gint use_version, use_host; + + for (use_version = TRUE; use_version >= FALSE; use_version--) + for (use_host = TRUE; use_host >= FALSE; use_host--) + { + gchar *tmp_dir; + + if (use_version && use_host) + tmp_dir = g_build_filename (*path, GTK_BINARY_VERSION, GTK_HOST, type, NULL); + else if (use_version) + tmp_dir = g_build_filename (*path, GTK_BINARY_VERSION, type, NULL); + else if (use_host) + tmp_dir = g_build_filename (*path, GTK_HOST, type, NULL); + else + tmp_dir = g_build_filename (*path, type, NULL); + + result[count++] = tmp_dir; + } + } + + result[count++] = NULL; + + return result; +} + +/* Like g_module_path, but use .la as the suffix + */ +static gchar* +module_build_la_path (const gchar *directory, + const gchar *module_name) +{ + gchar *filename; + gchar *result; + + if (strncmp (module_name, "lib", 3) == 0) + filename = (gchar *)module_name; + else + filename = g_strconcat ("lib", module_name, ".la", NULL); + + if (directory && *directory) + result = g_build_filename (directory, filename, NULL); + else + result = g_strdup (filename); + + if (filename != module_name) + g_free (filename); + + return result; +} + +/** + * _gtk_find_module: + * @name: the name of the module + * @type: the type of the module, for instance 'modules', 'engines', immodules' + * + * Looks for a dynamically loadable module named @name of type @type in the + * standard GTK+ module search path. + * + * Return value: the pathname to the found module, or %NULL if it wasn't found. + * Free with g_free(). + **/ +gchar * +_gtk_find_module (const gchar *name, + const gchar *type) +{ + gchar **paths; + gchar **path; + gchar *module_name = NULL; + + if (g_path_is_absolute (name)) + return g_strdup (name); + + paths = _gtk_get_module_path (type); + for (path = paths; *path; path++) + { + gchar *tmp_name; + + tmp_name = g_module_build_path (*path, name); + if (g_file_test (tmp_name, G_FILE_TEST_EXISTS)) + { + module_name = tmp_name; + goto found; + } + g_free(tmp_name); + + tmp_name = module_build_la_path (*path, name); + if (g_file_test (tmp_name, G_FILE_TEST_EXISTS)) + { + module_name = tmp_name; + goto found; + } + g_free(tmp_name); + } + + found: + g_strfreev (paths); + return module_name; +} + +static GModule * +find_module (const gchar *name) +{ + GModule *module; + gchar *module_name; + + module_name = _gtk_find_module (name, "modules"); + if (!module_name) + { + /* As last resort, try loading without an absolute path (using system + * library path) + */ + module_name = g_module_build_path (NULL, name); + } + + module = g_module_open (module_name, G_MODULE_BIND_LAZY); + g_free(module_name); + + return module; +} + +static GSList * +load_module (GSList *module_list, + const gchar *name) +{ + GtkModuleInitFunc modinit_func = NULL; + GtkModuleInfo *info; + GModule *module = NULL; + + if (g_module_supported ()) + { + module = find_module (name); + if (module && + g_module_symbol (module, "gtk_module_init", (gpointer *) &modinit_func) && + modinit_func) + { + if (!g_slist_find (module_list, (gconstpointer) modinit_func)) + { + g_module_make_resident (module); + info = g_new (GtkModuleInfo, 1); + + info->init_func = modinit_func; + g_module_symbol (module, "gtk_module_display_init", + (gpointer *) &info->display_init_func); + + module_list = g_slist_prepend (module_list, info); + } + else + { + g_module_close (module); + module = NULL; + } + } + } + if (!modinit_func) + { + g_message ("Failed to load module \"%s\": %s", + module ? g_module_name (module) : name, + g_module_error ()); + if (module) + g_module_close (module); + } + + return module_list; +} + +static GSList * +load_modules (const char *module_str) +{ + gchar **module_names = pango_split_file_list (module_str); + GSList *module_list = NULL; + gint i; + + for (i = 0; module_names[i]; i++) + module_list = load_module (module_list, module_names[i]); + + module_list = g_slist_reverse (module_list); + + g_strfreev (module_names); + + return module_list; +} + +static gboolean do_setlocale = TRUE; + +/** + * gtk_disable_setlocale: + * + * Prevents gtk_init() and gtk_init_check() from automatically + * calling setlocale (LC_ALL, ""). 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 (gtk_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 void +default_display_notify_cb (GdkDisplayManager *display_manager) +{ + GSList *slist; + + /* Initialize non-multihead-aware modules when the + * default display is first set to a non-NULL value. + */ + static gboolean initialized = FALSE; + + if (!gdk_display_get_default () || initialized) + return; + + initialized = TRUE; + + for (slist = gtk_modules; slist; slist = slist->next) + { + if (slist->data) + { + GtkModuleInfo *info = slist->data; + + if (!info->display_init_func) + info->init_func (>k_argc, >k_argv); + } + } +} + +static void +display_opened_cb (GdkDisplayManager *display_manager, + GdkDisplay *display) +{ + GSList *slist; + + for (slist = gtk_modules; slist; slist = slist->next) + { + if (slist->data) + { + GtkModuleInfo *info = slist->data; + + if (info->display_init_func) + info->display_init_func (display); + } + } +} +/** + * gtk_parse_args: + * @argc: a pointer to the number of command line arguments. + * @argv: 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. + * + * You shouldn't call this function explicitely if you are using + * gtk_init(), or gtk_init_check(). + * + * Return value: %TRUE if initialization succeeded, otherwise %FALSE. + **/ gboolean -gtk_init_check (int *argc, - char ***argv) +gtk_parse_args (int *argc, + char ***argv) { - GSList *gtk_modules = NULL; + GString *gtk_modules_string = NULL; GSList *slist; - gchar *env_string = NULL; + GdkDisplayManager *display_manager; + const gchar *env_string; if (gtk_initialized) return TRUE; + if (!check_setugid ()) + return FALSE; + #if 0 g_set_error_handler (gtk_error); g_set_warning_handler (gtk_warning); g_set_message_handler (gtk_message); g_set_print_handler (gtk_print); #endif - - /* Initialize "gdk". We pass along the 'argc' and 'argv' - * parameters as they contain information that GDK uses - */ - if (!gdk_init_check (argc, argv)) - return FALSE; + if (do_setlocale) + { + if (!setlocale (LC_ALL, "")) + g_warning ("Locale not supported by C library.\n\tUsing the fallback 'C' locale."); + } + + gdk_parse_args (argc, argv); gdk_event_handler_set ((GdkEventFunc)gtk_main_do_event, NULL, NULL); #ifdef G_ENABLE_DEBUG - env_string = getenv ("GTK_DEBUG"); + env_string = g_getenv ("GTK_DEBUG"); if (env_string != NULL) { gtk_debug_flags = g_parse_debug_string (env_string, @@ -229,26 +718,9 @@ gtk_init_check (int *argc, } #endif /* G_ENABLE_DEBUG */ - env_string = getenv ("GTK_MODULES"); + env_string = g_getenv ("GTK_MODULES"); if (env_string) - { - gchar **modules, **as; - -#ifndef __EMX__ - modules = g_strsplit (env_string, G_SEARCHPATH_SEPARATOR_S, -1); -#else - modules = g_strsplit (env_string, ";", -1); -#endif - for (as = modules; *as; as++) - { - if (**as) - gtk_modules = g_slist_prepend (gtk_modules, *as); - else - g_free (*as); - } - g_free (modules); - env_string = NULL; - } + gtk_modules_string = g_string_new (env_string); if (argc && argv) { @@ -272,7 +744,14 @@ gtk_init_check (int *argc, (*argv)[i] = NULL; if (module_name && *module_name) - gtk_modules = g_slist_prepend (gtk_modules, g_strdup (module_name)); + { + 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, module_name); + } } else if (strcmp ("--g-fatal-warnings", (*argv)[i]) == 0) { @@ -344,75 +823,30 @@ gtk_init_check (int *argc, *argc -= k; } } + + gtk_argv = g_malloc ((gtk_argc + 1) * sizeof (char*)); + for (i = 0; i < gtk_argc; i++) + gtk_argv[i] = g_strdup ((*argv)[i]); + gtk_argv[gtk_argc] = NULL; } - + + if (gtk_debug_flags & GTK_DEBUG_UPDATES) + gdk_window_set_debug_updates (TRUE); + /* load gtk modules */ - gtk_modules = g_slist_reverse (gtk_modules); - for (slist = gtk_modules; slist; slist = slist->next) + if (gtk_modules_string) { - gchar *module_name; - GModule *module = NULL; - GtkModuleInitFunc modinit_func = NULL; - - module_name = slist->data; - slist->data = NULL; -#ifndef __EMX__ - if (!g_path_is_absolute (module_name)) - { - gchar *old = module_name; - - module_name = g_module_build_path (NULL, module_name); - g_free (old); - } -#else - module_name = add_dll_suffix(module_name); -#endif - if (g_module_supported ()) - { - module = g_module_open (module_name, G_MODULE_BIND_LAZY); - if (module && - g_module_symbol (module, "gtk_module_init", (gpointer*) &modinit_func) && - modinit_func) - { - if (!g_slist_find (gtk_modules, modinit_func)) - { - g_module_make_resident (module); - slist->data = modinit_func; - } - else - { - g_module_close (module); - module = NULL; - } - } - } - if (!modinit_func) - { - g_message ("Failed to load module \"%s\": %s", - module ? g_module_name (module) : module_name, - g_module_error ()); - if (module) - g_module_close (module); - } - g_free (module_name); + gtk_modules = load_modules (gtk_modules_string->str); + g_string_free (gtk_modules_string, TRUE); } #ifdef ENABLE_NLS -# ifndef G_OS_WIN32 - bindtextdomain(GETTEXT_PACKAGE, GTK_LOCALEDIR); + bindtextdomain (GETTEXT_PACKAGE, GTK_LOCALEDIR); + bindtextdomain (GETTEXT_PACKAGE "-properties", GTK_LOCALEDIR); # ifdef HAVE_BIND_TEXTDOMAIN_CODESET - bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8"); + bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); + bind_textdomain_codeset (GETTEXT_PACKAGE "-properties", "UTF-8"); # endif -# else /* !G_OS_WIN32 */ - { - /* GTk+ locale dir is %WinDir%\gtk+\locale */ - bindtextdomain (GETTEXT_PACKAGE, - g_strconcat (gtk_win32_get_installation_directory (), - G_DIR_SEPARATOR_S, - "locale", - NULL)); - } -#endif #endif { @@ -429,66 +863,103 @@ gtk_init_check (int *argc, } } - /* Initialize the default visual and colormap to be - * used in creating widgets. (We want to use the system - * defaults so as to be nice to the colormap). - */ - gtk_visual = gdk_visual_get_system (); - gtk_colormap = gdk_colormap_get_system (); - - gtk_type_init (); - gtk_signal_init (); - gtk_rc_init (); - - - /* Register an exit function to make sure we are able to cleanup. - */ - g_atexit (gtk_exit_func); + gtk_type_init (0); + _gtk_accel_map_init (); + _gtk_rc_init (); /* Set the 'initialized' flag. */ gtk_initialized = TRUE; - /* initialize gtk modules + display_manager = gdk_display_manager_get (); + g_signal_connect (display_manager, "notify::default-display", + G_CALLBACK (default_display_notify_cb), NULL); + g_signal_connect (display_manager, "display-opened", + G_CALLBACK (display_opened_cb), NULL); + + /* initialize multhead aware gtk modules; for other modules, + * we wait until we have a display open; */ for (slist = gtk_modules; slist; slist = slist->next) { if (slist->data) { - GtkModuleInitFunc modinit; - - modinit = slist->data; - modinit (argc, argv); + GtkModuleInfo *info = slist->data; + + if (info->display_init_func) + info->init_func (argc, argv); } } - g_slist_free (gtk_modules); -#ifndef G_OS_WIN32 - /* No use warning on Win32, there aren't any non-devel versions anyhow... */ - g_message ("" "YOU ARE USING THE DEVEL BRANCH 1.3.x OF GTK+ WHICH IS CURRENTLY\n" - " UNDER HEAVY DEVELOPMENT AND FREQUENTLY INTRODUCES INSTABILITIES.\n" - " if you don't know why you are getting this, you probably want to\n" - " use the stable branch which can be retrived from\n" - " ftp://ftp.gtk.org/pub/gtk/v1.2/ or via CVS with\n" - " cvs checkout -r glib-1-2 glib; cvs checkout -r gtk-1-2 gtk+"); + return TRUE; +} + +#ifdef G_PLATFORM_WIN32 +#undef gtk_init_check #endif - return TRUE; +/** + * gtk_init_check: + * @argc: Address of the argc parameter of your + * main() function. Changed if any arguments were handled. + * @argv: Address of the argv parameter of main(). + * Any parameters understood by gtk_init() 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 GUI 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 GUI 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: Address of the argc parameter of your + * main() function. Changed if any arguments were handled. + * @argv: Address of the argv parameter of main(). + * Any parameters understood by gtk_init() 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. @argc and + * @argv are adjusted accordingly so your own code will + * never see those standard arguments. + * + * + * This function will terminate your program if it was unable to initialize + * the GUI for some reason. If you want your program to fall back to a + * textual interface you want to call gtk_init_check() instead. + * + **/ void gtk_init (int *argc, char ***argv) { if (!gtk_init_check (argc, argv)) { - g_warning ("cannot open display: %s", gdk_get_display ()); - exit(1); + const char *display_name_arg = gdk_get_display_arg_name (); + g_warning ("cannot open display: %s", display_name_arg ? display_name_arg : " "); + exit (1); } } -#ifdef G_OS_WIN32 +#ifdef G_PLATFORM_WIN32 static void check_sizeof_GtkWindow (size_t sizeof_GtkWindow) @@ -498,23 +969,48 @@ check_sizeof_GtkWindow (size_t sizeof_GtkWindow) "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 -fnative-struct switch."); + "your code with gcc without the -fnative-struct\n" + "(or -mms-bitfields) switch, or that you are using\n" + "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 -fnative-struct\n" + "(or -mms-bitfields) switch, or that you are using\n" + "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) +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) +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); } @@ -523,27 +1019,122 @@ gtk_init_check_abi_check (int *argc, char ***argv, int num_checks, size_t sizeof void gtk_exit (gint errorcode) { - /* Only if "gtk" has been initialized should we de-initialize. - */ - /* de-initialisation is done by the gtk_exit_funct(), - * no need to do this here (Alex J.) - */ - gdk_exit(errorcode); + exit (errorcode); } -gchar* + +/** + * gtk_set_locale: + * + * Initializes internationalization support for GTK+. gtk_init() + * automatically does this, so there is typically no point + * in calling this function. + * + * If you are calling this function because you changed the locale + * after GTK+ is was initialized, then calling this function + * may help a bit. (Note, however, that changing the locale + * after GTK+ is initialized may produce inconsistent results and + * is not really supported.) + * + * In detail - sets the current locale according to the + * program environment. This is the same as calling the C library function + * setlocale (LC_ALL, "") but also takes care of the + * locale specific setup of the windowing system used by GDK. + * + * Returns: a string corresponding to the locale set, typically in the + * form lang_COUNTRY, where lang is an ISO-639 language code, and + * COUNTRY is an ISO-3166 country code. On Unix, this form matches the + * result of the setlocale(); it is also used on other machines, such as + * Windows, where the C library returns a different result. The string is + * owned by GTK+ and should not be modified or freed. + **/ +gchar * gtk_set_locale (void) { return gdk_set_locale (); } -gchar* +/** + * _gtk_get_lc_ctype: + * + * Return the Unix-style locale string for the language currently in + * effect. On Unix systems, this is the return value from + * setlocale(LC_CTYPE, NULL), and the user can + * affect this through the environment variables LC_ALL, LC_CTYPE or + * LANG (checked in that order). The locale strings typically is in + * the form lang_COUNTRY, where lang is an ISO-639 language code, and + * COUNTRY is an ISO-3166 country code. For instance, sv_FI for + * Swedish as written in Finland or pt_BR for Portuguese as written in + * Brazil. + * + * On Windows, the C library doesn't use any such environment + * variables, and setting them won't affect the behaviour of functions + * like ctime(). The user sets the locale through the Regional Options + * in the Control Panel. The C library (in the setlocale() function) + * does not use country and language codes, but country and language + * names spelled out in English. + * However, this function does check the above environment + * variables, and does return a Unix-style locale string based on + * either said environment variables or the thread's current locale. + * + * Return value: a dynamically allocated string, free with g_free(). + */ + +gchar * +_gtk_get_lc_ctype (void) +{ +#ifdef G_OS_WIN32 + /* Somebody might try to set the locale for this process using the + * LANG or LC_ environment variables. The Microsoft C library + * doesn't know anything about them. You set the locale in the + * Control Panel. Setting these env vars won't have any affect on + * locale-dependent C library functions like ctime(). But just for + * kicks, do obey LC_ALL, LC_CTYPE and LANG in GTK. (This also makes + * it easier to test GTK and Pango in various default languages, you + * don't have to clickety-click in the Control Panel, you can simply + * start the program with LC_ALL=something on the command line.) + */ + gchar *p; + + p = getenv ("LC_ALL"); + if (p != NULL) + return g_strdup (p); + + p = getenv ("LC_CTYPE"); + if (p != NULL) + return g_strdup (p); + + p = getenv ("LANG"); + if (p != NULL) + return g_strdup (p); + + return g_win32_getlocale (); +#else + return g_strdup (setlocale (LC_CTYPE, NULL)); +#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. See + * _gtk_get_lc_ctype() for notes on behaviour on Windows. + * + * Return value: the default language as a #PangoLanguage, must not be + * freed + **/ +PangoLanguage * gtk_get_default_language (void) { gchar *lang; + PangoLanguage *result; gchar *p; - lang = g_strdup (setlocale (LC_CTYPE, NULL)); + lang = _gtk_get_lc_ctype (); p = strchr (lang, '.'); if (p) *p = '\0'; @@ -551,7 +1142,10 @@ gtk_get_default_language (void) if (p) *p = '\0'; - return lang; + result = pango_language_from_string (lang); + g_free (lang); + + return result; } void @@ -564,7 +1158,7 @@ gtk_main (void) gtk_main_loop_level++; - loop = g_main_new (TRUE); + loop = g_main_loop_new (NULL, TRUE); main_loops = g_slist_prepend (main_loops, loop); tmp_list = functions = init_functions; @@ -580,10 +1174,10 @@ gtk_main (void) } g_list_free (functions); - if (g_main_is_running (main_loops->data)) + if (g_main_loop_is_running (main_loops->data)) { GDK_THREADS_LEAVE (); - g_main_run (loop); + g_main_loop_run (loop); GDK_THREADS_ENTER (); gdk_flush (); } @@ -627,7 +1221,7 @@ gtk_main (void) main_loops = g_slist_remove (main_loops, loop); - g_main_destroy (loop); + g_main_loop_unref (loop); gtk_main_loop_level--; } @@ -643,53 +1237,175 @@ gtk_main_quit (void) { g_return_if_fail (main_loops != NULL); - g_main_quit (main_loops->data); + g_main_loop_quit (main_loops->data); } -gint +gboolean gtk_events_pending (void) { gboolean result; GDK_THREADS_LEAVE (); - result = g_main_pending(); + result = g_main_context_pending (NULL); GDK_THREADS_ENTER (); return result; } -gint +gboolean gtk_main_iteration (void) { GDK_THREADS_LEAVE (); - g_main_iteration (TRUE); + g_main_context_iteration (NULL, TRUE); GDK_THREADS_ENTER (); if (main_loops) - return !g_main_is_running (main_loops->data); + return !g_main_loop_is_running (main_loops->data); else return TRUE; } -gint +gboolean gtk_main_iteration_do (gboolean blocking) { GDK_THREADS_LEAVE (); - g_main_iteration (blocking); + g_main_context_iteration (NULL, blocking); GDK_THREADS_ENTER (); if (main_loops) - return !g_main_is_running (main_loops->data); + return !g_main_loop_is_running (main_loops->data); else return TRUE; } +/* private libgtk to libgdk interfaces + */ +gboolean gdk_pointer_grab_info_libgtk_only (GdkDisplay *display, + GdkWindow **grab_window, + gboolean *owner_events); +gboolean gdk_keyboard_grab_info_libgtk_only (GdkDisplay *display, + 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_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; + gboolean owner_events; + GdkDisplay *display; + + 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: + display = gdk_drawable_get_display (event->proximity.window); + if (!gdk_pointer_grab_info_libgtk_only (display, &grab_window, &owner_events) || + !owner_events) + return NULL; + break; + + case GDK_KEY_PRESS: + case GDK_KEY_RELEASE: + display = gdk_drawable_get_display (event->key.window); + if (!gdk_keyboard_grab_info_libgtk_only (display, &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, (void**) &grab_widget); + + 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; +} + void gtk_main_do_event (GdkEvent *event) { GtkWidget *event_widget; GtkWidget *grab_widget; + GtkWindowGroup *window_group; GdkEvent *next_event; + GdkEvent *rewritten_event = NULL; GList *tmp_list; /* If there are any events pending then get the next one. @@ -724,6 +1440,18 @@ gtk_main_do_event (GdkEvent *event) if (next_event) gdk_event_free (next_event); + 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 @@ -741,22 +1469,34 @@ gtk_main_do_event (GdkEvent *event) * them specially */ if (event->type == GDK_PROPERTY_NOTIFY) - gtk_selection_incr_event (event->any.window, - &event->property); - + _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); + /* Push the event onto a stack of current events for * gtk_current_event_get(). */ current_events = g_list_prepend (current_events, event); - + /* If there is a grab in effect... */ - if (grabs) + if (window_group->grabs) { - grab_widget = grabs->data; + grab_widget = window_group->grabs->data; /* If the grab widget is an ancestor of the event widget * then we send the event to the original event widget. @@ -787,11 +1527,11 @@ gtk_main_do_event (GdkEvent *event) break; case GDK_DELETE: - gtk_widget_ref (event_widget); - if ((!grabs || gtk_widget_get_toplevel (grabs->data) == event_widget) && + g_object_ref (event_widget); + if ((!window_group->grabs || gtk_widget_get_toplevel (window_group->grabs->data) == event_widget) && !gtk_widget_event (event_widget, event)) gtk_widget_destroy (event_widget); - gtk_widget_unref (event_widget); + g_object_unref (event_widget); break; case GDK_DESTROY: @@ -800,22 +1540,23 @@ gtk_main_do_event (GdkEvent *event) */ if (!event_widget->parent) { - gtk_widget_ref (event_widget); + g_object_ref (event_widget); if (!gtk_widget_event (event_widget, event) && - !GTK_OBJECT_DESTROYED (event_widget)) + GTK_WIDGET_REALIZED (event_widget)) gtk_widget_destroy (event_widget); - gtk_widget_unref (event_widget); + g_object_unref (event_widget); } break; case GDK_EXPOSE: if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget)) - gdk_window_begin_paint_rect (event->any.window, &event->expose.area); - - gtk_widget_event (event_widget, event); - - if (event->any.window && GTK_WIDGET_DOUBLE_BUFFERED (event_widget)) - gdk_window_end_paint (event->any.window); + { + 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 + gtk_widget_send_expose (event_widget, event); break; case GDK_PROPERTY_NOTIFY: @@ -829,6 +1570,7 @@ gtk_main_do_event (GdkEvent *event) case GDK_SELECTION_NOTIFY: case GDK_CLIENT_EVENT: case GDK_VISIBILITY_NOTIFY: + case GDK_WINDOW_STATE: gtk_widget_event (event_widget, event); break; @@ -857,9 +1599,13 @@ gtk_main_do_event (GdkEvent *event) case GDK_ENTER_NOTIFY: if (GTK_WIDGET_IS_SENSITIVE (grab_widget)) { + g_object_ref (event_widget); + gtk_widget_event (grab_widget, event); if (event_widget == grab_widget) GTK_PRIVATE_SET_FLAG (event_widget, GTK_LEAVE_PENDING); + + g_object_unref (event_widget); } break; @@ -875,66 +1621,182 @@ gtk_main_do_event (GdkEvent *event) case GDK_DRAG_STATUS: case GDK_DROP_FINISHED: - gtk_drag_source_handle_event (event_widget, event); + _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); + _gtk_drag_dest_handle_event (event_widget, event); + break; + default: + g_assert_not_reached (); break; } 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); } -gint +gboolean gtk_true (void) { return TRUE; } -gint +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 (toplevel && 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; +} GrabNotifyInfo; + +static gboolean +check_is_grabbed (GtkWidget *widget, + GtkWidget *grab_widget) +{ + if (grab_widget) + return !(widget == grab_widget || gtk_widget_is_ancestor (widget, grab_widget)); + else + return FALSE; +} + +static void +gtk_grab_notify_foreach (GtkWidget *child, + gpointer data) + +{ + GrabNotifyInfo *info = data; + gboolean was_grabbed = check_is_grabbed (child, info->old_grab_widget); + gboolean is_grabbed = check_is_grabbed (child, info->new_grab_widget); + + if (was_grabbed != is_grabbed) + { + g_object_ref (child); + + _gtk_widget_grab_notify (child, was_grabbed); + + if (GTK_IS_CONTAINER (child)) + gtk_container_foreach (GTK_CONTAINER (child), gtk_grab_notify_foreach, info); + + g_object_unref (child); + } +} + +static void +gtk_grab_notify (GtkWindowGroup *group, + GtkWidget *grab_widget, + gboolean was_grabbed) +{ + GList *toplevels; + GrabNotifyInfo info; + + if (was_grabbed) + { + info.old_grab_widget = grab_widget; + info.new_grab_widget = group->grabs ? group->grabs->data : NULL; + } + else + { + info.old_grab_widget = (group->grabs && group->grabs->next) ? group->grabs->next->data : NULL; + info.new_grab_widget = grab_widget; + } + + g_object_ref (group); + g_object_ref (grab_widget); + + 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); + + if (group == _gtk_window_get_group (toplevel)) + gtk_container_foreach (GTK_CONTAINER (toplevel), gtk_grab_notify_foreach, &info); + g_object_unref (toplevel); + } + + g_object_unref (group); + g_object_unref (grab_widget); +} + void gtk_grab_add (GtkWidget *widget) { + GtkWindowGroup *group; + gboolean was_grabbed; + g_return_if_fail (widget != NULL); - if (!GTK_WIDGET_HAS_GRAB (widget)) + if (!GTK_WIDGET_HAS_GRAB (widget) && GTK_WIDGET_IS_SENSITIVE (widget)) { GTK_WIDGET_SET_FLAGS (widget, GTK_HAS_GRAB); - grabs = g_slist_prepend (grabs, widget); - gtk_widget_ref (widget); + group = gtk_main_get_window_group (widget); + + was_grabbed = (group->grabs != NULL); + + g_object_ref (widget); + group->grabs = g_slist_prepend (group->grabs, widget); + + gtk_grab_notify (group, widget, FALSE); } } GtkWidget* gtk_grab_get_current (void) { - if (grabs) - return GTK_WIDGET (grabs->data); + GtkWindowGroup *group; + + group = gtk_main_get_window_group (NULL); + + if (group->grabs) + return GTK_WIDGET (group->grabs->data); return NULL; } void gtk_grab_remove (GtkWidget *widget) { + GtkWindowGroup *group; + g_return_if_fail (widget != NULL); if (GTK_WIDGET_HAS_GRAB (widget)) { GTK_WIDGET_UNSET_FLAGS (widget, GTK_HAS_GRAB); + + group = gtk_main_get_window_group (widget); + group->grabs = g_slist_remove (group->grabs, widget); - grabs = g_slist_remove (grabs, widget); - gtk_widget_unref (widget); + g_object_unref (widget); + + gtk_grab_notify (group, widget, TRUE); } } @@ -970,7 +1832,7 @@ gtk_key_snooper_install (GtkKeySnoopFunc snooper, } void -gtk_key_snooper_remove (guint snooper_id) +gtk_key_snooper_remove (guint snooper_id) { GtkKeySnooperData *data = NULL; GSList *slist; @@ -986,7 +1848,10 @@ gtk_key_snooper_remove (guint snooper_id) data = NULL; } if (data) - key_snoopers = g_slist_remove (key_snoopers, data); + { + key_snoopers = g_slist_remove (key_snoopers, data); + g_free (data); + } } static gint @@ -1064,15 +1929,14 @@ gtk_quit_add_destroy (guint main_level, GtkObject **object_p; g_return_if_fail (main_level > 0); - g_return_if_fail (object != NULL); g_return_if_fail (GTK_IS_OBJECT (object)); object_p = g_new (GtkObject*, 1); *object_p = object; - gtk_signal_connect (object, - "destroy", - GTK_SIGNAL_FUNC (gtk_widget_destroyed), - object_p); + g_signal_connect (object, + "destroy", + G_CALLBACK (gtk_widget_destroyed), + object_p); gtk_quit_add (main_level, (GtkFunction) gtk_quit_destructor, object_p); } @@ -1200,13 +2064,13 @@ guint gtk_idle_add (GtkFunction function, gpointer data) { - return g_idle_add_full (GTK_PRIORITY_DEFAULT, function, data, NULL); + return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, function, data, NULL); } guint -gtk_idle_add_priority (gint priority, - GtkFunction function, - gpointer data) +gtk_idle_add_priority (gint priority, + GtkFunction function, + gpointer data) { return g_idle_add_full (priority, function, data, NULL); } @@ -1275,7 +2139,7 @@ gtk_invoke_idle_timeout (gpointer data) GtkArg args[1]; gint ret_val = FALSE; args[0].name = NULL; - args[0].type = GTK_TYPE_BOOL; + args[0].type = G_TYPE_BOOLEAN; args[0].d.pointer_data = &ret_val; closure->marshal (NULL, closure->data, 0, args); return ret_val; @@ -1289,18 +2153,29 @@ gtk_invoke_input (gpointer data, GtkClosure *closure = data; GtkArg args[3]; - args[0].type = GTK_TYPE_INT; + args[0].type = G_TYPE_INT; args[0].name = NULL; - GTK_VALUE_INT(args[0]) = source; - args[1].type = GTK_TYPE_GDK_INPUT_CONDITION; + GTK_VALUE_INT (args[0]) = source; + args[1].type = GDK_TYPE_INPUT_CONDITION; args[1].name = NULL; - GTK_VALUE_FLAGS(args[1]) = condition; - args[2].type = GTK_TYPE_NONE; + GTK_VALUE_FLAGS (args[1]) = condition; + args[2].type = G_TYPE_NONE; args[2].name = NULL; closure->marshal (NULL, closure->data, 2, args); } +/** + * gtk_get_current_event: + * + * Obtains a copy of the event currently being processed by GTK+. For + * example, if you get a "clicked" signal from #GtkButton, the current + * event will be the #GdkEventButton that triggered the "clicked" + * signal. The returned event must be freed with gdk_event_free(). + * If there is no current event, the function returns %NULL. + * + * Return value: a copy of the current event, or %NULL if no current event. + **/ GdkEvent* gtk_get_current_event (void) { @@ -1327,6 +2202,16 @@ gtk_get_current_event_time (void) return GDK_CURRENT_TIME; } +/** + * gtk_get_current_event_state: + * @state: 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) { @@ -1341,6 +2226,16 @@ gtk_get_current_event_state (GdkModifierType *state) } } +/** + * 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: the widget that originally received @event, or %NULL + **/ GtkWidget* gtk_get_event_widget (GdkEvent *event) { @@ -1353,16 +2248,6 @@ gtk_get_event_widget (GdkEvent *event) return widget; } -static void -gtk_exit_func (void) -{ - if (gtk_initialized) - { - gtk_initialized = FALSE; - } -} - - static gint gtk_quit_invoke_function (GtkQuitFunction *quitf) { @@ -1374,7 +2259,7 @@ gtk_quit_invoke_function (GtkQuitFunction *quitf) gint ret_val = FALSE; args[0].name = NULL; - args[0].type = GTK_TYPE_BOOL; + args[0].type = G_TYPE_BOOLEAN; args[0].d.pointer_data = &ret_val; ((GtkCallbackMarshal) quitf->marshal) (NULL, quitf->data, @@ -1383,18 +2268,42 @@ gtk_quit_invoke_function (GtkQuitFunction *quitf) } } +/** + * 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 "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. Consider asking on + * the mailing list for 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) { gint handled_event; - g_return_if_fail (widget != NULL); g_return_if_fail (GTK_IS_WIDGET (widget)); g_return_if_fail (event != NULL); handled_event = FALSE; + g_object_ref (widget); + if ((event->type == GDK_KEY_PRESS) || (event->type == GDK_KEY_RELEASE)) { @@ -1405,30 +2314,62 @@ gtk_propagate_event (GtkWidget *widget, */ GtkWidget *window; - window = gtk_widget_get_ancestor (widget, GTK_TYPE_WINDOW); - if (window) - { - if (GTK_WIDGET_IS_SENSITIVE (window)) - gtk_widget_event (window, event); - - handled_event = TRUE; /* don't send to widget */ - } + window = gtk_widget_get_toplevel (widget); + if (window && GTK_IS_WINDOW (window)) + { + /* 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 = gtk_widget_event (widget, event); + + if (!handled_event) + { + window = gtk_widget_get_toplevel (widget); + if (window && GTK_IS_WINDOW (window)) + { + if (GTK_WIDGET_IS_SENSITIVE (window)) + gtk_widget_event (window, event); + } + } + + handled_event = TRUE; /* don't send to widget */ + } } /* Other events get propagated up the widget tree * so that parents can see the button and motion * events of the children. */ - while (!handled_event && widget) + if (!handled_event) { - GtkWidget *tmp; + while (TRUE) + { + GtkWidget *tmp; + + /* 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 = widget->parent; + g_object_unref (widget); - gtk_widget_ref (widget); - handled_event = !GTK_WIDGET_IS_SENSITIVE (widget) || gtk_widget_event (widget, event); - tmp = widget->parent; - gtk_widget_unref (widget); - widget = tmp; + widget = tmp; + + if (!handled_event && widget) + g_object_ref (widget); + else + break; + } } + else + g_object_unref (widget); } #if 0 @@ -1543,3 +2484,19 @@ gtk_print (gchar *str) gtk_widget_show (window); } #endif + +gboolean +_gtk_boolean_handled_accumulator (GSignalInvocationHint *ihint, + GValue *return_accu, + const GValue *handler_return, + gpointer dummy) +{ + gboolean continue_emission; + gboolean signal_handled; + + signal_handled = g_value_get_boolean (handler_return); + g_value_set_boolean (return_accu, signal_handled); + continue_emission = !signal_handled; + + return continue_emission; +}