Minor changelog updates
[pidgin-git.git] / pidgin / gtkmain.c
blob0e8463e0c9200eb63d25c424365cb888303191de
1 /*
2 * pidgin
4 * Pidgin is the legal property of its developers, whose names are too numerous
5 * to list here. Please refer to the COPYRIGHT file distributed with this
6 * source distribution.
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02111-1301 USA
24 #include "internal.h"
25 #include "pidgin.h"
27 #include "account.h"
28 #include "conversation.h"
29 #include "core.h"
30 #include "debug.h"
31 #include "eventloop.h"
32 #include "ft.h"
33 #include "log.h"
34 #include "network.h"
35 #include "notify.h"
36 #include "prefs.h"
37 #include "prpl.h"
38 #include "pounce.h"
39 #include "sound.h"
40 #include "status.h"
41 #include "util.h"
42 #include "whiteboard.h"
44 #include "gtkaccount.h"
45 #include "gtkblist.h"
46 #include "gtkconn.h"
47 #include "gtkconv.h"
48 #include "gtkdebug.h"
49 #include "gtkdialogs.h"
50 #include "gtkdocklet.h"
51 #include "gtkeventloop.h"
52 #include "gtkft.h"
53 #include "gtkidle.h"
54 #include "gtklog.h"
55 #include "gtknotify.h"
56 #include "gtkplugin.h"
57 #include "gtkpounce.h"
58 #include "gtkprefs.h"
59 #include "gtkprivacy.h"
60 #include "gtkrequest.h"
61 #include "gtkroomlist.h"
62 #include "gtksavedstatuses.h"
63 #include "gtksession.h"
64 #include "gtksound.h"
65 #include "gtkthemes.h"
66 #include "gtkutils.h"
67 #include "pidginstock.h"
68 #include "gtkwhiteboard.h"
70 #ifdef HAVE_SIGNAL_H
71 # include <signal.h>
72 #endif
74 #include <getopt.h>
76 #ifdef HAVE_STARTUP_NOTIFICATION
77 # define SN_API_NOT_YET_FROZEN
78 # include <libsn/sn-launchee.h>
79 # include <gdk/gdkx.h>
80 #endif
84 #ifdef HAVE_STARTUP_NOTIFICATION
85 static SnLauncheeContext *sn_context = NULL;
86 static SnDisplay *sn_display = NULL;
87 #endif
89 #ifdef HAVE_SIGNAL_H
92 * Lists of signals we wish to catch and those we wish to ignore.
93 * Each list terminated with -1
95 static int catch_sig_list[] = {
96 SIGSEGV,
97 SIGHUP,
98 SIGINT,
99 SIGTERM,
100 SIGQUIT,
101 SIGCHLD,
102 SIGALRM,
106 static int ignore_sig_list[] = {
107 SIGPIPE,
110 #endif
112 static void
113 dologin_named(const char *name)
115 PurpleAccount *account;
116 char **names;
117 int i;
119 if (name != NULL) { /* list of names given */
120 names = g_strsplit(name, ",", 64);
121 for (i = 0; names[i] != NULL; i++) {
122 account = purple_accounts_find(names[i], NULL);
123 if (account != NULL) { /* found a user */
124 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
127 g_strfreev(names);
128 } else { /* no name given, use the first account */
129 GList *accounts;
131 accounts = purple_accounts_get_all();
132 if (accounts != NULL)
134 account = (PurpleAccount *)accounts->data;
135 purple_account_set_enabled(account, PIDGIN_UI, TRUE);
140 #ifdef HAVE_SIGNAL_H
141 static void sighandler(int sig);
144 * Reap all our dead children. Sometimes libpurple forks off a separate
145 * process to do some stuff. When that process exits we are
146 * informed about it so that we can call waitpid() and let it
147 * stop being a zombie.
149 * We used to do this immediately when our signal handler was
150 * called, but because of GStreamer we now wait one second before
151 * reaping anything. Why? For some reason GStreamer fork()s
152 * during their initialization process. I don't understand why...
153 * but they do it, and there's nothing we can do about it.
155 * Anyway, so then GStreamer waits for its child to die and then
156 * it continues with the initialization process. This means that
157 * we have a race condition where GStreamer is waitpid()ing for its
158 * child to die and we're catching the SIGCHLD signal. If GStreamer
159 * is awarded the zombied process then everything is ok. But if libpurple
160 * reaps the zombie process then the GStreamer initialization sequence
161 * fails.
163 * So the ugly solution is to wait a second to give GStreamer time to
164 * reap that bad boy.
166 * GStreamer 0.10.10 and newer have a gst_register_fork_set_enabled()
167 * function that can be called by applications to disable forking
168 * during initialization. But it's not in 0.10.0, so we shouldn't
169 * use it.
171 * All of this child process reaping stuff is currently only used for
172 * processes that were forked to play sounds. It's not needed for
173 * forked DNS child, which have their own waitpid() call. It might
174 * be wise to move this code into gtksound.c.
176 static void
177 clean_pid()
179 int status;
180 pid_t pid;
182 do {
183 pid = waitpid(-1, &status, WNOHANG);
184 } while (pid != 0 && pid != (pid_t)-1);
186 if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
187 char errmsg[BUFSIZ];
188 snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
189 perror(errmsg);
192 /* Restore signal catching */
193 signal(SIGALRM, sighandler);
196 char *segfault_message;
198 static void
199 sighandler(int sig)
201 switch (sig) {
202 case SIGHUP:
203 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
204 purple_connections_disconnect_all();
205 break;
206 case SIGSEGV:
207 fprintf(stderr, "%s", segfault_message);
208 abort();
209 break;
210 case SIGCHLD:
211 /* Restore signal catching */
212 signal(SIGCHLD, sighandler);
213 alarm(1);
214 break;
215 case SIGALRM:
216 clean_pid();
217 break;
218 default:
219 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
220 purple_connections_disconnect_all();
222 purple_plugins_unload_all();
224 if (gtk_main_level())
225 gtk_main_quit();
226 exit(0);
229 #endif
231 static int
232 ui_main()
234 #ifndef _WIN32
235 GList *icons = NULL;
236 GdkPixbuf *icon = NULL;
237 char *icon_path;
238 int i;
239 struct {
240 const char *dir;
241 const char *filename;
242 } icon_sizes[] = {
243 {"16x16", "pidgin.png"},
244 {"24x24", "pidgin.png"},
245 {"32x32", "pidgin.png"},
246 {"48x48", "pidgin.png"},
247 {"scalable", "pidgin.svg"}
250 #endif
252 pidgin_themes_init();
254 pidgin_blist_setup_sort_methods();
256 #ifndef _WIN32
257 /* use the nice PNG icon for all the windows */
258 for(i=0; i<G_N_ELEMENTS(icon_sizes); i++) {
259 icon_path = g_build_filename(DATADIR, "icons", "hicolor", icon_sizes[i].dir, "apps", icon_sizes[i].filename, NULL);
260 icon = gdk_pixbuf_new_from_file(icon_path, NULL);
261 g_free(icon_path);
262 if (icon) {
263 icons = g_list_append(icons,icon);
264 } else {
265 purple_debug_error("ui_main",
266 "Failed to load the default window icon (%spx version)!\n", icon_sizes[i]);
269 if(NULL == icons) {
270 purple_debug_error("ui_main", "Unable to load any size of default window icon!\n");
271 } else {
272 gtk_window_set_default_icon_list(icons);
274 g_list_foreach(icons, (GFunc)g_object_unref, NULL);
275 g_list_free(icons);
277 #endif
279 return 0;
282 static void
283 debug_init(void)
285 purple_debug_set_ui_ops(pidgin_debug_get_ui_ops());
286 pidgin_debug_init();
289 static void
290 pidgin_ui_init(void)
292 /* Set the UI operation structures. */
293 purple_accounts_set_ui_ops(pidgin_accounts_get_ui_ops());
294 purple_xfers_set_ui_ops(pidgin_xfers_get_ui_ops());
295 purple_blist_set_ui_ops(pidgin_blist_get_ui_ops());
296 purple_notify_set_ui_ops(pidgin_notify_get_ui_ops());
297 purple_privacy_set_ui_ops(pidgin_privacy_get_ui_ops());
298 purple_request_set_ui_ops(pidgin_request_get_ui_ops());
299 purple_sound_set_ui_ops(pidgin_sound_get_ui_ops());
300 purple_connections_set_ui_ops(pidgin_connections_get_ui_ops());
301 purple_whiteboard_set_ui_ops(pidgin_whiteboard_get_ui_ops());
302 #ifdef USE_SCREENSAVER
303 purple_idle_set_ui_ops(pidgin_idle_get_ui_ops());
304 #endif
306 pidgin_stock_init();
307 pidgin_account_init();
308 pidgin_connection_init();
309 pidgin_blist_init();
310 pidgin_status_init();
311 pidgin_conversations_init();
312 pidgin_pounces_init();
313 pidgin_privacy_init();
314 pidgin_xfers_init();
315 pidgin_roomlist_init();
316 pidgin_log_init();
317 pidgin_docklet_init();
320 static GHashTable *ui_info = NULL;
322 static void
323 pidgin_quit(void)
325 #ifdef USE_SM
326 /* unplug */
327 pidgin_session_end();
328 #endif
330 /* Save the plugins we have loaded for next time. */
331 pidgin_plugins_save();
333 /* Uninit */
334 pidgin_conversations_uninit();
335 pidgin_status_uninit();
336 pidgin_docklet_uninit();
337 pidgin_blist_uninit();
338 pidgin_connection_uninit();
339 pidgin_account_uninit();
340 pidgin_xfers_uninit();
341 pidgin_debug_uninit();
343 if(NULL != ui_info)
344 g_hash_table_destroy(ui_info);
346 /* and end it all... */
347 gtk_main_quit();
350 static GHashTable *pidgin_ui_get_info()
352 if(NULL == ui_info) {
353 ui_info = g_hash_table_new(g_str_hash, g_str_equal);
355 g_hash_table_insert(ui_info, "name", (char*)PIDGIN_NAME);
356 g_hash_table_insert(ui_info, "version", VERSION);
359 return ui_info;
362 static PurpleCoreUiOps core_ops =
364 pidgin_prefs_init,
365 debug_init,
366 pidgin_ui_init,
367 pidgin_quit,
368 pidgin_ui_get_info,
369 NULL,
370 NULL,
371 NULL
374 static PurpleCoreUiOps *
375 pidgin_core_get_ui_ops(void)
377 return &core_ops;
380 static void
381 show_usage(const char *name, gboolean terse)
383 char *text;
385 if (terse) {
386 text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, VERSION, name);
387 } else {
388 text = g_strdup_printf(_("%s %s\n"
389 "Usage: %s [OPTION]...\n\n"
390 " -c, --config=DIR use DIR for config files\n"
391 " -d, --debug print debugging messages to stdout\n"
392 " -h, --help display this help and exit\n"
393 " -m, --multiple do not ensure single instance\n"
394 " -n, --nologin don't automatically login\n"
395 " -l, --login[=NAME] automatically login (optional argument NAME specifies\n"
396 " account(s) to use, separated by commas)\n"
397 " -v, --version display the current version and exit\n"), PIDGIN_NAME, VERSION, name);
400 purple_print_utf8_to_console(stdout, text);
401 g_free(text);
404 #ifdef HAVE_STARTUP_NOTIFICATION
405 static void
406 sn_error_trap_push(SnDisplay *display, Display *xdisplay)
408 gdk_error_trap_push();
411 static void
412 sn_error_trap_pop(SnDisplay *display, Display *xdisplay)
414 gdk_error_trap_pop();
417 static void
418 startup_notification_complete(void)
420 Display *xdisplay;
422 xdisplay = GDK_DISPLAY();
423 sn_display = sn_display_new(xdisplay,
424 sn_error_trap_push,
425 sn_error_trap_pop);
426 sn_context =
427 sn_launchee_context_new_from_environment(sn_display,
428 DefaultScreen(xdisplay));
430 if (sn_context != NULL)
432 sn_launchee_context_complete(sn_context);
433 sn_launchee_context_unref(sn_context);
435 sn_display_unref(sn_display);
438 #endif /* HAVE_STARTUP_NOTIFICATION */
440 /* FUCKING GET ME A TOWEL! */
441 #ifdef _WIN32
442 /* suppress gcc "no previous prototype" warning */
443 int pidgin_main(HINSTANCE hint, int argc, char *argv[]);
444 int pidgin_main(HINSTANCE hint, int argc, char *argv[])
445 #else
446 int main(int argc, char *argv[])
447 #endif
449 gboolean opt_help = FALSE;
450 gboolean opt_login = FALSE;
451 gboolean opt_nologin = FALSE;
452 gboolean opt_version = FALSE;
453 gboolean opt_si = TRUE; /* Check for single instance? */
454 char *opt_config_dir_arg = NULL;
455 char *opt_login_arg = NULL;
456 char *opt_session_arg = NULL;
457 char *search_path;
458 GList *accounts;
459 #ifdef HAVE_SIGNAL_H
460 int sig_indx; /* for setting up signal catching */
461 sigset_t sigset;
462 RETSIGTYPE (*prev_sig_disp)(int);
463 char errmsg[BUFSIZ];
464 #ifndef DEBUG
465 char *segfault_message_tmp;
466 GError *error = NULL;
467 #endif
468 #endif
469 int opt;
470 gboolean gui_check;
471 gboolean debug_enabled;
472 gboolean migration_failed = FALSE;
473 GList *active_accounts;
475 struct option long_options[] = {
476 {"config", required_argument, NULL, 'c'},
477 {"debug", no_argument, NULL, 'd'},
478 {"help", no_argument, NULL, 'h'},
479 {"login", optional_argument, NULL, 'l'},
480 {"multiple", no_argument, NULL, 'm'},
481 {"nologin", no_argument, NULL, 'n'},
482 {"session", required_argument, NULL, 's'},
483 {"version", no_argument, NULL, 'v'},
484 {0, 0, 0, 0}
487 #ifdef DEBUG
488 debug_enabled = TRUE;
489 #else
490 debug_enabled = FALSE;
491 #endif
493 /* This is the first Glib function call. Make sure to initialize GThread bfeore then */
494 g_thread_init(NULL);
496 #ifdef ENABLE_NLS
497 bindtextdomain(PACKAGE, LOCALEDIR);
498 bind_textdomain_codeset(PACKAGE, "UTF-8");
499 textdomain(PACKAGE);
500 #endif
502 #ifdef HAVE_SETLOCALE
503 /* Locale initialization is not complete here. See gtk_init_check() */
504 setlocale(LC_ALL, "");
505 #endif
507 #ifdef HAVE_SIGNAL_H
509 #ifndef DEBUG
510 /* We translate this here in case the crash breaks gettext. */
511 segfault_message_tmp = g_strdup_printf(_(
512 "%s has segfaulted and attempted to dump a core file.\n"
513 "This is a bug in the software and has happened through\n"
514 "no fault of your own.\n\n"
515 "If you can reproduce the crash, please notify the developers\n"
516 "by reporting a bug at:\n"
517 "%ssimpleticket/\n\n"
518 "Please make sure to specify what you were doing at the time\n"
519 "and post the backtrace from the core file. If you do not know\n"
520 "how to get the backtrace, please read the instructions at\n"
521 "%swiki/GetABacktrace\n\n"
522 "If you need further assistance, please IM either SeanEgn or \n"
523 "LSchiere (via AIM). Contact information for Sean and Luke \n"
524 "on other protocols is at\n"
525 "%swiki/DeveloperPages\n"),
526 PIDGIN_NAME, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE, PURPLE_DEVEL_WEBSITE
529 /* we have to convert the message (UTF-8 to console
530 charset) early because after a segmentation fault
531 it's not a good practice to allocate memory */
532 segfault_message = g_locale_from_utf8(segfault_message_tmp,
533 -1, NULL, NULL, &error);
534 if (segfault_message != NULL) {
535 g_free(segfault_message_tmp);
537 else {
538 /* use 'segfault_message_tmp' (UTF-8) as a fallback */
539 g_warning("%s\n", error->message);
540 g_error_free(error);
541 segfault_message = segfault_message_tmp;
543 #else
544 /* Don't mark this for translation. */
545 segfault_message = g_strdup(
546 "Hi, user. We need to talk.\n"
547 "I think something's gone wrong here. It's probably my fault.\n"
548 "No, really, it's not you... it's me... no no no, I think we get along well\n"
549 "it's just that.... well, I want to see other people. I... what?!? NO! I \n"
550 "haven't been cheating on you!! How many times do you want me to tell you?! And\n"
551 "for the last time, it's just a rash!\n"
553 #endif
555 /* Let's not violate any PLA's!!!! */
556 /* jseymour: whatever the fsck that means */
557 /* Robot101: for some reason things like gdm like to block *
558 * useful signals like SIGCHLD, so we unblock all the ones we *
559 * declare a handler for. thanks JSeymour and Vann. */
560 if (sigemptyset(&sigset)) {
561 snprintf(errmsg, BUFSIZ, "Warning: couldn't initialise empty signal set");
562 perror(errmsg);
564 for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) {
565 if((prev_sig_disp = signal(catch_sig_list[sig_indx], sighandler)) == SIG_ERR) {
566 snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d for catching",
567 catch_sig_list[sig_indx]);
568 perror(errmsg);
570 if(sigaddset(&sigset, catch_sig_list[sig_indx])) {
571 snprintf(errmsg, BUFSIZ, "Warning: couldn't include signal %d for unblocking",
572 catch_sig_list[sig_indx]);
573 perror(errmsg);
576 for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) {
577 if((prev_sig_disp = signal(ignore_sig_list[sig_indx], SIG_IGN)) == SIG_ERR) {
578 snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d to ignore",
579 ignore_sig_list[sig_indx]);
580 perror(errmsg);
584 if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) {
585 snprintf(errmsg, BUFSIZ, "Warning: couldn't unblock signals");
586 perror(errmsg);
588 #endif
590 /* scan command-line options */
591 opterr = 1;
592 while ((opt = getopt_long(argc, argv,
593 #ifndef _WIN32
594 "c:dhmnl::s:v",
595 #else
596 "c:dhnl::v",
597 #endif
598 long_options, NULL)) != -1) {
599 switch (opt) {
600 case 'c': /* config dir */
601 g_free(opt_config_dir_arg);
602 opt_config_dir_arg = g_strdup(optarg);
603 break;
604 case 'd': /* debug */
605 debug_enabled = TRUE;
606 break;
607 case 'h': /* help */
608 opt_help = TRUE;
609 break;
610 case 'n': /* no autologin */
611 opt_nologin = TRUE;
612 break;
613 case 'l': /* login, option username */
614 opt_login = TRUE;
615 g_free(opt_login_arg);
616 if (optarg != NULL)
617 opt_login_arg = g_strdup(optarg);
618 break;
619 case 's': /* use existing session ID */
620 g_free(opt_session_arg);
621 opt_session_arg = g_strdup(optarg);
622 break;
623 case 'v': /* version */
624 opt_version = TRUE;
625 break;
626 case 'm': /* do not ensure single instance. */
627 opt_si = FALSE;
628 break;
629 case '?': /* show terse help */
630 default:
631 show_usage(argv[0], TRUE);
632 #ifdef HAVE_SIGNAL_H
633 g_free(segfault_message);
634 #endif
635 return 0;
636 break;
640 /* show help message */
641 if (opt_help) {
642 show_usage(argv[0], FALSE);
643 #ifdef HAVE_SIGNAL_H
644 g_free(segfault_message);
645 #endif
646 return 0;
648 /* show version message */
649 if (opt_version) {
650 printf("%s %s\n", PIDGIN_NAME, VERSION);
651 #ifdef HAVE_SIGNAL_H
652 g_free(segfault_message);
653 #endif
654 return 0;
657 /* set a user-specified config directory */
658 if (opt_config_dir_arg != NULL) {
659 purple_util_set_user_dir(opt_config_dir_arg);
663 * We're done piddling around with command line arguments.
664 * Fire up this baby.
667 purple_debug_set_enabled(debug_enabled);
669 /* If we're using a custom configuration directory, we
670 * do NOT want to migrate, or weird things will happen. */
671 if (opt_config_dir_arg == NULL)
673 if (!purple_core_migrate())
675 migration_failed = TRUE;
679 search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL);
680 gtk_rc_add_default_file(search_path);
681 g_free(search_path);
683 gui_check = gtk_init_check(&argc, &argv);
684 if (!gui_check) {
685 char *display = gdk_get_display();
687 printf("%s %s\n", PIDGIN_NAME, VERSION);
689 g_warning("cannot open display: %s", display ? display : "unset");
690 g_free(display);
691 #ifdef HAVE_SIGNAL_H
692 g_free(segfault_message);
693 #endif
695 return 1;
698 #if GLIB_CHECK_VERSION(2,2,0)
699 g_set_application_name(_("Pidgin"));
700 #endif /* glib-2.0 >= 2.2.0 */
702 #ifdef _WIN32
703 winpidgin_init(hint);
704 #endif
706 if (migration_failed)
708 char *old = g_strconcat(purple_home_dir(),
709 G_DIR_SEPARATOR_S ".gaim", NULL);
710 const char *text = _(
711 "%s encountered errors migrating your settings "
712 "from %s to %s. Please investigate and complete the "
713 "migration by hand. Please report this error at http://developer.pidgin.im");
714 GtkWidget *dialog;
716 dialog = gtk_message_dialog_new(NULL,
718 GTK_MESSAGE_ERROR,
719 GTK_BUTTONS_CLOSE,
720 text, PIDGIN_NAME,
721 old, purple_user_dir());
722 g_free(old);
724 g_signal_connect_swapped(dialog, "response",
725 G_CALLBACK(gtk_main_quit), NULL);
727 gtk_widget_show_all(dialog);
729 gtk_main();
731 #ifdef HAVE_SIGNAL_H
732 g_free(segfault_message);
733 #endif
734 return 0;
737 purple_core_set_ui_ops(pidgin_core_get_ui_ops());
738 purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
741 * Set plugin search directories. Give priority to the plugins
742 * in user's home directory.
744 search_path = g_build_filename(purple_user_dir(), "plugins", NULL);
745 purple_plugins_add_search_path(search_path);
746 g_free(search_path);
747 purple_plugins_add_search_path(LIBDIR);
749 if (!purple_core_init(PIDGIN_UI)) {
750 fprintf(stderr,
751 "Initialization of the libpurple core failed. Dumping core.\n"
752 "Please report this!\n");
753 #ifdef HAVE_SIGNAL_H
754 g_free(segfault_message);
755 #endif
756 abort();
759 if (opt_si && !purple_core_ensure_single_instance()) {
760 purple_core_quit();
761 #ifdef HAVE_SIGNAL_H
762 g_free(segfault_message);
763 #endif
764 return 0;
767 /* TODO: Move blist loading into purple_blist_init() */
768 purple_set_blist(purple_blist_new());
769 purple_blist_load();
771 /* load plugins we had when we quit */
772 purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
774 /* TODO: Move pounces loading into purple_pounces_init() */
775 purple_pounces_load();
777 ui_main();
779 #ifdef USE_SM
780 pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg);
781 #endif
782 if (opt_session_arg != NULL) {
783 g_free(opt_session_arg);
784 opt_session_arg = NULL;
786 if (opt_config_dir_arg != NULL) {
787 g_free(opt_config_dir_arg);
788 opt_config_dir_arg = NULL;
792 * We want to show the blist early in the init process so the
793 * user feels warm and fuzzy (not cold and prickley).
795 purple_blist_show();
797 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
798 pidgin_debug_window_show();
800 if (opt_login) {
801 /* disable all accounts */
802 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
803 PurpleAccount *account = accounts->data;
804 purple_account_set_enabled(account, PIDGIN_UI, FALSE);
806 /* honor the startup status preference */
807 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
808 purple_savedstatus_activate(purple_savedstatus_get_startup());
809 /* now enable the requested ones */
810 dologin_named(opt_login_arg);
811 if (opt_login_arg != NULL) {
812 g_free(opt_login_arg);
813 opt_login_arg = NULL;
815 } else if (opt_nologin) {
816 /* Set all accounts to "offline" */
817 PurpleSavedStatus *saved_status;
819 /* If we've used this type+message before, lookup the transient status */
820 saved_status = purple_savedstatus_find_transient_by_type_and_message(
821 PURPLE_STATUS_OFFLINE, NULL);
823 /* If this type+message is unique then create a new transient saved status */
824 if (saved_status == NULL)
825 saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
827 /* Set the status for each account */
828 purple_savedstatus_activate(saved_status);
829 } else {
830 /* Everything is good to go--sign on already */
831 if (!purple_prefs_get_bool("/purple/savedstatus/startup_current_status"))
832 purple_savedstatus_activate(purple_savedstatus_get_startup());
833 purple_accounts_restore_current_statuses();
836 if ((active_accounts = purple_accounts_get_all_active()) == NULL)
838 pidgin_accounts_window_show();
840 else
842 g_list_free(active_accounts);
845 #ifdef HAVE_STARTUP_NOTIFICATION
846 startup_notification_complete();
847 #endif
849 #ifdef _WIN32
850 winpidgin_post_init();
851 #endif
853 gtk_main();
855 #ifdef HAVE_SIGNAL_H
856 g_free(segfault_message);
857 #endif
859 #ifdef _WIN32
860 winpidgin_cleanup();
861 #endif
863 return 0;