pidgin/gtkmain.c

branch
cpw.khc.msnp14
changeset 20478
46933dc62880
parent 20472
6a6d2ef151e6
parent 16158
04028f467df0
child 20481
65485e2ed8a3
equal deleted inserted replaced
20476:198222e01a7d 20478:46933dc62880
1 /*
2 * pidgin
3 *
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.
7 *
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.
12 *
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.
17 *
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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24 #include "internal.h"
25 #include "pidgin.h"
26
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 "notify.h"
35 #include "prefs.h"
36 #include "prpl.h"
37 #include "pounce.h"
38 #include "sound.h"
39 #include "status.h"
40 #include "util.h"
41 #include "whiteboard.h"
42
43 #include "gtkaccount.h"
44 #include "gtkblist.h"
45 #include "gtkconn.h"
46 #include "gtkconv.h"
47 #include "gtkdebug.h"
48 #include "gtkdialogs.h"
49 #include "gtkdocklet.h"
50 #include "gtkeventloop.h"
51 #include "gtkft.h"
52 #include "gtkidle.h"
53 #include "gtklog.h"
54 #include "gtknotify.h"
55 #include "gtkplugin.h"
56 #include "gtkpounce.h"
57 #include "gtkprefs.h"
58 #include "gtkprivacy.h"
59 #include "gtkrequest.h"
60 #include "gtkroomlist.h"
61 #include "gtksavedstatuses.h"
62 #include "gtksession.h"
63 #include "gtksound.h"
64 #include "gtkthemes.h"
65 #include "gtkutils.h"
66 #include "pidginstock.h"
67 #include "gtkwhiteboard.h"
68
69 #ifdef HAVE_SIGNAL_H
70 # include <signal.h>
71 #endif
72
73 #include <getopt.h>
74
75 #ifdef HAVE_STARTUP_NOTIFICATION
76 # define SN_API_NOT_YET_FROZEN
77 # include <libsn/sn-launchee.h>
78 # include <gdk/gdkx.h>
79 #endif
80
81
82
83 #ifdef HAVE_STARTUP_NOTIFICATION
84 static SnLauncheeContext *sn_context = NULL;
85 static SnDisplay *sn_display = NULL;
86 #endif
87
88 #ifdef HAVE_SIGNAL_H
89
90 /*
91 * Lists of signals we wish to catch and those we wish to ignore.
92 * Each list terminated with -1
93 */
94 static int catch_sig_list[] = {
95 SIGSEGV,
96 SIGHUP,
97 SIGINT,
98 SIGTERM,
99 SIGQUIT,
100 SIGCHLD,
101 SIGALRM,
102 -1
103 };
104
105 static int ignore_sig_list[] = {
106 SIGPIPE,
107 -1
108 };
109 #endif
110
111 static int
112 dologin_named(const char *name)
113 {
114 PurpleAccount *account;
115 char **names;
116 int i;
117 int ret = -1;
118
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 ret = 0;
125 purple_account_connect(account);
126 }
127 }
128 g_strfreev(names);
129 } else { /* no name given, use the first account */
130 GList *accounts;
131
132 accounts = purple_accounts_get_all();
133 if (accounts != NULL)
134 {
135 account = (PurpleAccount *)accounts->data;
136 ret = 0;
137 purple_account_connect(account);
138 }
139 }
140
141 return ret;
142 }
143
144 #ifdef HAVE_SIGNAL_H
145 static void sighandler(int sig);
146
147 /**
148 * Reap all our dead children. Sometimes Purple forks off a separate
149 * process to do some stuff. When that process exits we are
150 * informed about it so that we can call waitpid() and let it
151 * stop being a zombie.
152 *
153 * We used to do this immediately when our signal handler was
154 * called, but because of GStreamer we now wait one second before
155 * reaping anything. Why? For some reason GStreamer fork()s
156 * during their initialization process. I don't understand why...
157 * but they do it, and there's nothing we can do about it.
158 *
159 * Anyway, so then GStreamer waits for its child to die and then
160 * it continues with the initialization process. This means that
161 * we have a race condition where GStreamer is waitpid()ing for its
162 * child to die and we're catching the SIGCHLD signal. If GStreamer
163 * is awarded the zombied process then everything is ok. But if Purple
164 * reaps the zombie process then the GStreamer initialization sequence
165 * fails.
166 *
167 * So the ugly solution is to wait a second to give GStreamer time to
168 * reap that bad boy.
169 *
170 * GStreamer 0.10.10 and newer have a gst_register_fork_set_enabled()
171 * function that can be called by applications to disable forking
172 * during initialization. But it's not in 0.10.0, so we shouldn't
173 * use it.
174 *
175 * All of this child process reaping stuff is currently only used for
176 * processes that were forked to play sounds. It's not needed for
177 * forked DNS child, which have their own waitpid() call. It might
178 * be wise to move this code into gtksound.c.
179 */
180 static void
181 clean_pid()
182 {
183 int status;
184 pid_t pid;
185
186 do {
187 pid = waitpid(-1, &status, WNOHANG);
188 } while (pid != 0 && pid != (pid_t)-1);
189
190 if ((pid == (pid_t) - 1) && (errno != ECHILD)) {
191 char errmsg[BUFSIZ];
192 snprintf(errmsg, BUFSIZ, "Warning: waitpid() returned %d", pid);
193 perror(errmsg);
194 }
195
196 /* Restore signal catching */
197 signal(SIGALRM, sighandler);
198 }
199
200 char *segfault_message;
201
202 static void
203 sighandler(int sig)
204 {
205 switch (sig) {
206 case SIGHUP:
207 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
208 purple_connections_disconnect_all();
209 break;
210 case SIGSEGV:
211 fprintf(stderr, "%s", segfault_message);
212 abort();
213 break;
214 case SIGCHLD:
215 /* Restore signal catching */
216 signal(SIGCHLD, sighandler);
217 alarm(1);
218 break;
219 case SIGALRM:
220 clean_pid();
221 break;
222 default:
223 purple_debug_warning("sighandler", "Caught signal %d\n", sig);
224 purple_connections_disconnect_all();
225
226 purple_plugins_unload_all();
227
228 if (gtk_main_level())
229 gtk_main_quit();
230 exit(0);
231 }
232 }
233 #endif
234
235 static int
236 ui_main()
237 {
238 #ifndef _WIN32
239 GList *icons = NULL;
240 GdkPixbuf *icon = NULL;
241 char *icon_path;
242 int i;
243 const char *icon_sizes[] = {
244 "16",
245 "24",
246 "32",
247 "48"
248 };
249
250 #endif
251
252 pidginthemes_init();
253
254 pidgin_blist_setup_sort_methods();
255
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, "pixmaps", "pidgin", "icons", icon_sizes[i], "pidgin.png", 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]);
267 }
268 }
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);
273
274 g_list_foreach(icons, (GFunc)g_object_unref, NULL);
275 g_list_free(icons);
276 }
277 #endif
278
279 return 0;
280 }
281
282 static void
283 debug_init(void)
284 {
285 purple_debug_set_ui_ops(pidgin_debug_get_ui_ops());
286 pidgin_debug_init();
287 }
288
289 static void
290 pidgin_ui_init(void)
291 {
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
305
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 }
318
319 static void
320 pidgin_quit(void)
321 {
322 #ifdef USE_SM
323 /* unplug */
324 pidgin_session_end();
325 #endif
326
327 /* Save the plugins we have loaded for next time. */
328 pidgin_plugins_save();
329
330 /* Uninit */
331 pidgin_conversations_uninit();
332 pidgin_status_uninit();
333 pidgin_docklet_uninit();
334 pidgin_blist_uninit();
335 pidgin_connection_uninit();
336 pidgin_account_uninit();
337 pidgin_xfers_uninit();
338 pidgin_debug_uninit();
339
340 /* and end it all... */
341 gtk_main_quit();
342 }
343
344 static PurpleCoreUiOps core_ops =
345 {
346 pidgin_prefs_init,
347 debug_init,
348 pidgin_ui_init,
349 pidgin_quit
350 };
351
352 static PurpleCoreUiOps *
353 pidgin_core_get_ui_ops(void)
354 {
355 return &core_ops;
356 }
357
358 static void
359 show_usage(const char *name, gboolean terse)
360 {
361 char *text;
362
363 if (terse) {
364 text = g_strdup_printf(_("%s %s. Try `%s -h' for more information.\n"), PIDGIN_NAME, VERSION, name);
365 } else {
366 text = g_strdup_printf(_("%s %s\n"
367 "Usage: %s [OPTION]...\n\n"
368 " -c, --config=DIR use DIR for config files\n"
369 " -d, --debug print debugging messages to stdout\n"
370 " -h, --help display this help and exit\n"
371 " -n, --nologin don't automatically login\n"
372 " -l, --login[=NAME] automatically login (optional argument NAME specifies\n"
373 " account(s) to use, separated by commas)\n"
374 " -v, --version display the current version and exit\n"), PIDGIN_NAME, VERSION, name);
375 }
376
377 purple_print_utf8_to_console(stdout, text);
378 g_free(text);
379 }
380
381 #ifdef HAVE_STARTUP_NOTIFICATION
382 static void
383 sn_error_trap_push(SnDisplay *display, Display *xdisplay)
384 {
385 gdk_error_trap_push();
386 }
387
388 static void
389 sn_error_trap_pop(SnDisplay *display, Display *xdisplay)
390 {
391 gdk_error_trap_pop();
392 }
393
394 static void
395 startup_notification_complete(void)
396 {
397 Display *xdisplay;
398
399 xdisplay = GDK_DISPLAY();
400 sn_display = sn_display_new(xdisplay,
401 sn_error_trap_push,
402 sn_error_trap_pop);
403 sn_context =
404 sn_launchee_context_new_from_environment(sn_display,
405 DefaultScreen(xdisplay));
406
407 if (sn_context != NULL)
408 {
409 sn_launchee_context_complete(sn_context);
410 sn_launchee_context_unref(sn_context);
411
412 sn_display_unref(sn_display);
413 }
414 }
415 #endif /* HAVE_STARTUP_NOTIFICATION */
416
417 /* FUCKING GET ME A TOWEL! */
418 #ifdef _WIN32
419 /* suppress gcc "no previous prototype" warning */
420 int pidgin_main(HINSTANCE hint, int argc, char *argv[]);
421 int pidgin_main(HINSTANCE hint, int argc, char *argv[])
422 #else
423 int main(int argc, char *argv[])
424 #endif
425 {
426 gboolean opt_help = FALSE;
427 gboolean opt_login = FALSE;
428 gboolean opt_nologin = FALSE;
429 gboolean opt_version = FALSE;
430 char *opt_config_dir_arg = NULL;
431 char *opt_login_arg = NULL;
432 char *opt_session_arg = NULL;
433 int dologin_ret = -1;
434 char *search_path;
435 GList *accounts;
436 #ifdef HAVE_SIGNAL_H
437 int sig_indx; /* for setting up signal catching */
438 sigset_t sigset;
439 RETSIGTYPE (*prev_sig_disp)(int);
440 char errmsg[BUFSIZ];
441 #ifndef DEBUG
442 char *segfault_message_tmp;
443 GError *error = NULL;
444 #endif
445 #endif
446 int opt;
447 gboolean gui_check;
448 gboolean debug_enabled;
449
450 struct option long_options[] = {
451 {"config", required_argument, NULL, 'c'},
452 {"debug", no_argument, NULL, 'd'},
453 {"help", no_argument, NULL, 'h'},
454 {"login", optional_argument, NULL, 'l'},
455 {"nologin", no_argument, NULL, 'n'},
456 {"session", required_argument, NULL, 's'},
457 {"version", no_argument, NULL, 'v'},
458 {0, 0, 0, 0}
459 };
460
461 #ifdef DEBUG
462 debug_enabled = TRUE;
463 #else
464 debug_enabled = FALSE;
465 #endif
466
467 #ifdef PURPLE_FATAL_ASSERTS
468 /* Make g_return_... functions fatal. */
469 g_log_set_always_fatal(G_LOG_LEVEL_CRITICAL);
470 #endif
471
472 #ifdef ENABLE_NLS
473 bindtextdomain(PACKAGE, LOCALEDIR);
474 bind_textdomain_codeset(PACKAGE, "UTF-8");
475 textdomain(PACKAGE);
476 #endif
477
478 #ifdef HAVE_SETLOCALE
479 /* Locale initialization is not complete here. See gtk_init_check() */
480 setlocale(LC_ALL, "");
481 #endif
482
483 #ifdef HAVE_SIGNAL_H
484
485 #ifndef DEBUG
486 /* We translate this here in case the crash breaks gettext. */
487 segfault_message_tmp = g_strdup_printf(_(
488 "%s has segfaulted and attempted to dump a core file.\n"
489 "This is a bug in the software and has happened through\n"
490 "no fault of your own.\n\n"
491 "If you can reproduce the crash, please notify the Pidgin\n"
492 "developers by reporting a bug at\n"
493 "%sbug.php\n\n"
494 "Please make sure to specify what you were doing at the time\n"
495 "and post the backtrace from the core file. If you do not know\n"
496 "how to get the backtrace, please read the instructions at\n"
497 "%sgdb.php\n\n"
498 "If you need further assistance, please IM either SeanEgn or \n"
499 "LSchiere (via AIM). Contact information for Sean and Luke \n"
500 "on other protocols is at\n"
501 "%scontactinfo.php\n"),
502 PIDGIN_NAME, PURPLE_WEBSITE, PURPLE_WEBSITE, PURPLE_WEBSITE
503 );
504
505 /* we have to convert the message (UTF-8 to console
506 charset) early because after a segmentation fault
507 it's not a good practice to allocate memory */
508 segfault_message = g_locale_from_utf8(segfault_message_tmp,
509 -1, NULL, NULL, &error);
510 if (segfault_message != NULL) {
511 g_free(segfault_message_tmp);
512 }
513 else {
514 /* use 'segfault_message_tmp' (UTF-8) as a fallback */
515 g_warning("%s\n", error->message);
516 g_error_free(error);
517 segfault_message = segfault_message_tmp;
518 }
519 #else
520 /* Don't mark this for translation. */
521 segfault_message = g_strdup(
522 "Hi, user. We need to talk.\n"
523 "I think something's gone wrong here. It's probably my fault.\n"
524 "No, really, it's not you... it's me... no no no, I think we get along well\n"
525 "it's just that.... well, I want to see other people. I... what?!? NO! I \n"
526 "haven't been cheating on you!! How many times do you want me to tell you?! And\n"
527 "for the last time, it's just a rash!\n"
528 );
529 #endif
530
531 /* Let's not violate any PLA's!!!! */
532 /* jseymour: whatever the fsck that means */
533 /* Robot101: for some reason things like gdm like to block *
534 * useful signals like SIGCHLD, so we unblock all the ones we *
535 * declare a handler for. thanks JSeymour and Vann. */
536 if (sigemptyset(&sigset)) {
537 snprintf(errmsg, BUFSIZ, "Warning: couldn't initialise empty signal set");
538 perror(errmsg);
539 }
540 for(sig_indx = 0; catch_sig_list[sig_indx] != -1; ++sig_indx) {
541 if((prev_sig_disp = signal(catch_sig_list[sig_indx], sighandler)) == SIG_ERR) {
542 snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d for catching",
543 catch_sig_list[sig_indx]);
544 perror(errmsg);
545 }
546 if(sigaddset(&sigset, catch_sig_list[sig_indx])) {
547 snprintf(errmsg, BUFSIZ, "Warning: couldn't include signal %d for unblocking",
548 catch_sig_list[sig_indx]);
549 perror(errmsg);
550 }
551 }
552 for(sig_indx = 0; ignore_sig_list[sig_indx] != -1; ++sig_indx) {
553 if((prev_sig_disp = signal(ignore_sig_list[sig_indx], SIG_IGN)) == SIG_ERR) {
554 snprintf(errmsg, BUFSIZ, "Warning: couldn't set signal %d to ignore",
555 ignore_sig_list[sig_indx]);
556 perror(errmsg);
557 }
558 }
559
560 if (sigprocmask(SIG_UNBLOCK, &sigset, NULL)) {
561 snprintf(errmsg, BUFSIZ, "Warning: couldn't unblock signals");
562 perror(errmsg);
563 }
564 #endif
565
566 /* scan command-line options */
567 opterr = 1;
568 while ((opt = getopt_long(argc, argv,
569 #ifndef _WIN32
570 "c:dhnl::s:v",
571 #else
572 "c:dhnl::v",
573 #endif
574 long_options, NULL)) != -1) {
575 switch (opt) {
576 case 'c': /* config dir */
577 g_free(opt_config_dir_arg);
578 opt_config_dir_arg = g_strdup(optarg);
579 break;
580 case 'd': /* debug */
581 debug_enabled = TRUE;
582 break;
583 case 'h': /* help */
584 opt_help = TRUE;
585 break;
586 case 'n': /* no autologin */
587 opt_nologin = TRUE;
588 break;
589 case 'l': /* login, option username */
590 opt_login = TRUE;
591 g_free(opt_login_arg);
592 if (optarg != NULL)
593 opt_login_arg = g_strdup(optarg);
594 break;
595 case 's': /* use existing session ID */
596 g_free(opt_session_arg);
597 opt_session_arg = g_strdup(optarg);
598 break;
599 case 'v': /* version */
600 opt_version = TRUE;
601 break;
602 case '?': /* show terse help */
603 default:
604 show_usage(argv[0], TRUE);
605 return 0;
606 break;
607 }
608 }
609
610 /* show help message */
611 if (opt_help) {
612 show_usage(argv[0], FALSE);
613 return 0;
614 }
615 /* show version message */
616 if (opt_version) {
617 printf(PIDGIN_NAME " %s\n", VERSION);
618 return 0;
619 }
620
621 /* set a user-specified config directory */
622 if (opt_config_dir_arg != NULL) {
623 purple_util_set_user_dir(opt_config_dir_arg);
624 }
625
626 /*
627 * We're done piddling around with command line arguments.
628 * Fire up this baby.
629 */
630
631 purple_debug_set_enabled(debug_enabled);
632
633
634 search_path = g_build_filename(purple_user_dir(), "gtkrc-2.0", NULL);
635 gtk_rc_add_default_file(search_path);
636 g_free(search_path);
637
638 gui_check = gtk_init_check(&argc, &argv);
639 if (!gui_check) {
640 char *display = gdk_get_display();
641
642 printf(PIDGIN_NAME " %s\n", VERSION);
643
644 g_warning("cannot open display: %s", display ? display : "unset");
645 g_free(display);
646
647 return 1;
648 }
649
650 #ifdef _WIN32
651 winpidgin_init(hint);
652 #endif
653
654 purple_core_set_ui_ops(pidgin_core_get_ui_ops());
655 purple_eventloop_set_ui_ops(pidgin_eventloop_get_ui_ops());
656
657 /*
658 * Set plugin search directories. Give priority to the plugins
659 * in user's home directory.
660 */
661 search_path = g_build_filename(purple_user_dir(), "plugins", NULL);
662 purple_plugins_add_search_path(search_path);
663 g_free(search_path);
664 purple_plugins_add_search_path(LIBDIR);
665
666 if (!purple_core_init(PIDGIN_UI)) {
667 fprintf(stderr,
668 "Initialization of the " PIDGIN_NAME " core failed. Dumping core.\n"
669 "Please report this!\n");
670 abort();
671 }
672
673 /* TODO: Move blist loading into purple_blist_init() */
674 purple_set_blist(purple_blist_new());
675 purple_blist_load();
676
677 /* TODO: Move prefs loading into purple_prefs_init() */
678 purple_prefs_load();
679 purple_prefs_update_old();
680 pidgin_prefs_update_old();
681
682 /* load plugins we had when we quit */
683 purple_plugins_load_saved(PIDGIN_PREFS_ROOT "/plugins/loaded");
684 pidgin_docklet_init();
685
686 /* TODO: Move pounces loading into purple_pounces_init() */
687 purple_pounces_load();
688
689
690 /* HACK BY SEANEGAN:
691 * We've renamed prpl-oscar to prpl-aim and prpl-icq, accordingly.
692 * Let's do that change right here... after everything's loaded, but
693 * before anything has happened
694 */
695 for (accounts = purple_accounts_get_all(); accounts != NULL; accounts = accounts->next) {
696 PurpleAccount *account = accounts->data;
697 if (!strcmp(purple_account_get_protocol_id(account), "prpl-oscar")) {
698 if (isdigit(*purple_account_get_username(account)))
699 purple_account_set_protocol_id(account, "prpl-icq");
700 else
701 purple_account_set_protocol_id(account, "prpl-aim");
702 }
703 }
704
705 ui_main();
706
707 #ifdef USE_SM
708 pidgin_session_init(argv[0], opt_session_arg, opt_config_dir_arg);
709 #endif
710 if (opt_session_arg != NULL) {
711 g_free(opt_session_arg);
712 opt_session_arg = NULL;
713 }
714 if (opt_config_dir_arg != NULL) {
715 g_free(opt_config_dir_arg);
716 opt_config_dir_arg = NULL;
717 }
718
719 /*
720 * We want to show the blist early in the init process so the
721 * user feels warm and fuzzy (not cold and prickley).
722 */
723 purple_blist_show();
724
725 if (purple_prefs_get_bool(PIDGIN_PREFS_ROOT "/debug/enabled"))
726 pidgin_debug_window_show();
727
728 if (opt_login) {
729 dologin_ret = dologin_named(opt_login_arg);
730 if (opt_login_arg != NULL) {
731 g_free(opt_login_arg);
732 opt_login_arg = NULL;
733 }
734 }
735
736 if (opt_nologin)
737 {
738 /* Set all accounts to "offline" */
739 PurpleSavedStatus *saved_status;
740
741 /* If we've used this type+message before, lookup the transient status */
742 saved_status = purple_savedstatus_find_transient_by_type_and_message(
743 PURPLE_STATUS_OFFLINE, NULL);
744
745 /* If this type+message is unique then create a new transient saved status */
746 if (saved_status == NULL)
747 saved_status = purple_savedstatus_new(NULL, PURPLE_STATUS_OFFLINE);
748
749 /* Set the status for each account */
750 purple_savedstatus_activate(saved_status);
751 }
752 else
753 {
754 /* Everything is good to go--sign on already */
755 if (!purple_prefs_get_bool("/core/savedstatus/startup_current_status"))
756 purple_savedstatus_activate(purple_savedstatus_get_startup());
757 purple_accounts_restore_current_statuses();
758 }
759
760 if ((accounts = purple_accounts_get_all_active()) == NULL)
761 {
762 pidgin_accounts_window_show();
763 }
764 else
765 {
766 g_list_free(accounts);
767 }
768
769 #ifdef HAVE_STARTUP_NOTIFICATION
770 startup_notification_complete();
771 #endif
772
773 #ifdef _WIN32
774 winpidgin_post_init();
775 #endif
776
777 gtk_main();
778
779 #ifdef HAVE_SIGNAL_H
780 g_free(segfault_message);
781 #endif
782
783 #ifdef _WIN32
784 winpidgin_cleanup();
785 #endif
786
787 return 0;
788 }

mercurial