/* Blame Elliot for the poptimization of this file */ #ifdef HAVE_CONFIG_H # include #endif /* AIX requires this to be the first thing in the file. */ #ifndef __GNUC__ # if HAVE_ALLOCA_H # include # else # ifdef _AIX #pragma alloca # else # ifndef alloca /* predefined by HP cc +Olibcalls */ char *alloca (); # endif # endif # endif #endif #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_ESD #include #endif #include "libgnome/libgnomeP.h" #include "libgnome/gnome-popt.h" #include "gnome-preferences.h" #include "libgnomeui/gnome-client.h" #include "libgnomeui/gnome-init.h" #include "libgnomeui/gnome-winhints.h" static void initialize_gtk_signal_relay(void); static gboolean relay_gtk_signal(GtkObject *object, guint signal_id, guint n_params, GtkArg *params, gchar *signame); extern char *program_invocation_name; extern char *program_invocation_short_name; extern void gnome_type_init (void); static void gnome_rc_parse(gchar *command); #ifdef USE_SEGV_HANDLE static void gnome_segv_handle(int signum); #endif static GdkPixmap *imlib_image_loader(GdkWindow *window, GdkColormap *colormap, GdkBitmap **mask, GdkColor *transparent_color, const gchar *filename); /* The master client. */ static GnomeClient *client = NULL; static gboolean gnome_initialized = FALSE; static void gnome_add_gtk_arg_callback(poptContext con, enum poptCallbackReason reason, const struct poptOption * opt, const char * arg, void * data) { static int gnome_gtk_initialized = FALSE; static GPtrArray *gtk_args = NULL; char *newstr; int final_argc; char **final_argv; if(gnome_gtk_initialized) { /* * gnome has already been initialized, so app might be making a * second pass over the args - just ignore */ return; } switch(reason) { gchar *file_name; case POPT_CALLBACK_REASON_PRE: gtk_args = g_ptr_array_new(); /* Note that the value of argv[0] passed to main() may be * different from the value that this passes to gtk */ g_ptr_array_add(gtk_args, g_strdup(poptGetInvocationName(con))); break; case POPT_CALLBACK_REASON_OPTION: newstr = g_strconcat("--", opt->longName, NULL); g_ptr_array_add(gtk_args, newstr); gnome_client_add_static_arg(client, newstr, NULL); if(opt->argInfo == POPT_ARG_STRING) { g_ptr_array_add(gtk_args, (char *)arg); gnome_client_add_static_arg(client, arg, NULL); } break; case POPT_CALLBACK_REASON_POST: g_ptr_array_add(gtk_args, NULL); final_argc = gtk_args->len - 1; final_argv = g_memdup(gtk_args->pdata, sizeof(char *) * gtk_args->len); gtk_init(&final_argc, &final_argv); g_free(final_argv); #ifdef GNOME_CLIENT_WILL_COPY_ARGS g_strfreev(gtk_args->pdata); /* this weirdness is here to eliminate memory leaks if gtk_init() modifies argv[] */ g_ptr_array_free(gtk_args, FALSE); #else g_ptr_array_free(gtk_args, TRUE); #endif gtk_args = NULL; gnome_gtk_initialized = TRUE; file_name = g_concat_dir_and_file (gnome_user_accels_dir, gnome_app_id); #if GTK_CHECK_VERSION(1,2,1) gtk_item_factory_parse_rc (file_name); #else g_warning ("Accelerator support disabled. Use at least GTK+ 1.2.1 if you want it."); #endif g_free (file_name); break; } } static const struct poptOption gtk_options [] = { { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL}, { NULL, '\0', POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, &gnome_add_gtk_arg_callback, 0, NULL}, { "gdk-debug", '\0', POPT_ARG_STRING, NULL, 0, N_("Gdk debugging flags to set"), N_("FLAGS")}, { "gdk-no-debug", '\0', POPT_ARG_STRING, NULL, 0, N_("Gdk debugging flags to unset"), N_("FLAGS")}, { "display", '\0', POPT_ARG_STRING, NULL, 0, N_("X display to use"), N_("DISPLAY")}, { "sync", '\0', POPT_ARG_NONE, NULL, 0, N_("Make X calls synchronous"), NULL}, { "no-xshm", '\0', POPT_ARG_NONE, NULL, 0, N_("Don't use X shared memory extension"), NULL}, { "name", '\0', POPT_ARG_STRING, NULL, 0, N_("Program name as used by the window manager"), N_("NAME")}, { "class", '\0', POPT_ARG_STRING, NULL, 0, N_("Program class as used by the window manager"), N_("CLASS")}, { "gxid_host", '\0', POPT_ARG_STRING, NULL, 0, NULL, N_("HOST")}, { "gxid_port", '\0', POPT_ARG_STRING, NULL, 0, NULL, N_("PORT")}, { "xim-preedit", '\0', POPT_ARG_STRING, NULL, 0, NULL, N_("STYLE")}, { "xim-status", '\0', POPT_ARG_STRING, NULL, 0, NULL, N_("STYLE")}, { "gtk-debug", '\0', POPT_ARG_STRING, NULL, 0, N_("Gtk+ debugging flags to set"), N_("FLAGS")}, { "gtk-no-debug", '\0', POPT_ARG_STRING, NULL, 0, N_("Gtk+ debugging flags to unset"), N_("FLAGS")}, { "g-fatal-warnings", '\0', POPT_ARG_NONE, NULL, 0, N_("Make all warnings fatal"), NULL}, { "gtk-module", '\0', POPT_ARG_STRING, NULL, 0, N_("Load an additional Gtk module"), N_("MODULE")}, { NULL, '\0', 0, NULL, 0} }; static void gnome_init_cb(poptContext ctx, enum poptCallbackReason reason, const struct poptOption *opt) { #ifdef USE_SEGV_HANDLE struct sigaction sa; #endif if(gnome_initialized) return; switch(reason) { case POPT_CALLBACK_REASON_PRE: #ifdef USE_SEGV_HANDLE /* * Yes, we do this twice, so if an error occurs before init, * it will be caught, and if it happens after init, we'll override * gtk's handler */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = (gpointer)gnome_segv_handle; sigaction(SIGSEGV, &sa, NULL); #endif gtk_set_locale(); client = gnome_master_client(); break; case POPT_CALLBACK_REASON_POST: gdk_imlib_init(); gnome_type_init(); gtk_rc_set_image_loader(imlib_image_loader); gnome_rc_parse(program_invocation_name); gnome_preferences_load(); /* * No, this is not the most evil thing you have * seen raster. Imlib is the most evil thing * we have seen. And until the cache in imlib * is smarter, this stays. * * Last I checked, there was no imlib-capplet */ if (gnome_preferences_get_disable_imlib_cache ()){ int pixmaps, images; /* * If cache info has been set to -1, -1, it * means something initialized before us and * is requesting the cache to not be touched */ gdk_imlib_get_cache_info (&pixmaps, &images); if (!(pixmaps == -1 || images == -1)) gdk_imlib_set_cache_info (0, images); } #ifdef USE_SEGV_HANDLE memset(&sa, 0, sizeof(sa)); sa.sa_handler = (gpointer)gnome_segv_handle; sigaction(SIGSEGV, &sa, NULL); #endif /* lame trigger stuff. This really needs to be sorted out better so that the trigger API is exported more - perhaps there's something hidden in glib that already does this :) */ initialize_gtk_signal_relay(); gnome_initialized = TRUE; break; case POPT_CALLBACK_REASON_OPTION: if(opt->val == -1) { g_print ("Gnome %s %s\n", gnome_app_id, gnome_app_version); exit(0); _exit(1); } break; } } static const struct poptOption gnome_options[] = { { NULL, '\0', POPT_ARG_INTL_DOMAIN, PACKAGE, 0, NULL, NULL}, {NULL, '\0', POPT_ARG_CALLBACK|POPT_CBFLAG_PRE|POPT_CBFLAG_POST, &gnome_init_cb, 0, NULL, NULL}, {"version", 'V', POPT_ARG_NONE, NULL, -1}, POPT_AUTOHELP {NULL, '\0', 0, NULL, 0} }; static void gnome_register_options(void) { gnomelib_register_popt_table(gtk_options, "GTK options"); gnomelib_register_popt_table(gnome_options, "GNOME GUI options"); } /* #define ALLOC_DEBUGGING_HOOKS */ #ifdef ALLOC_DEBUGGING_HOOKS #include #include static struct mallinfo mi1, mi2; #define AM() mi1 = mallinfo(); #define PM(x) mi2 = mallinfo(); printf(x ": used %d, now %d\n", \ mi2.uordblks - mi1.uordblks, mi2.uordblks); typedef struct { gulong magic; gulong bsize; struct timeval tv; } BlkInfo; static void (*old_free_hook)(__malloc_ptr_t __ptr); static __malloc_ptr_t (*old_malloc_hook) (size_t __size); static __malloc_ptr_t (*old_realloc_hook) (__malloc_ptr_t __ptr, size_t __size); static void my_free_hook(__malloc_ptr_t ptr) { char buf[512]; BlkInfo *tv; struct timeval now; tv = (BlkInfo *)(((char *)ptr) - sizeof(BlkInfo)); __free_hook = old_free_hook; if(tv->magic != 0xadaedead) { goto out; } gettimeofday(&now, NULL); sprintf(buf, "-%p %ld %ld\n", ptr, tv->bsize, (now.tv_sec*1000000 + now.tv_usec) - (tv->tv.tv_sec*1000000 + tv->tv.tv_usec)); write(1, buf, strlen(buf)); free(tv); out: __free_hook = my_free_hook; } static __malloc_ptr_t my_malloc_hook(size_t bsize) { BlkInfo *newblk; __malloc_ptr_t retval; char buf[512]; if(!bsize) return NULL; __malloc_hook = old_malloc_hook; newblk = malloc(bsize+sizeof(BlkInfo)); if(newblk) { newblk->magic = 0xadaedead; newblk->bsize = bsize; gettimeofday(&newblk->tv, NULL); } retval = newblk; retval += sizeof(BlkInfo); sprintf(buf, "+%p %ld\n", retval, bsize); write(1, buf, strlen(buf)); __malloc_hook = my_malloc_hook; return retval; } static __malloc_ptr_t my_realloc_hook(__malloc_ptr_t __ptr, size_t __size) { BlkInfo *oldblk, *newblk; __malloc_ptr_t rv; __realloc_hook = old_realloc_hook; oldblk = (((char *)__ptr) - sizeof(BlkInfo)); if(__ptr && oldblk->magic == 0xadaedead) { newblk = realloc(oldblk, __size + sizeof(BlkInfo)); rv = newblk + 1; newblk->bsize = __size; } else rv = realloc(__ptr, __size); __realloc_hook = my_realloc_hook; return rv; } #endif /** * gnome_init_with_popt_table: * @app_id: Application id. * @app_version: Application version. * @argc: pointer to argc (for example, as received by main) * @argv: pointer to argc (for example, as received by main) * @options: poptOption table with options to parse * @flags: popt flags. * @return_ctx: if non-NULL, the popt context is returned here. * * Initializes the application. This sets up all of the GNOME * internals and prepares them (imlib, gdk, session-management, triggers, * sound, user preferences) * * Unlike gnome_init, with gnome_init_with_popt_table you can provide * a table of popt options (popt is the command line argument parsing * library). */ int gnome_init_with_popt_table(const char *app_id, const char *app_version, int argc, char **argv, const struct poptOption *options, int flags, poptContext *return_ctx) { poptContext ctx; char *appdesc; #ifdef ALLOC_DEBUGGING_HOOKS appdesc = malloc(10); appdesc = realloc(appdesc, 20); free(appdesc); old_free_hook = __free_hook; old_malloc_hook = __malloc_hook; old_realloc_hook = __realloc_hook; __free_hook = my_free_hook; __malloc_hook = my_malloc_hook; __realloc_hook = my_realloc_hook; #endif g_return_val_if_fail(gnome_initialized == FALSE, -1); gnomelib_init (app_id, app_version); gnome_register_options(); if (getenv ("GTK_DEBUG_OBJECTS")) gtk_debug_flags |= GTK_DEBUG_OBJECTS; gnome_client_init(); if(options) { appdesc = alloca(sizeof(" options") + strlen(app_id)); strcpy(appdesc, app_id); strcat(appdesc, " options"); gnomelib_register_popt_table(options, appdesc); } ctx = gnomelib_parse_args(argc, argv, flags); if(return_ctx) *return_ctx = ctx; else poptFreeContext(ctx); /* reduce mem usage (hopefully) */ gnome_config_sync(); g_blow_chunks(); gnome_win_hints_init(); return 0; } /** * gnome_init: * @app_id: Application id. * @app_version: Application version. * @argc: argc (for example, as received by main) * @argv: argv (for example, as received by main) * * Initializes the application. This sets up all of the GNOME * internals and prepares them (imlib, gdk, session-management, triggers, * sound, user preferences) */ int gnome_init(const char *app_id, const char *app_version, int argc, char **argv) { g_return_val_if_fail(gnome_initialized == FALSE, -1); gnome_init_with_popt_table(app_id, app_version, argc, argv, NULL, 0, NULL); return 0; } /* perhaps this belongs in libgnome.. move it if you like. */ /* automagically parse all the gtkrc files for us. * * Parse: * $gnomedatadir/gtkrc * $gnomedatadir/$apprc * ~/.gnome/gtkrc * ~/.gnome/$apprc * * appname is derived from argv[0]. IMHO this is a great solution. * It provides good consistancy (you always know the rc file will be * the same name as the executable), and it's easy for the programmer. * * If you don't like it.. give me a good reason. Symlin */ static void gnome_rc_parse (gchar *command) { gint i; gint buf_len; gint found = 0; gchar *buf = NULL; gchar *file; gchar *apprc; buf_len = strlen(command); for (i = 0; i < buf_len; i++) { if (command[buf_len - i] == '/') { buf = g_strdup (&command[buf_len - i + 1]); found = TRUE; break; } } if (!found) buf = g_strdup (command); apprc = g_malloc (strlen(buf) + 3); g_snprintf(apprc, strlen(buf) + 3, "%src", buf); g_free(buf); /* /gtkrc */ file = gnome_unconditional_datadir_file("gtkrc"); if (file){ gtk_rc_add_default_file (file); g_free (file); } /* / */ file = gnome_unconditional_datadir_file(apprc); if (file){ gtk_rc_add_default_file (file); g_free (file); } /* ~/.gnome/gtkrc */ file = gnome_util_home_file("gtkrc"); if (file){ gtk_rc_add_default_file (file); g_free (file); } /* ~/.gnome/ */ file = gnome_util_home_file(apprc); if (file){ gtk_rc_add_default_file (file); g_free (file); } g_free (apprc); gtk_rc_init (); } #ifdef USE_SEGV_HANDLE static void gnome_segv_handle(int signum) { static int in_segv = 0; pid_t pid; if(in_segv) return; in_segv++; pid = fork(); if (pid){ /* Wait for user to see the dialog, then exit. */ int estatus; pid_t eret; eret = waitpid(pid, &estatus, 0); exit(1); } else { char buf[32]; g_snprintf(buf, sizeof(buf), "%d", signum); /* Child process */ execl(GNOMEBINDIR "/gnome_segv", GNOMEBINDIR "/gnome_segv", program_invocation_name, buf, NULL); } in_segv--; } #endif /* USE_SEGV_HANDLE */ static GdkPixmap * imlib_image_loader(GdkWindow *window, GdkColormap *colormap, GdkBitmap **mask, GdkColor *transparent_color, const gchar *filename) { GdkPixmap *retval; if (gdk_imlib_load_file_to_pixmap ((char *) filename, &retval, mask)) return retval; else return NULL; } static gboolean relay_gtk_signal(GtkObject *object, guint signal_id, guint n_params, GtkArg *params, gchar *signame) { #ifdef HAVE_ESD char *pieces[3] = {"gtk-events", NULL, NULL}; static GQuark disable_sound_quark = 0; pieces[1] = signame; if(!disable_sound_quark) disable_sound_quark = g_quark_from_static_string("gnome_disable_sound_events"); if(gtk_object_get_data_by_id(object, disable_sound_quark)) return TRUE; if(GTK_IS_WIDGET(object)) { if(!GTK_WIDGET_DRAWABLE(object)) return TRUE; if(GTK_IS_MENU_ITEM(object) && GTK_MENU_ITEM(object)->submenu) return TRUE; } gnome_triggers_vdo("", NULL, (const char **)pieces); return TRUE; #else return FALSE; /* this shouldn't even happen... */ #endif } static void initialize_gtk_signal_relay (void) { #ifdef HAVE_ESD gpointer iter_signames; char *signame; char *ctmp, *ctmp2; if (gnome_sound_connection < 0) return; if (!gnome_config_get_bool ("/sound/system/settings/event_sounds=true")) return; ctmp = gnome_config_file ("/sound/events/gtk-events.soundlist"); ctmp2 = g_strconcat ("=", ctmp, "=", NULL); g_free (ctmp); iter_signames = gnome_config_init_iterator_sections (ctmp2); gnome_config_push_prefix (ctmp2); g_free (ctmp2); while ((iter_signames = gnome_config_iterator_next (iter_signames, &signame, NULL))) { int signums [5]; int nsigs, i; /* * XXX this is an incredible hack based on a compile-time * knowledge of what gtk widgets do what, rather than * anything based on the info available at runtime. */ if(!strcmp (signame, "activate")){ gtk_type_class (gtk_menu_item_get_type ()); signums [0] = gtk_signal_lookup (signame, gtk_menu_item_get_type ()); gtk_type_class (gtk_editable_get_type ()); signums [1] = gtk_signal_lookup (signame, gtk_editable_get_type ()); nsigs = 2; } else if(!strcmp(signame, "toggled")){ gtk_type_class (gtk_toggle_button_get_type ()); signums [0] = gtk_signal_lookup (signame, gtk_toggle_button_get_type ()); gtk_type_class (gtk_check_menu_item_get_type ()); signums [1] = gtk_signal_lookup (signame, gtk_check_menu_item_get_type ()); nsigs = 2; } else if (!strcmp (signame, "clicked")){ gtk_type_class (gtk_button_get_type ()); signums [0] = gtk_signal_lookup (signame, gtk_button_get_type ()); nsigs = 1; } else { gtk_type_class (gtk_widget_get_type ()); signums [0] = gtk_signal_lookup (signame, gtk_widget_get_type ()); nsigs = 1; } for(i = 0; i < nsigs; i++) if (signums [i] > 0) gtk_signal_add_emission_hook (signums [i], (GtkEmissionHook)relay_gtk_signal, signame); } gnome_config_pop_prefix (); #endif }