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