summaryrefslogtreecommitdiff
path: root/src/caja-main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/caja-main.c')
-rw-r--r--src/caja-main.c590
1 files changed, 590 insertions, 0 deletions
diff --git a/src/caja-main.c b/src/caja-main.c
new file mode 100644
index 00000000..0a414ef5
--- /dev/null
+++ b/src/caja-main.c
@@ -0,0 +1,590 @@
+/*
+ * Caja
+ *
+ * Copyright (C) 1999, 2000 Red Hat, Inc.
+ * Copyright (C) 1999, 2000 Eazel, Inc.
+ *
+ * Caja is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * Caja 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
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Authors: Elliot Lee <[email protected]>,
+ * Darin Adler <[email protected]>,
+ * John Sullivan <[email protected]>
+ *
+ */
+
+/* caja-main.c: Implementation of the routines that drive program lifecycle and main window creation/destruction. */
+
+#include <config.h>
+#include "caja-main.h"
+
+#include "caja-application.h"
+#include "caja-self-check-functions.h"
+#include "caja-window.h"
+#include <dlfcn.h>
+#include <signal.h>
+#include <eel/eel-debug.h>
+#include <eel/eel-glib-extensions.h>
+#include <eel/eel-self-checks.h>
+#include <libegg/eggsmclient.h>
+#include <libegg/eggdesktopfile.h>
+#include <gdk/gdkx.h>
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include <gio/gdesktopappinfo.h>
+#include <libcaja-private/caja-debug-log.h>
+#include <libcaja-private/caja-global-preferences.h>
+#include <libcaja-private/caja-lib-self-check-functions.h>
+#include <libcaja-private/caja-icon-names.h>
+#include <libxml/parser.h>
+#ifdef HAVE_LOCALE_H
+ #include <locale.h>
+#endif
+#ifdef HAVE_MALLOC_H
+ #include <malloc.h>
+#endif
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef HAVE_EXEMPI
+ #include <exempi/xmp.h>
+#endif
+
+/* Keeps track of everyone who wants the main event loop kept active */
+static GSList* event_loop_registrants;
+
+static gboolean exit_with_last_window = TRUE;
+
+static gboolean is_event_loop_needed(void)
+{
+ return event_loop_registrants != NULL || !exit_with_last_window;
+}
+
+static int quit_if_in_main_loop (gpointer callback_data)
+{
+ guint level;
+
+ g_assert (callback_data == NULL);
+
+ level = gtk_main_level ();
+
+ /* We can be called even outside the main loop,
+ * so check that we are in a loop before calling quit.
+ */
+ if (level != 0)
+ {
+ gtk_main_quit ();
+ }
+
+ /* We need to be called again if we quit a nested loop. */
+ return level > 1;
+}
+
+static void eel_gtk_main_quit_all (void)
+{
+ /* Calling gtk_main_quit directly only kills the current/top event loop.
+ * This idler will be run by the current event loop, killing it, and then
+ * by the next event loop, ...
+ */
+ g_idle_add (quit_if_in_main_loop, NULL);
+}
+
+static void event_loop_unregister (GtkObject *object)
+{
+ event_loop_registrants = g_slist_remove (event_loop_registrants, object);
+
+ if (!is_event_loop_needed ())
+ {
+ eel_gtk_main_quit_all ();
+ }
+}
+
+void caja_main_event_loop_register (GtkObject *object)
+{
+ g_signal_connect (object, "destroy", G_CALLBACK (event_loop_unregister), NULL);
+ event_loop_registrants = g_slist_prepend (event_loop_registrants, object);
+}
+
+gboolean caja_main_is_event_loop_mainstay (GtkObject *object)
+{
+ return g_slist_length (event_loop_registrants) == 1
+ && event_loop_registrants->data == object;
+}
+
+void caja_main_event_loop_quit (gboolean explicit)
+{
+ if (explicit)
+ {
+ /* Explicit --quit, make sure we don't restart */
+
+ /* To quit all instances, reset exit_with_last_window */
+ exit_with_last_window = TRUE;
+
+ if (event_loop_registrants == NULL)
+ {
+ /* If this is reached, caja must run in "daemon" mode
+ * (i.e. !exit_with_last_window) with no windows open.
+ * We need to quit_all here because the below loop won't
+ * trigger a quit.
+ */
+ eel_gtk_main_quit_all();
+ }
+
+ /* TODO: With the old session we needed to set restart
+ style to MATE_RESTART_IF_RUNNING here, but i don't think we need
+ that now since mate-session doesn't restart apps except on startup. */
+ }
+ while (event_loop_registrants != NULL)
+ {
+ gtk_object_destroy (event_loop_registrants->data);
+ }
+}
+
+static void dump_debug_log (void)
+{
+ char *filename;
+
+ filename = g_build_filename (g_get_home_dir (), "caja-debug-log.txt", NULL);
+ caja_debug_log_dump (filename, NULL); /* NULL GError */
+ g_free (filename);
+}
+
+static int debug_log_pipes[2];
+
+static gboolean debug_log_io_cb (GIOChannel *io, GIOCondition condition, gpointer data)
+{
+ char a;
+
+ while (read (debug_log_pipes[0], &a, 1) != 1)
+ ;
+
+ caja_debug_log (TRUE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "user requested dump of debug log");
+
+ dump_debug_log ();
+ return FALSE;
+}
+
+static void sigusr1_handler (int sig)
+{
+ while (write (debug_log_pipes[1], "a", 1) != 1)
+ ;
+}
+
+/* This is totally broken as we're using non-signal safe
+ * calls in sigfatal_handler. Disable by default. */
+#ifdef USE_SEGV_HANDLER
+
+/* sigaction structures for the old handlers of these signals */
+static struct sigaction old_segv_sa;
+static struct sigaction old_abrt_sa;
+static struct sigaction old_trap_sa;
+static struct sigaction old_fpe_sa;
+static struct sigaction old_bus_sa;
+
+static void
+sigfatal_handler (int sig)
+{
+ void (* func) (int);
+
+ /* FIXME: is this totally busted? We do malloc() inside these functions,
+ * and yet we are inside a signal handler...
+ */
+ caja_debug_log (TRUE, CAJA_DEBUG_LOG_DOMAIN_USER,
+ "debug log dumped due to signal %d", sig);
+ dump_debug_log ();
+
+ switch (sig)
+ {
+ case SIGSEGV:
+ func = old_segv_sa.sa_handler;
+ break;
+
+ case SIGABRT:
+ func = old_abrt_sa.sa_handler;
+ break;
+
+ case SIGTRAP:
+ func = old_trap_sa.sa_handler;
+ break;
+
+ case SIGFPE:
+ func = old_fpe_sa.sa_handler;
+ break;
+
+ case SIGBUS:
+ func = old_bus_sa.sa_handler;
+ break;
+
+ default:
+ func = NULL;
+ break;
+ }
+
+ /* this scares me */
+ if (func != NULL && func != SIG_IGN && func != SIG_DFL)
+ (* func) (sig);
+}
+#endif
+
+static void
+setup_debug_log_signals (void)
+{
+ struct sigaction sa;
+ GIOChannel *io;
+
+ if (pipe (debug_log_pipes) == -1)
+ g_error ("Could not create pipe() for debug log");
+
+ io = g_io_channel_unix_new (debug_log_pipes[0]);
+ g_io_add_watch (io, G_IO_IN, debug_log_io_cb, NULL);
+
+ sa.sa_handler = sigusr1_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+ sigaction (SIGUSR1, &sa, NULL);
+
+ /* This is totally broken as we're using non-signal safe
+ * calls in sigfatal_handler. Disable by default. */
+#ifdef USE_SEGV_HANDLER
+ sa.sa_handler = sigfatal_handler;
+ sigemptyset (&sa.sa_mask);
+ sa.sa_flags = 0;
+
+ sigaction(SIGSEGV, &sa, &old_segv_sa);
+ sigaction(SIGABRT, &sa, &old_abrt_sa);
+ sigaction(SIGTRAP, &sa, &old_trap_sa);
+ sigaction(SIGFPE, &sa, &old_fpe_sa);
+ sigaction(SIGBUS, &sa, &old_bus_sa);
+#endif
+}
+
+static GLogFunc default_log_handler;
+
+static void
+log_override_cb (const gchar *log_domain,
+ GLogLevelFlags log_level,
+ const gchar *message,
+ gpointer user_data)
+{
+ gboolean is_debug;
+ gboolean is_milestone;
+
+ is_debug = ((log_level & G_LOG_LEVEL_DEBUG) != 0);
+ is_milestone = !is_debug;
+
+ caja_debug_log (is_milestone, CAJA_DEBUG_LOG_DOMAIN_GLOG, "%s", message);
+
+ if (!is_debug)
+ (* default_log_handler) (log_domain, log_level, message, user_data);
+}
+
+static void
+setup_debug_log_glog (void)
+{
+ default_log_handler = g_log_set_default_handler (log_override_cb, NULL);
+}
+
+static void
+setup_debug_log (void)
+{
+ char *config_filename;
+
+ config_filename = g_build_filename (g_get_home_dir (), "caja-debug-log.conf", NULL);
+ caja_debug_log_load_configuration (config_filename, NULL); /* NULL GError */
+ g_free (config_filename);
+
+ setup_debug_log_signals ();
+ setup_debug_log_glog ();
+}
+
+int
+main (int argc, char *argv[])
+{
+ gboolean kill_shell;
+ gboolean no_default_window;
+ gboolean browser_window;
+ gboolean no_desktop;
+ gboolean version;
+ gboolean autostart_mode;
+ const char *autostart_id;
+ gchar *geometry;
+ gchar **remaining;
+ gboolean perform_self_check;
+ CajaApplication *application;
+ GOptionContext *context;
+ GFile *file;
+ char *uri;
+ char **uris;
+ GPtrArray *uris_array;
+ GError *error;
+ int i;
+
+ const GOptionEntry options[] =
+ {
+#ifndef CAJA_OMIT_SELF_CHECK
+ {
+ "check", 'c', 0, G_OPTION_ARG_NONE, &perform_self_check,
+ N_("Perform a quick set of self-check tests."), NULL
+ },
+#endif
+ {
+ "version", '\0', 0, G_OPTION_ARG_NONE, &version,
+ N_("Show the version of the program."), NULL
+ },
+ {
+ "geometry", 'g', 0, G_OPTION_ARG_STRING, &geometry,
+ N_("Create the initial window with the given geometry."), N_("GEOMETRY")
+ },
+ {
+ "no-default-window", 'n', 0, G_OPTION_ARG_NONE, &no_default_window,
+ N_("Only create windows for explicitly specified URIs."), NULL
+ },
+ {
+ "no-desktop", '\0', 0, G_OPTION_ARG_NONE, &no_desktop,
+ N_("Do not manage the desktop (ignore the preference set in the preferences dialog)."), NULL
+ },
+ {
+ "browser", '\0', 0, G_OPTION_ARG_NONE, &browser_window,
+ N_("open a browser window."), NULL
+ },
+ {
+ "quit", 'q', 0, G_OPTION_ARG_NONE, &kill_shell,
+ N_("Quit Caja."), NULL
+ },
+ { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &remaining, NULL, N_("[URI...]") },
+
+ { NULL }
+ };
+
+#if defined (HAVE_MALLOPT) && defined(M_MMAP_THRESHOLD)
+ /* Caja uses lots and lots of small and medium size allocations,
+ * and then a few large ones for the desktop background. By default
+ * glibc uses a dynamic treshold for how large allocations should
+ * be mmaped. Unfortunately this triggers quickly for caja when
+ * it does the desktop background allocations, raising the limit
+ * such that a lot of temporary large allocations end up on the
+ * heap and are thus not returned to the OS. To fix this we set
+ * a hardcoded limit. I don't know what a good value is, but 128K
+ * was the old glibc static limit, lets use that.
+ */
+ mallopt (M_MMAP_THRESHOLD, 128 *1024);
+#endif
+
+ g_thread_init (NULL);
+
+ /* This will be done by gtk+ later, but for now, force it to MATE */
+ g_desktop_app_info_set_desktop_env ("MATE");
+
+ if (g_getenv ("CAJA_DEBUG") != NULL)
+ {
+ eel_make_warnings_and_criticals_stop_in_debugger ();
+ }
+
+ /* Initialize gettext support */
+ bindtextdomain (GETTEXT_PACKAGE, MATELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ autostart_mode = FALSE;
+
+ autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID");
+ if (autostart_id != NULL && *autostart_id != '\0')
+ {
+ autostart_mode = TRUE;
+ }
+
+ /* Get parameters. */
+ remaining = NULL;
+ geometry = NULL;
+ version = FALSE;
+ kill_shell = FALSE;
+ no_default_window = FALSE;
+ no_desktop = FALSE;
+ perform_self_check = FALSE;
+ browser_window = FALSE;
+
+ g_set_prgname ("caja");
+
+ if (g_file_test (DATADIR "/applications/caja.desktop", G_FILE_TEST_EXISTS))
+ {
+ egg_set_desktop_file (DATADIR "/applications/caja.desktop");
+ }
+
+ context = g_option_context_new (_("\n\nBrowse the file system with the file manager"));
+ g_option_context_add_main_entries (context, options, NULL);
+
+ g_option_context_add_group (context, gtk_get_option_group (TRUE));
+ g_option_context_add_group (context, egg_sm_client_get_option_group ());
+
+ error = NULL;
+ if (!g_option_context_parse (context, &argc, &argv, &error))
+ {
+ g_printerr ("Could not parse arguments: %s\n", error->message);
+ g_error_free (error);
+ return 1;
+ }
+
+ g_option_context_free (context);
+
+ if (version)
+ {
+ g_print ("MATE caja " PACKAGE_VERSION "\n");
+ return 0;
+ }
+
+#ifdef HAVE_EXEMPI
+ xmp_init();
+#endif
+
+ setup_debug_log ();
+
+ /* If in autostart mode (aka started by mate-session), we need to ensure
+ * caja starts with the correct options.
+ */
+ if (autostart_mode)
+ {
+ no_default_window = TRUE;
+ no_desktop = FALSE;
+ }
+
+ if (perform_self_check && remaining != NULL)
+ {
+ /* translators: %s is an option (e.g. --check) */
+ fprintf (stderr, _("caja: %s cannot be used with URIs.\n"),
+ "--check");
+ return EXIT_FAILURE;
+ }
+ if (perform_self_check && kill_shell)
+ {
+ fprintf (stderr, _("caja: --check cannot be used with other options.\n"));
+ return EXIT_FAILURE;
+ }
+ if (kill_shell && remaining != NULL)
+ {
+ fprintf (stderr, _("caja: %s cannot be used with URIs.\n"),
+ "--quit");
+ return EXIT_FAILURE;
+ }
+ if (geometry != NULL && remaining != NULL && remaining[0] != NULL && remaining[1] != NULL)
+ {
+ fprintf (stderr, _("caja: --geometry cannot be used with more than one URI.\n"));
+ return EXIT_FAILURE;
+ }
+
+ /* Initialize the services that we use. */
+ LIBXML_TEST_VERSION
+
+ /* Initialize preferences. This is needed so that proper
+ * defaults are available before any preference peeking
+ * happens.
+ */
+ caja_global_preferences_init ();
+
+ /* exit_with_last_window being FALSE, caja can run without window. */
+ exit_with_last_window = eel_preferences_get_boolean (CAJA_PREFERENCES_EXIT_WITH_LAST_WINDOW);
+
+ if (no_desktop)
+ {
+ eel_preferences_set_is_invisible
+ (CAJA_PREFERENCES_SHOW_DESKTOP, TRUE);
+ eel_preferences_set_is_invisible
+ (CAJA_PREFERENCES_DESKTOP_IS_HOME_DIR, TRUE);
+ }
+
+ application = NULL;
+
+ /* Do either the self-check or the real work. */
+ if (perform_self_check)
+ {
+ #ifndef CAJA_OMIT_SELF_CHECK
+ /* Run the checks (each twice) for caja and libcaja-private. */
+
+ caja_run_self_checks ();
+ caja_run_lib_self_checks ();
+ eel_exit_if_self_checks_failed ();
+
+ caja_run_self_checks ();
+ caja_run_lib_self_checks ();
+ eel_exit_if_self_checks_failed ();
+ #endif
+ }
+ else
+ {
+ /* Convert args to URIs */
+ uris = NULL;
+ if (remaining != NULL)
+ {
+ uris_array = g_ptr_array_new ();
+ for (i = 0; remaining[i] != NULL; i++)
+ {
+ file = g_file_new_for_commandline_arg (remaining[i]);
+ if (file != NULL)
+ {
+ uri = g_file_get_uri (file);
+ g_object_unref (file);
+ if (uri)
+ {
+ g_ptr_array_add (uris_array, uri);
+ }
+ }
+ }
+ g_ptr_array_add (uris_array, NULL);
+ uris = (char**) g_ptr_array_free (uris_array, FALSE);
+ g_strfreev (remaining);
+ }
+
+
+ /* Run the caja application. */
+ application = caja_application_new ();
+
+ if (egg_sm_client_is_resumed (application->smclient))
+ {
+ no_default_window = TRUE;
+ }
+
+ caja_application_startup
+ (application,
+ kill_shell, no_default_window, no_desktop,
+ browser_window,
+ geometry,
+ uris);
+ g_strfreev (uris);
+
+ if (unique_app_is_running (application->unique_app) ||
+ kill_shell)
+ {
+ exit_with_last_window = TRUE;
+ }
+
+ if (is_event_loop_needed ())
+ {
+ gtk_main ();
+ }
+ }
+
+ caja_icon_info_clear_caches ();
+
+ if (application != NULL)
+ {
+ g_object_unref (application);
+ }
+
+ eel_debug_shut_down ();
+
+ caja_application_save_accel_map (NULL);
+
+ return EXIT_SUCCESS;
+}