added SIGBUS handler
[geeqie.git] / src / main.c
1 /*
2  * Geeqie
3  * (C) 2006 John Ellis
4  * Copyright (C) 2008 The Geeqie Team
5  *
6  * Author: John Ellis
7  *
8  * This software is released under the GNU General Public License (GNU GPL).
9  * Please read the included file COPYING for more information.
10  * This software comes with no warranty of any kind, use at your own risk!
11  */
12
13
14 #include "main.h"
15
16 #include "cache.h"
17 #include "collect.h"
18 #include "collect-io.h"
19 #include "filedata.h"
20 #include "filefilter.h"
21 #include "history_list.h"
22 #include "image-overlay.h"
23 #include "layout.h"
24 #include "layout_image.h"
25 #include "options.h"
26 #include "remote.h"
27 #include "secure_save.h"
28 #include "similar.h"
29 #include "ui_fileops.h"
30 #include "ui_utildlg.h"
31 #include "cache_maint.h"
32 #include "thumb.h"
33 #include "metadata.h"
34
35 #include <gdk/gdkkeysyms.h> /* for keyboard values */
36
37 #include <signal.h>
38 #include <sys/mman.h>
39
40 #include <math.h>
41 #ifdef G_OS_UNIX
42 #include <pwd.h>
43 #endif
44
45
46 static RemoteConnection *remote_connection = NULL;
47
48
49 /*
50  *-----------------------------------------------------------------------------
51  * keyboard functions
52  *-----------------------------------------------------------------------------
53  */
54
55 void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event)
56 {
57         static gint delta = 0;
58         static guint32 time_old = 0;
59         static guint keyval_old = 0;
60
61         if (event->state & GDK_CONTROL_MASK)
62                 {
63                 if (*x < 0) *x = G_MININT / 2;
64                 if (*x > 0) *x = G_MAXINT / 2;
65                 if (*y < 0) *y = G_MININT / 2;
66                 if (*y > 0) *y = G_MAXINT / 2;
67
68                 return;
69                 }
70
71         if (options->progressive_key_scrolling)
72                 {
73                 guint32 time_diff;
74
75                 time_diff = event->time - time_old;
76
77                 /* key pressed within 125ms ? (1/8 second) */
78                 if (time_diff > 125 || event->keyval != keyval_old) delta = 0;
79
80                 time_old = event->time;
81                 keyval_old = event->keyval;
82
83                 delta += 2;
84                 }
85         else
86                 {
87                 delta = 8;
88                 }
89
90         *x = *x * delta;
91         *y = *y * delta;
92 }
93
94
95
96 /*
97  *-----------------------------------------------------------------------------
98  * command line parser (private) hehe, who needs popt anyway?
99  *-----------------------------------------------------------------------------
100  */
101
102 static gint startup_blank = FALSE;
103 static gint startup_full_screen = FALSE;
104 static gint startup_in_slideshow = FALSE;
105 static gint startup_command_line_collection = FALSE;
106
107
108 static void parse_command_line_add_file(const gchar *file_path, gchar **path, gchar **file,
109                                         GList **list, GList **collection_list)
110 {
111         gchar *path_parsed;
112
113         path_parsed = g_strdup(file_path);
114         parse_out_relatives(path_parsed);
115
116         if (file_extension_match(path_parsed, GQ_COLLECTION_EXT))
117                 {
118                 *collection_list = g_list_append(*collection_list, path_parsed);
119                 }
120         else
121                 {
122                 if (!*path) *path = remove_level_from_path(path_parsed);
123                 if (!*file) *file = g_strdup(path_parsed);
124                 *list = g_list_prepend(*list, file_data_new_simple(path_parsed));
125                 }
126 }
127
128 static void parse_command_line_add_dir(const gchar *dir, gchar **path, gchar **file,
129                                        GList **list)
130 {
131         GList *files;
132         gchar *path_parsed;
133         FileData *dir_fd;
134
135         path_parsed = g_strdup(dir);
136         parse_out_relatives(path_parsed);
137         dir_fd = file_data_new_simple(path_parsed);
138         
139
140         if (filelist_read(dir_fd, &files, NULL))
141                 {
142                 GList *work;
143
144                 files = filelist_filter(files, FALSE);
145                 files = filelist_sort_path(files);
146
147                 work = files;
148                 while (work)
149                         {
150                         FileData *fd = work->data;
151                         if (!*path) *path = remove_level_from_path(fd->path);
152                         if (!*file) *file = g_strdup(fd->path);
153                         *list = g_list_prepend(*list, fd);
154
155                         work = work->next;
156                         }
157
158                 g_list_free(files);
159                 }
160
161         g_free(path_parsed);
162         file_data_unref(dir_fd);
163 }
164
165 static void parse_command_line_process_dir(const gchar *dir, gchar **path, gchar **file,
166                                            GList **list, gchar **first_dir)
167 {
168
169         if (!*list && !*first_dir)
170                 {
171                 *first_dir = g_strdup(dir);
172                 }
173         else
174                 {
175                 if (*first_dir)
176                         {
177                         parse_command_line_add_dir(*first_dir, path, file, list);
178                         g_free(*first_dir);
179                         *first_dir = NULL;
180                         }
181                 parse_command_line_add_dir(dir, path, file, list);
182                 }
183 }
184
185 static void parse_command_line_process_file(const gchar *file_path, gchar **path, gchar **file,
186                                             GList **list, GList **collection_list, gchar **first_dir)
187 {
188
189         if (*first_dir)
190                 {
191                 parse_command_line_add_dir(*first_dir, path, file, list);
192                 g_free(*first_dir);
193                 *first_dir = NULL;
194                 }
195         parse_command_line_add_file(file_path, path, file, list, collection_list);
196 }
197
198 static void parse_command_line(gint argc, gchar *argv[], gchar **path, gchar **file,
199                                GList **cmd_list, GList **collection_list,
200                                gchar **geometry)
201 {
202         GList *list = NULL;
203         GList *remote_list = NULL;
204         GList *remote_errors = NULL;
205         gint remote_do = FALSE;
206         gchar *first_dir = NULL;
207
208         if (argc > 1)
209                 {
210                 gint i;
211                 gchar *base_dir = get_current_dir();
212                 i = 1;
213                 while (i < argc)
214                         {
215                         const gchar *cmd_line = argv[i];
216                         gchar *cmd_all = g_build_filename(base_dir, cmd_line, NULL);
217
218                         if (cmd_line[0] == G_DIR_SEPARATOR && isdir(cmd_line))
219                                 {
220                                 parse_command_line_process_dir(cmd_line, path, file, &list, &first_dir);
221                                 }
222                         else if (isdir(cmd_all))
223                                 {
224                                 parse_command_line_process_dir(cmd_all, path, file, &list, &first_dir);
225                                 }
226                         else if (cmd_line[0] == G_DIR_SEPARATOR && isfile(cmd_line))
227                                 {
228                                 parse_command_line_process_file(cmd_line, path, file,
229                                                                 &list, collection_list, &first_dir);
230                                 }
231                         else if (isfile(cmd_all))
232                                 {
233                                 parse_command_line_process_file(cmd_all, path, file,
234                                                                 &list, collection_list, &first_dir);
235                                 }
236                         else if (strncmp(cmd_line, "--debug", 7) == 0 && (cmd_line[7] == '\0' || cmd_line[7] == '='))
237                                 {
238                                 /* do nothing but do not produce warnings */
239                                 }
240                         else if (strcmp(cmd_line, "+t") == 0 ||
241                                  strcmp(cmd_line, "--with-tools") == 0)
242                                 {
243                                 options->layout.tools_float = FALSE;
244                                 options->layout.tools_hidden = FALSE;
245
246                                 remote_list = g_list_append(remote_list, "+t");
247                                 }
248                         else if (strcmp(cmd_line, "-t") == 0 ||
249                                  strcmp(cmd_line, "--without-tools") == 0)
250                                 {
251                                 options->layout.tools_hidden = TRUE;
252
253                                 remote_list = g_list_append(remote_list, "-t");
254                                 }
255                         else if (strcmp(cmd_line, "-f") == 0 ||
256                                  strcmp(cmd_line, "--fullscreen") == 0)
257                                 {
258                                 startup_full_screen = TRUE;
259                                 }
260                         else if (strcmp(cmd_line, "-s") == 0 ||
261                                  strcmp(cmd_line, "--slideshow") == 0)
262                                 {
263                                 startup_in_slideshow = TRUE;
264                                 }
265                         else if (strcmp(cmd_line, "-l") == 0 ||
266                                  strcmp(cmd_line, "--list") == 0)
267                                 {
268                                 startup_command_line_collection = TRUE;
269                                 }
270                         else if (strncmp(cmd_line, "--geometry=", 11) == 0)
271                                 {
272                                 if (!*geometry) *geometry = g_strdup(cmd_line + 11);
273                                 }
274                         else if (strcmp(cmd_line, "-r") == 0 ||
275                                  strcmp(cmd_line, "--remote") == 0)
276                                 {
277                                 if (!remote_do)
278                                         {
279                                         remote_do = TRUE;
280                                         remote_list = remote_build_list(remote_list, argc - i, &argv[i], &remote_errors);
281                                         }
282                                 }
283                         else if (strcmp(cmd_line, "-rh") == 0 ||
284                                  strcmp(cmd_line, "--remote-help") == 0)
285                                 {
286                                 remote_help();
287                                 exit(0);
288                                 }
289                         else if (strcmp(cmd_line, "--blank") == 0)
290                                 {
291                                 startup_blank = TRUE;
292                                 }
293                         else if (strcmp(cmd_line, "-v") == 0 ||
294                                  strcmp(cmd_line, "--version") == 0)
295                                 {
296                                 printf_term("%s %s\n", GQ_APPNAME, VERSION);
297                                 exit(0);
298                                 }
299                         else if (strcmp(cmd_line, "--alternate") == 0)
300                                 {
301                                 /* enable faster experimental algorithm */
302                                 log_printf("Alternate similarity algorithm enabled\n");
303                                 image_sim_alternate_set(TRUE);
304                                 }
305                         else if (strcmp(cmd_line, "-h") == 0 ||
306                                  strcmp(cmd_line, "--help") == 0)
307                                 {
308                                 printf_term("%s %s\n", GQ_APPNAME, VERSION);
309                                 printf_term(_("Usage: %s [options] [path]\n\n"), GQ_APPNAME_LC);
310                                 print_term(_("valid options are:\n"));
311                                 print_term(_("  +t, --with-tools           force show of tools\n"));
312                                 print_term(_("  -t, --without-tools        force hide of tools\n"));
313                                 print_term(_("  -f, --fullscreen           start in full screen mode\n"));
314                                 print_term(_("  -s, --slideshow            start in slideshow mode\n"));
315                                 print_term(_("  -l, --list                 open collection window for command line\n"));
316                                 print_term(_("      --geometry=GEOMETRY    set main window location\n"));
317                                 print_term(_("  -r, --remote               send following commands to open window\n"));
318                                 print_term(_("  -rh,--remote-help          print remote command list\n"));
319 #ifdef DEBUG
320                                 print_term(_("  --debug[=level]            turn on debug output\n"));
321 #endif
322                                 print_term(_("  -v, --version              print version info\n"));
323                                 print_term(_("  -h, --help                 show this message\n\n"));
324
325 #if 0
326                                 /* these options are not officially supported!
327                                  * only for testing new features, no need to translate them */
328                                 print_term(  "  --alternate                use alternate similarity algorithm\n");
329 #endif
330
331                                 exit(0);
332                                 }
333                         else if (!remote_do)
334                                 {
335                                 printf_term(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line);
336                                 }
337
338                         g_free(cmd_all);
339                         i++;
340                         }
341                 g_free(base_dir);
342                 parse_out_relatives(*path);
343                 parse_out_relatives(*file);
344                 }
345
346         list = g_list_reverse(list);
347
348         if (!*path && first_dir)
349                 {
350                 *path = first_dir;
351                 first_dir = NULL;
352
353                 parse_out_relatives(*path);
354                 }
355         g_free(first_dir);
356
357         if (remote_do)
358                 {
359                 if (remote_errors)
360                         {
361                         GList *work = remote_errors;
362                         
363                         printf_term(_("Invalid or ignored remote options: "));
364                         while (work)
365                                 {
366                                 gchar *opt = work->data;
367                                                 
368                                 printf_term("%s%s", (work == remote_errors) ? "" : ", ", opt);
369                                 work = work->next;
370                                 }
371
372                         printf_term(_("\nUse --remote-help for valid remote options.\n"));
373                         }
374
375                 remote_control(argv[0], remote_list, *path, list, *collection_list);
376                 }
377         g_list_free(remote_list);
378
379         if (list && list->next)
380                 {
381                 *cmd_list = list;
382                 }
383         else
384                 {
385                 filelist_free(list);
386                 *cmd_list = NULL;
387                 }
388 }
389
390 static void parse_command_line_for_debug_option(gint argc, gchar *argv[])
391 {
392 #ifdef DEBUG
393         const gchar *debug_option = "--debug";
394         gint len = strlen(debug_option);
395
396         if (argc > 1)
397                 {
398                 gint i;
399
400                 for (i = 1; i < argc; i++)
401                         {
402                         const gchar *cmd_line = argv[i];
403                         if (strncmp(cmd_line, debug_option, len) == 0)
404                                 {
405                                 gint cmd_line_len = strlen(cmd_line);
406
407                                 /* we now increment the debug state for verbosity */
408                                 if (cmd_line_len == len)
409                                         debug_level_add(1);
410                                 else if (cmd_line[len] == '=' && g_ascii_isdigit(cmd_line[len+1]))
411                                         {
412                                         gint n = atoi(cmd_line + len + 1);
413                                         if (n < 0) n = 1;
414                                         debug_level_add(n);
415                                         }
416                                 }
417                         }
418                 }
419
420         DEBUG_1("debugging output enabled (level %d)", get_debug_level());
421 #endif
422 }
423
424 /*
425  *-----------------------------------------------------------------------------
426  * startup, init, and exit
427  *-----------------------------------------------------------------------------
428  */
429
430 #define RC_HISTORY_NAME "history"
431
432 static void keys_load(void)
433 {
434         gchar *path;
435
436         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
437         history_list_load(path);
438         g_free(path);
439 }
440
441 static void keys_save(void)
442 {
443         gchar *path;
444
445         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
446         history_list_save(path);
447         g_free(path);
448 }
449
450 static void mkdir_if_not_exists(const gchar *path)
451 {
452         if (isdir(path)) return;
453
454         log_printf(_("Creating %s dir:%s\n"), GQ_APPNAME, path);
455
456         if (!recursive_mkdir_if_not_exists(path, 0755))
457                 {
458                 log_printf(_("Could not create dir:%s\n"), path);
459                 }
460 }
461
462
463 /* We add to duplicate and modify  gtk_accel_map_print() and gtk_accel_map_save()
464  * to improve the reliability in special cases (especially when disk is full)
465  * These functions are now using secure saving stuff.
466  */
467 static void gq_accel_map_print(
468                     gpointer    data,
469                     const gchar *accel_path,
470                     guint       accel_key,
471                     GdkModifierType accel_mods,
472                     gboolean    changed)
473 {
474         GString *gstring = g_string_new(changed ? NULL : "; ");
475         SecureSaveInfo *ssi = data;
476         gchar *tmp, *name;
477
478         g_string_append(gstring, "(gtk_accel_path \"");
479
480         tmp = g_strescape(accel_path, NULL);
481         g_string_append(gstring, tmp);
482         g_free(tmp);
483
484         g_string_append(gstring, "\" \"");
485
486         name = gtk_accelerator_name(accel_key, accel_mods);
487         tmp = g_strescape(name, NULL);
488         g_free(name);
489         g_string_append(gstring, tmp);
490         g_free(tmp);
491
492         g_string_append(gstring, "\")\n");
493
494         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
495
496         g_string_free(gstring, TRUE);
497 }
498
499 static gboolean gq_accel_map_save(const gchar *path)
500 {
501         gchar *pathl;
502         SecureSaveInfo *ssi;
503         GString *gstring;
504
505         pathl = path_from_utf8(path);
506         ssi = secure_open(pathl);
507         g_free(pathl);
508         if (!ssi)
509                 {
510                 log_printf(_("error saving file: %s\n"), path);
511                 return FALSE;
512                 }
513         
514         gstring = g_string_new("; ");
515         if (g_get_prgname())
516                 g_string_append(gstring, g_get_prgname());
517         g_string_append(gstring, " GtkAccelMap rc-file         -*- scheme -*-\n");
518         g_string_append(gstring, "; this file is an automated accelerator map dump\n");
519         g_string_append(gstring, ";\n");
520
521         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
522
523         g_string_free(gstring, TRUE);
524
525         gtk_accel_map_foreach((gpointer) ssi, gq_accel_map_print);
526
527         if (secure_close(ssi))
528                 {
529                 log_printf(_("error saving file: %s\nerror: %s\n"), path,
530                            secsave_strerror(secsave_errno));
531                 return FALSE;
532                 }
533
534         return TRUE;
535 }
536
537 static gchar *accep_map_filename(void)
538 {
539         return g_build_filename(get_rc_dir(), "accels", NULL);
540 }
541
542 static void accel_map_save(void)
543 {
544         gchar *path;
545
546         path = accep_map_filename();
547         gq_accel_map_save(path);
548         g_free(path);
549 }
550
551 static void accel_map_load(void)
552 {
553         gchar *path;
554         gchar *pathl;
555
556         path = accep_map_filename();
557         pathl = path_from_utf8(path);
558         gtk_accel_map_load(pathl);
559         g_free(pathl);
560         g_free(path);
561 }
562
563 static void gtkrc_load(void)
564 {
565         gchar *path;
566         gchar *pathl;
567
568         /* If a gtkrc file exists in the rc directory, add it to the
569          * list of files to be parsed at the end of gtk_init() */
570         path = g_build_filename(get_rc_dir(), "gtkrc", NULL);
571         pathl = path_from_utf8(path);
572         if (access(pathl, R_OK) == 0)
573                 gtk_rc_add_default_file(pathl);
574         g_free(pathl);
575         g_free(path);
576 }
577
578 static void exit_program_final(void)
579 {
580         LayoutWindow *lw = NULL;
581
582         remote_close(remote_connection);
583
584         collect_manager_flush();
585
586         save_options(options);
587         keys_save();
588         accel_map_save();
589
590         if (layout_valid(&lw))
591                 {
592                 layout_free(lw);
593                 }
594
595         gtk_main_quit();
596 }
597
598 static GenericDialog *exit_dialog = NULL;
599
600 static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer data)
601 {
602         exit_dialog = NULL;
603         generic_dialog_close(gd);
604 }
605
606 static void exit_confirm_exit_cb(GenericDialog *gd, gpointer data)
607 {
608         exit_dialog = NULL;
609         generic_dialog_close(gd);
610         exit_program_final();
611 }
612
613 static gint exit_confirm_dlg(void)
614 {
615         GtkWidget *parent;
616         LayoutWindow *lw;
617         gchar *msg;
618
619         if (exit_dialog)
620                 {
621                 gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
622                 return TRUE;
623                 }
624
625         if (!collection_window_modified_exists()) return FALSE;
626
627         parent = NULL;
628         lw = NULL;
629         if (layout_valid(&lw))
630                 {
631                 parent = lw->window;
632                 }
633
634         msg = g_strdup_printf("%s - %s", GQ_APPNAME, _("exit"));
635         exit_dialog = generic_dialog_new(msg,
636                                 "exit", parent, FALSE,
637                                 exit_confirm_cancel_cb, NULL);
638         g_free(msg);
639         msg = g_strdup_printf(_("Quit %s"), GQ_APPNAME);
640         generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
641                                    msg, _("Collections have been modified. Quit anyway?"));
642         g_free(msg);
643         generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
644
645         gtk_widget_show(exit_dialog->dialog);
646
647         return TRUE;
648 }
649
650 static void exit_program_write_metadata_cb(gint success, const gchar *dest_path, gpointer data)
651 {
652         if (success) exit_program();
653 }
654
655 void exit_program(void)
656 {
657         layout_image_full_screen_stop(NULL);
658
659         if (metadata_write_queue_confirm(exit_program_write_metadata_cb, NULL)) return;
660
661         if (exit_confirm_dlg()) return;
662
663         exit_program_final();
664 }
665
666
667
668 /* This code is supposed to handle situation when a file mmaped by image_loader 
669  * or by exif loader is truncated by some other process.
670  * This is probably not completely correct according to posix, because
671  * mmap is not in the list of calls that can be used safely in signal handler,
672  * but anyway, the handler is called in situation when the application would
673  * crash otherwise.
674  * Ideas for improvement are welcome ;)
675  */
676 /* FIXME: this probably needs some better ifdefs. Please report any compilation problems */
677
678 #ifdef SIGBUS
679 static void sigbus_handler_cb(int signum, siginfo_t *info, void *context)
680 {
681         unsigned long pagesize = sysconf(_SC_PAGE_SIZE);
682         DEBUG_1("SIGBUS %p", info->si_addr);
683         mmap((void *)(((unsigned long)info->si_addr / pagesize) * pagesize), pagesize, PROT_READ | PROT_WRITE, MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
684 }
685 #endif
686
687 static void setup_sigbus_handler(void)
688 {
689 #ifdef SIGBUS
690         struct sigaction sigbus_action;
691         sigfillset(&sigbus_action.sa_mask);
692         sigbus_action.sa_sigaction = sigbus_handler_cb;
693         sigbus_action.sa_flags = SA_SIGINFO;
694
695         sigaction(SIGBUS, &sigbus_action, NULL);
696 #endif
697 }
698
699 gint main(gint argc, gchar *argv[])
700 {
701         LayoutWindow *lw;
702         gchar *path = NULL;
703         gchar *cmd_path = NULL;
704         gchar *cmd_file = NULL;
705         GList *cmd_list = NULL;
706         GList *collection_list = NULL;
707         CollectionData *first_collection = NULL;
708         gchar *geometry = NULL;
709         gchar *buf;
710         CollectionData *cd = NULL;
711
712 #ifdef HAVE_GTHREAD
713         g_thread_init (NULL);
714         gdk_threads_init();
715         gdk_threads_enter();
716 #endif
717         
718         /* init execution time counter (debug only) */
719         init_exec_time();
720
721         /* setup locale, i18n */
722         gtk_set_locale();
723
724 #ifdef ENABLE_NLS
725         bindtextdomain(PACKAGE, GQ_LOCALEDIR);
726         bind_textdomain_codeset(PACKAGE, "UTF-8");
727         textdomain(PACKAGE);
728 #endif
729
730         /* setup random seed for random slideshow */
731         srand(time(NULL));
732
733 #if 1
734         log_printf("%s %s, This is an alpha release.\n", GQ_APPNAME, VERSION);
735 #endif
736
737         setup_sigbus_handler();
738
739         /* register global notify functions */
740         file_data_register_notify_func(cache_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
741         file_data_register_notify_func(thumb_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
742         file_data_register_notify_func(collect_manager_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
743
744         parse_command_line_for_debug_option(argc, argv);
745
746         options = init_options(NULL);
747         setup_default_options(options);
748         load_options(options);
749
750         gtkrc_load();
751         gtk_init(&argc, &argv);
752
753         parse_command_line(argc, argv, &cmd_path, &cmd_file, &cmd_list, &collection_list, &geometry);
754
755         if (gtk_major_version < GTK_MAJOR_VERSION ||
756             (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
757                 {
758                 log_printf("!!! This is a friendly warning.\n");
759                 log_printf("!!! The version of GTK+ in use now is older than when %s was compiled.\n", GQ_APPNAME);
760                 log_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
761                 log_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
762                 log_printf("!!! %s may quit unexpectedly with a relocation error.\n", GQ_APPNAME);
763                 }
764
765         mkdir_if_not_exists(get_rc_dir());
766         mkdir_if_not_exists(get_collections_dir());
767         mkdir_if_not_exists(get_thumbnails_cache_dir());
768         mkdir_if_not_exists(get_metadata_cache_dir());
769
770         keys_load();
771         filter_add_defaults();
772         filter_rebuild();
773
774         accel_map_load();
775
776         if (startup_blank)
777                 {
778                 g_free(cmd_path);
779                 cmd_path = NULL;
780                 g_free(cmd_file);
781                 cmd_file = NULL;
782                 filelist_free(cmd_list);
783                 cmd_list = NULL;
784                 string_list_free(collection_list);
785                 collection_list = NULL;
786
787                 path = NULL;
788                 }
789         else if (cmd_path)
790                 {
791                 path = g_strdup(cmd_path);
792                 }
793         else if (options->startup.restore_path && options->startup.path && isdir(options->startup.path))
794                 {
795                 path = g_strdup(options->startup.path);
796                 }
797         else
798                 {
799                 path = get_current_dir();
800                 }
801
802         lw = layout_new_with_geometry(NULL, options->layout.tools_float, options->layout.tools_hidden, geometry);
803         layout_sort_set(lw, options->file_sort.method, options->file_sort.ascending);
804
805         if (collection_list && !startup_command_line_collection)
806                 {
807                 GList *work;
808
809                 work = collection_list;
810                 while (work)
811                         {
812                         CollectWindow *cw;
813                         const gchar *path;
814
815                         path = work->data;
816                         work = work->next;
817
818                         cw = collection_window_new(path);
819                         if (!first_collection && cw) first_collection = cw->cd;
820                         }
821                 }
822
823         if (cmd_list ||
824             (startup_command_line_collection && collection_list))
825                 {
826                 GList *work;
827
828                 if (startup_command_line_collection)
829                         {
830                         CollectWindow *cw;
831
832                         cw = collection_window_new("");
833                         cd = cw->cd;
834                         }
835                 else
836                         {
837                         cd = collection_new("");        /* if we pass NULL, untitled counter is falsely increm. */
838                         }
839
840                 g_free(cd->path);
841                 cd->path = NULL;
842                 g_free(cd->name);
843                 cd->name = g_strdup(_("Command line"));
844
845                 collection_path_changed(cd);
846
847                 work = cmd_list;
848                 while (work)
849                         {
850                         collection_add(cd, file_data_new_simple((gchar *)work->data), FALSE);
851                         work = work->next;
852                         }
853
854                 work = collection_list;
855                 while (work)
856                         {
857                         collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND);
858                         work = work->next;
859                         }
860
861                 layout_set_path(lw, path);
862                 if (cd->list) layout_image_set_collection(lw, cd, cd->list->data);
863
864                 /* mem leak, we never unref this collection when !startup_command_line_collection
865                  * (the image view of the main window does not hold a ref to the collection)
866                  * this is sort of unavoidable, for if it did hold a ref, next/back
867                  * may not work as expected when closing collection windows.
868                  *
869                  * collection_unref(cd);
870                  */
871
872                 }
873         else if (cmd_file)
874                 {
875                 layout_set_path(lw, cmd_file);
876                 }
877         else
878                 {
879                 layout_set_path(lw, path);
880                 if (first_collection)
881                         {
882                         layout_image_set_collection(lw, first_collection,
883                                                     collection_get_first(first_collection));
884                         }
885                 }
886
887         image_osd_set(lw->image, options->image_overlay.common.state | (options->image_overlay.common.show_at_startup ? OSD_SHOW_INFO : OSD_SHOW_NOTHING));
888
889         g_free(geometry);
890         g_free(cmd_path);
891         g_free(cmd_file);
892         filelist_free(cmd_list);
893         string_list_free(collection_list);
894         g_free(path);
895
896         if (startup_full_screen) layout_image_full_screen_start(lw);
897         if (startup_in_slideshow) layout_image_slideshow_start(lw);
898
899         buf = g_build_filename(get_rc_dir(), ".command", NULL);
900         remote_connection = remote_server_init(buf, cd);
901         g_free(buf);
902         
903         gtk_main();
904 #ifdef HAVE_GTHREAD
905         gdk_threads_leave();
906 #endif
907         return 0;
908 }
909 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */