9113612b01414cc6ff4fcd36766ee2eb404aac44
[geeqie.git] / src / main.cc
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 <signal.h>
23 #include <sys/mman.h>
24
25 #ifdef G_OS_UNIX
26 #include <pwd.h>
27 #endif
28 #include <locale.h>
29
30 #include "main.h"
31
32 #include "cache.h"
33 #include "collect.h"
34 #include "collect-io.h"
35 #include "filedata.h"
36 #include "filefilter.h"
37 #include "history-list.h"
38 #include "image.h"
39 #include "img-view.h"
40 #include "layout-image.h"
41 #include "layout-util.h"
42 #include "misc.h"
43 #include "rcfile.h"
44 #include "remote.h"
45 #include "secure-save.h"
46 #include "ui-fileops.h"
47 #include "ui-utildlg.h"
48 #include "cache-maint.h"
49 #include "thumb.h"
50 #include "metadata.h"
51 #include "exif.h"
52 #include "histogram.h"
53 #include "pixbuf-util.h"
54 #include "glua.h"
55 #include "whereami.h"
56
57 #ifdef HAVE_CLUTTER
58 #include <clutter-gtk/clutter-gtk.h>
59 #endif
60
61 #ifdef HAVE_EXECINFO_H
62 #include <execinfo.h>
63 #endif
64
65 gboolean thumb_format_changed = FALSE;
66 static RemoteConnection *remote_connection = NULL;
67
68 gchar *gq_prefix;
69 gchar *gq_localedir;
70 gchar *gq_helpdir;
71 gchar *gq_htmldir;
72 gchar *gq_appdir;
73 gchar *gq_bindir;
74 gchar *gq_executable_path;
75 gchar *desktop_file_template;
76 gchar *instance_identifier;
77
78 #if defined(SA_SIGINFO)
79 void sigsegv_handler_cb(int UNUSED(signo), siginfo_t *info, void *UNUSED(context))
80 {
81         gchar hex_char[16];
82         gint i;
83         guint64 addr;
84         guint64 char_index;
85 #ifdef HAVE_EXECINFO_H
86         gint bt_size;
87         void *bt[1024];
88 #endif
89
90         hex_char[0] = '0';
91         hex_char[1] = '1';
92         hex_char[2] = '2';
93         hex_char[3] = '3';
94         hex_char[4] = '4';
95         hex_char[5] = '5';
96         hex_char[6] = '6';
97         hex_char[7] = '7';
98         hex_char[8] = '8';
99         hex_char[9] = '9';
100         hex_char[10] = 'a';
101         hex_char[11] = 'b';
102         hex_char[12] = 'c';
103         hex_char[13] = 'd';
104         hex_char[14] = 'e';
105         hex_char[15] = 'f';
106
107         write(STDOUT_FILENO, "Geeqie fatal error\n", 19);
108         write(STDOUT_FILENO, "Signal: Segmentation fault\n", 27);
109
110         write(STDOUT_FILENO, "Code: ", 6);
111         write(STDOUT_FILENO,  (info->si_code == SEGV_MAPERR) ? "Address not mapped" : "Invalid permissions", strlen((info->si_code == SEGV_MAPERR) ? "Address not mapped" : "Invalid permissions"));
112         write(STDOUT_FILENO, "\n", 1);
113
114         write(STDOUT_FILENO, "Address: ", 9);
115
116         if (info->si_addr == 0)
117                 {
118                 write(STDOUT_FILENO, "0x0\n", 4);
119                 }
120         else
121                 {
122                 /* Assume the address is 64-bit */
123                 write(STDOUT_FILENO, "0x", 2);
124                 addr = info->si_addr;
125
126                 for (i = 0; i < 16; i++)
127                         {
128                         char_index = addr & 0xf000000000000000;
129                         char_index = char_index >> 60;
130                         addr = addr << 4;
131
132                         write(STDOUT_FILENO, &hex_char[char_index], 1);
133                         }
134                 write(STDOUT_FILENO, "\n", 1);
135                 }
136
137 #ifdef HAVE_EXECINFO_H
138         bt_size = backtrace(bt, 1024);
139         backtrace_symbols_fd(bt, bt_size, STDOUT_FILENO);
140 #endif
141
142         exit(EXIT_FAILURE);
143 }
144 #else /* defined(SA_SIGINFO) */
145 void sigsegv_handler_cb(int UNUSED(signo))
146 {
147 #ifdef HAVE_EXECINFO_H
148         gint bt_size;
149         void *bt[1024];
150 #endif
151
152         write(STDOUT_FILENO, "Geeqie fatal error\n", 19);
153         write(STDOUT_FILENO, "Signal: Segmentation fault\n", 27);
154
155 #ifdef HAVE_EXECINFO_H
156         bt_size = backtrace(bt, 1024);
157         backtrace_symbols_fd(bt, bt_size, STDOUT_FILENO);
158 #endif
159
160         exit(EXIT_FAILURE);
161 }
162 #endif /* defined(SA_SIGINFO) */
163
164 /*
165  *-----------------------------------------------------------------------------
166  * keyboard functions
167  *-----------------------------------------------------------------------------
168  */
169
170 void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event)
171 {
172         static gint delta = 0;
173         static guint32 time_old = 0;
174         static guint keyval_old = 0;
175
176         if (event->state & GDK_CONTROL_MASK)
177                 {
178                 if (*x < 0) *x = G_MININT / 2;
179                 if (*x > 0) *x = G_MAXINT / 2;
180                 if (*y < 0) *y = G_MININT / 2;
181                 if (*y > 0) *y = G_MAXINT / 2;
182
183                 return;
184                 }
185
186         if (options->progressive_key_scrolling)
187                 {
188                 guint32 time_diff;
189
190                 time_diff = event->time - time_old;
191
192                 /* key pressed within 125ms ? (1/8 second) */
193                 if (time_diff > 125 || event->keyval != keyval_old) delta = 0;
194
195                 time_old = event->time;
196                 keyval_old = event->keyval;
197
198                 delta += 2;
199                 }
200         else
201                 {
202                 delta = 8;
203                 }
204
205         *x = *x * delta * options->keyboard_scroll_step;
206         *y = *y * delta * options->keyboard_scroll_step;
207 }
208
209
210
211 /*
212  *-----------------------------------------------------------------------------
213  * command line parser (private) hehe, who needs popt anyway?
214  *-----------------------------------------------------------------------------
215  */
216
217 static void parse_command_line_add_file(const gchar *file_path, gchar **path, gchar **file,
218                                         GList **list, GList **collection_list)
219 {
220         gchar *path_parsed;
221
222         path_parsed = g_strdup(file_path);
223         parse_out_relatives(path_parsed);
224
225         if (file_extension_match(path_parsed, GQ_COLLECTION_EXT))
226                 {
227                 *collection_list = g_list_append(*collection_list, path_parsed);
228                 }
229         else
230                 {
231                 if (!*path) *path = remove_level_from_path(path_parsed);
232                 if (!*file) *file = g_strdup(path_parsed);
233                 *list = g_list_prepend(*list, path_parsed);
234                 }
235 }
236
237 static void parse_command_line_add_dir(const gchar *dir, gchar **UNUSED(path), gchar **UNUSED(file),
238                                        GList **UNUSED(list))
239 {
240 #if 0
241         /* This is broken because file filter is not initialized yet.
242         */
243         GList *files;
244         gchar *path_parsed;
245         FileData *dir_fd;
246
247         path_parsed = g_strdup(dir);
248         parse_out_relatives(path_parsed);
249         dir_fd = file_data_new_dir(path_parsed);
250
251
252         if (filelist_read(dir_fd, &files, NULL))
253                 {
254                 GList *work;
255
256                 files = filelist_filter(files, FALSE);
257                 files = filelist_sort_path(files);
258
259                 work = files;
260                 while (work)
261                         {
262                         FileData *fd = work->data;
263                         if (!*path) *path = remove_level_from_path(fd->path);
264                         if (!*file) *file = g_strdup(fd->path);
265                         *list = g_list_prepend(*list, fd);
266
267                         work = work->next;
268                         }
269
270                 g_list_free(files);
271                 }
272
273         g_free(path_parsed);
274         file_data_unref(dir_fd);
275 #else
276         DEBUG_1("multiple directories specified, ignoring: %s", dir);
277 #endif
278 }
279
280 static void parse_command_line_process_dir(const gchar *dir, gchar **path, gchar **file,
281                                            GList **list, gchar **first_dir)
282 {
283
284         if (!*list && !*first_dir)
285                 {
286                 *first_dir = g_strdup(dir);
287                 }
288         else
289                 {
290                 if (*first_dir)
291                         {
292                         parse_command_line_add_dir(*first_dir, path, file, list);
293                         g_free(*first_dir);
294                         *first_dir = NULL;
295                         }
296                 parse_command_line_add_dir(dir, path, file, list);
297                 }
298 }
299
300 static void parse_command_line_process_file(const gchar *file_path, gchar **path, gchar **file,
301                                             GList **list, GList **collection_list, gchar **first_dir)
302 {
303
304         if (*first_dir)
305                 {
306                 parse_command_line_add_dir(*first_dir, path, file, list);
307                 g_free(*first_dir);
308                 *first_dir = NULL;
309                 }
310         parse_command_line_add_file(file_path, path, file, list, collection_list);
311 }
312
313 static void parse_command_line(gint argc, gchar *argv[])
314 {
315         GList *list = NULL;
316         GList *remote_list = NULL;
317         GList *remote_errors = NULL;
318         gboolean remote_do = FALSE;
319         gchar *first_dir = NULL;
320         gchar *app_lock;
321         gchar *pwd;
322         gchar *current_dir;
323         gchar *geometry = NULL;
324         GtkWidget *dialog_warning;
325         GString *command_line_errors = g_string_new(NULL);
326
327         command_line = g_new0(CommandLine, 1);
328
329         command_line->argc = argc;
330         command_line->argv = argv;
331         command_line->regexp = NULL;
332
333         if (argc > 1)
334                 {
335                 gint i;
336                 gchar *base_dir = get_current_dir();
337                 i = 1;
338                 while (i < argc)
339                         {
340                         gchar *cmd_line = path_to_utf8(argv[i]);
341                         gchar *cmd_all = g_build_filename(base_dir, cmd_line, NULL);
342
343                         if (cmd_line[0] == G_DIR_SEPARATOR && isdir(cmd_line))
344                                 {
345                                 parse_command_line_process_dir(cmd_line, &command_line->path, &command_line->file, &list, &first_dir);
346                                 }
347                         else if (isdir(cmd_all))
348                                 {
349                                 parse_command_line_process_dir(cmd_all, &command_line->path, &command_line->file, &list, &first_dir);
350                                 }
351                         else if (cmd_line[0] == G_DIR_SEPARATOR && isfile(cmd_line))
352                                 {
353                                 parse_command_line_process_file(cmd_line, &command_line->path, &command_line->file,
354                                                                 &list, &command_line->collection_list, &first_dir);
355                                 }
356                         else if (isfile(cmd_all))
357                                 {
358                                 parse_command_line_process_file(cmd_all, &command_line->path, &command_line->file,
359                                                                 &list, &command_line->collection_list, &first_dir);
360                                 }
361                         else if (download_web_file(cmd_line, FALSE, NULL))
362                                 {
363                                 }
364                         else if (is_collection(cmd_line))
365                                 {
366                                 gchar *path = NULL;
367
368                                 path = collection_path(cmd_line);
369                                 parse_command_line_process_file(path, &command_line->path, &command_line->file,
370                                                                 &list, &command_line->collection_list, &first_dir);
371                                 g_free(path);
372                                 }
373                         else if (strncmp(cmd_line, "--debug", 7) == 0 && (cmd_line[7] == '\0' || cmd_line[7] == '='))
374                                 {
375                                 /* do nothing but do not produce warnings */
376                                 }
377                         else if (strncmp(cmd_line, "--disable-clutter", 17) == 0 && (cmd_line[17] == '\0'))
378                                 {
379                                 /* do nothing but do not produce warnings */
380                                 }
381                         else if (strcmp(cmd_line, "+t") == 0 ||
382                                  strcmp(cmd_line, "--with-tools") == 0)
383                                 {
384                                 command_line->tools_show = TRUE;
385
386                                 remote_list = g_list_append(remote_list, "+t");
387                                 }
388                         else if (strcmp(cmd_line, "-t") == 0 ||
389                                  strcmp(cmd_line, "--without-tools") == 0)
390                                 {
391                                 command_line->tools_hide = TRUE;
392
393                                 remote_list = g_list_append(remote_list, "-t");
394                                 }
395                         else if (strcmp(cmd_line, "-f") == 0 ||
396                                  strcmp(cmd_line, "--fullscreen") == 0)
397                                 {
398                                 command_line->startup_full_screen = TRUE;
399                                 }
400                         else if (strcmp(cmd_line, "-s") == 0 ||
401                                  strcmp(cmd_line, "--slideshow") == 0)
402                                 {
403                                 command_line->startup_in_slideshow = TRUE;
404                                 }
405                         else if (strcmp(cmd_line, "-l") == 0 ||
406                                  strcmp(cmd_line, "--list") == 0)
407                                 {
408                                 command_line->startup_command_line_collection = TRUE;
409                                 }
410                         else if (strncmp(cmd_line, "--geometry=", 11) == 0)
411                                 {
412                                 if (!command_line->geometry) command_line->geometry = g_strdup(cmd_line + 11);
413                                 }
414                         else if (strcmp(cmd_line, "-r") == 0 ||
415                                  strcmp(cmd_line, "--remote") == 0)
416                                 {
417                                 if (!remote_do)
418                                         {
419                                         remote_do = TRUE;
420                                         remote_list = remote_build_list(remote_list, argc - i, &argv[i], &remote_errors);
421                                         }
422                                 }
423                         else if ((strcmp(cmd_line, "+w") == 0) ||
424                                                 strcmp(cmd_line, "--show-log-window") == 0)
425                                 {
426                                 command_line->log_window_show = TRUE;
427                                 }
428                         else if (strncmp(cmd_line, "-o:", 3) == 0)
429                                 {
430                                 command_line->log_file = g_strdup(cmd_line + 3);
431                                 }
432                         else if (strncmp(cmd_line, "--log-file:", 11) == 0)
433                                 {
434                                 command_line->log_file = g_strdup(cmd_line + 11);
435                                 }
436                         else if (strncmp(cmd_line, "-g:", 3) == 0)
437                                 {
438                                 set_regexp(g_strdup(cmd_line+3));
439                                 }
440                         else if (strncmp(cmd_line, "-grep:", 6) == 0)
441                                 {
442                                 set_regexp(g_strdup(cmd_line+3));
443                                 }
444                         else if (strncmp(cmd_line, "-n", 2) == 0)
445                                 {
446                                 command_line->new_instance = TRUE;
447                                 }
448                         else if (strncmp(cmd_line, "--new-instance", 14) == 0)
449                                 {
450                                 command_line->new_instance = TRUE;
451                                 }
452                         else if (strcmp(cmd_line, "-rh") == 0 ||
453                                  strcmp(cmd_line, "--remote-help") == 0)
454                                 {
455                                 remote_help();
456                                 exit(0);
457                                 }
458                         else if (strcmp(cmd_line, "--blank") == 0)
459                                 {
460                                 command_line->startup_blank = TRUE;
461                                 }
462                         else if (strcmp(cmd_line, "-v") == 0 ||
463                                  strcmp(cmd_line, "--version") == 0)
464                                 {
465                                 printf_term(FALSE, "%s %s GTK%d\n", GQ_APPNAME, VERSION, gtk_major_version);
466                                 exit(0);
467                                 }
468                         else if (strcmp(cmd_line, "--alternate") == 0)
469                                 {
470                                 /* enable faster experimental algorithm */
471                                 log_printf("Alternate similarity algorithm enabled\n");
472                                 image_sim_alternate_set(TRUE);
473                                 }
474                         else if (strcmp(cmd_line, "-h") == 0 ||
475                                  strcmp(cmd_line, "--help") == 0)
476                                 {
477                                 printf_term(FALSE, "%s %s\n", GQ_APPNAME, VERSION);
478                                 printf_term(FALSE, _("Usage: %s [options] [path]\n\n"), GQ_APPNAME_LC);
479                                 print_term(FALSE, _("Valid options:\n"));
480                                 print_term(FALSE, _("      --blank                      start with blank file list\n"));
481                                 print_term(FALSE, _("      --cache-maintenance <path>   run cache maintenance in non-GUI mode\n"));
482                                 print_term(FALSE, _("      --disable-clutter            disable use of Clutter library (i.e. GPU accel.)\n"));
483                                 print_term(FALSE, _("  -f, --fullscreen                 start in full screen mode\n"));
484                                 print_term(FALSE, _("      --geometry=WxH+XOFF+YOFF     set main window location\n"));
485                                 print_term(FALSE, _("  -h, --help                       show this message\n"));
486                                 print_term(FALSE, _("  -l, --list [files] [collections] open collection window for command line\n"));
487                                 print_term(FALSE, _("  -n, --new-instance               open a new instance of Geeqie\n"));
488                                 print_term(FALSE, _("  -o:, --log-file:<file>     save log data to file\n"));
489                                 print_term(FALSE, _("  -r, --remote                     send following commands to open window\n"));
490                                 print_term(FALSE, _("  -rh, --remote-help               print remote command list\n"));
491                                 print_term(FALSE, _("  -s, --slideshow                  start in slideshow mode\n"));
492                                 print_term(FALSE, _("  +t, --with-tools                 force show of tools\n"));
493                                 print_term(FALSE, _("  -t, --without-tools              force hide of tools\n"));
494                                 print_term(FALSE, _("  -v, --version                    print version info\n"));
495                                 print_term(FALSE, _("  +w, --show-log-window            show log window\n"));
496 #ifdef DEBUG
497                                 print_term(FALSE, _("      --debug[=level]              turn on debug output\n"));
498                                 print_term(FALSE, _("  -g:, --grep:<regexp>     filter debug output\n"));
499 #endif
500
501 #if 0
502                                 /* these options are not officially supported!
503                                  * only for testing new features, no need to translate them */
504                                 print_term(FALSE, "  --alternate                use alternate similarity algorithm\n");
505 #endif
506                                 print_term(FALSE, "\n");
507
508                                 remote_help();
509
510
511                                 exit(0);
512                                 }
513                         else if (!remote_do)
514                                 {
515                                 command_line_errors = g_string_append(command_line_errors, cmd_line);
516                                 command_line_errors = g_string_append(command_line_errors, "\n");
517                                 }
518
519                         g_free(cmd_all);
520                         g_free(cmd_line);
521                         i++;
522                         }
523
524                 if (command_line_errors->len > 0)
525                         {
526                         dialog_warning = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "Invalid parameter(s):");
527                         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_warning), "%s", command_line_errors->str);
528                         gtk_window_set_title(GTK_WINDOW(dialog_warning), GQ_APPNAME);
529                         gtk_window_set_keep_above(GTK_WINDOW(dialog_warning), TRUE);
530                         gtk_dialog_run(GTK_DIALOG(dialog_warning));
531                         gtk_widget_destroy(dialog_warning);
532                         g_string_free(command_line_errors, TRUE);
533
534                         exit(EXIT_FAILURE);
535                         }
536
537                 g_free(base_dir);
538                 parse_out_relatives(command_line->path);
539                 parse_out_relatives(command_line->file);
540                 }
541
542         list = g_list_reverse(list);
543
544         if (!command_line->path && first_dir)
545                 {
546                 command_line->path = first_dir;
547                 first_dir = NULL;
548
549                 parse_out_relatives(command_line->path);
550                 }
551         g_free(first_dir);
552
553         if (!command_line->new_instance)
554                 {
555                 /* If Geeqie is already running, prevent a second instance
556                  * from being started. Open a new window instead.
557                  */
558                 app_lock = g_build_filename(get_rc_dir(), ".command", NULL);
559                 if (remote_server_exists(app_lock) && !remote_do)
560                         {
561                         remote_do = TRUE;
562                         if (command_line->geometry)
563                                 {
564                                 geometry = g_strdup_printf("--geometry=%s", command_line->geometry);
565                                 remote_list = g_list_prepend(remote_list, geometry);
566                                 }
567                         remote_list = g_list_prepend(remote_list, "--new-window");
568                         }
569                 g_free(app_lock);
570                 }
571
572         if (remote_do)
573                 {
574                 if (remote_errors)
575                         {
576                         GList *work = remote_errors;
577
578                         while (work)
579                                 {
580                                 gchar *opt = work->data;
581
582                                 command_line_errors = g_string_append(command_line_errors, opt);
583                                 command_line_errors = g_string_append(command_line_errors, "\n");
584                                 work = work->next;
585                                 }
586
587                         dialog_warning = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "Invalid parameter(s):");
588                         gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_warning), "%s", command_line_errors->str);
589                         gtk_window_set_title(GTK_WINDOW(dialog_warning), GQ_APPNAME);
590                         gtk_window_set_keep_above(GTK_WINDOW(dialog_warning), TRUE);
591                         gtk_dialog_run(GTK_DIALOG(dialog_warning));
592                         gtk_widget_destroy(dialog_warning);
593                         g_string_free(command_line_errors, TRUE);
594
595                         exit(EXIT_FAILURE);
596                         }
597
598                 /* prepend the current dir the remote command was made from,
599                  * for use by any remote command that needs it
600                  */
601                 current_dir = g_get_current_dir();
602                 pwd = g_strconcat("--PWD:", current_dir, NULL);
603                 remote_list = g_list_prepend(remote_list, pwd);
604
605                 remote_control(argv[0], remote_list, command_line->path, list, command_line->collection_list);
606                 /* There is no return to this point
607                  */
608                 g_free(pwd);
609                 g_free(current_dir);
610                 }
611         g_free(geometry);
612         g_list_free(remote_list);
613
614         if (list && list->next)
615                 {
616                 command_line->cmd_list = list;
617                 }
618         else
619                 {
620                 string_list_free(list);
621                 command_line->cmd_list = NULL;
622                 }
623
624         if (command_line->startup_blank)
625                 {
626                 g_free(command_line->path);
627                 command_line->path = NULL;
628                 g_free(command_line->file);
629                 command_line->file = NULL;
630                 filelist_free(command_line->cmd_list);
631                 command_line->cmd_list = NULL;
632                 string_list_free(command_line->collection_list);
633                 command_line->collection_list = NULL;
634                 }
635 }
636
637 static void parse_command_line_for_debug_option(gint argc, gchar *argv[])
638 {
639 #ifdef DEBUG
640         const gchar *debug_option = "--debug";
641         gint len = strlen(debug_option);
642
643         if (argc > 1)
644                 {
645                 gint i;
646
647                 for (i = 1; i < argc; i++)
648                         {
649                         const gchar *cmd_line = argv[i];
650                         if (strncmp(cmd_line, debug_option, len) == 0)
651                                 {
652                                 gint cmd_line_len = strlen(cmd_line);
653
654                                 /* we now increment the debug state for verbosity */
655                                 if (cmd_line_len == len)
656                                         debug_level_add(1);
657                                 else if (cmd_line[len] == '=' && g_ascii_isdigit(cmd_line[len+1]))
658                                         {
659                                         gint n = atoi(cmd_line + len + 1);
660                                         if (n < 0) n = 1;
661                                         debug_level_add(n);
662                                         }
663                                 }
664                         }
665                 }
666
667         DEBUG_1("debugging output enabled (level %d)", get_debug_level());
668 #endif
669 }
670
671 #ifdef HAVE_CLUTTER
672 static gboolean parse_command_line_for_clutter_option(gint argc, gchar *argv[])
673 {
674         const gchar *clutter_option = "--disable-clutter";
675         gint len = strlen(clutter_option);
676         gboolean ret = FALSE;
677
678         if (argc > 1)
679                 {
680                 gint i;
681
682                 for (i = 1; i < argc; i++)
683                         {
684                         const gchar *cmd_line = argv[i];
685                         if (strncmp(cmd_line, clutter_option, len) == 0)
686                                 {
687                                 ret = TRUE;
688                                 }
689                         }
690                 }
691
692         return ret;
693 }
694 #endif
695
696 static gboolean parse_command_line_for_cache_maintenance_option(gint argc, gchar *argv[])
697 {
698         const gchar *cache_maintenance_option = "--cache-maintenance";
699         gint len = strlen(cache_maintenance_option);
700         gboolean ret = FALSE;
701
702         if (argc >= 2)
703                 {
704                 const gchar *cmd_line = argv[1];
705                 if (strncmp(cmd_line, cache_maintenance_option, len) == 0)
706                         {
707                         ret = TRUE;
708                         }
709                 }
710
711         return ret;
712 }
713
714 static void process_command_line_for_cache_maintenance_option(gint argc, gchar *argv[])
715 {
716         gchar *rc_path;
717         gchar *folder_path = NULL;
718         gsize size;
719         gsize i = 0;
720         gchar *buf_config_file;
721         gint diff_count;
722
723         if (argc >= 3)
724                 {
725                 folder_path = expand_tilde(argv[2]);
726
727                 if (isdir(folder_path))
728                         {
729                         rc_path = g_build_filename(get_rc_dir(), RC_FILE_NAME, NULL);
730
731                         if (isfile(rc_path))
732                                 {
733                                 if (g_file_get_contents(rc_path, &buf_config_file, &size, NULL))
734                                         {
735                                         while (i < size)
736                                                 {
737                                                 diff_count = strncmp("</global>", &buf_config_file[i], 9);
738                                                 if (diff_count == 0)
739                                                         {
740                                                         break;
741                                                         }
742                                                 i++;
743                                                 }
744                                         /* Load only the <global> section */
745                                         load_config_from_buf(buf_config_file, i + 9, FALSE);
746
747                                         if (options->thumbnails.enable_caching)
748                                                 {
749                                                 cache_maintenance(folder_path);
750                                                 }
751                                         else
752                                                 {
753                                                 print_term(TRUE, "Caching not enabled\n");
754                                                 exit(EXIT_FAILURE);
755                                                 }
756                                         g_free(buf_config_file);
757                                         }
758                                 else
759                                         {
760                                         print_term(TRUE, g_strconcat(_("Cannot load "), rc_path, "\n", NULL));
761                                         exit(EXIT_FAILURE);
762                                         }
763                                 }
764                         else
765                                 {
766                                 print_term(TRUE, g_strconcat(_("Configuration file path "), rc_path, _(" is not a file\n"), NULL));
767                                 exit(EXIT_FAILURE);
768                                 }
769                         g_free(rc_path);
770                         }
771                 else
772                         {
773                         print_term(TRUE, g_strconcat(argv[2], _(" is not a folder\n"), NULL));
774                         exit(EXIT_FAILURE);
775                         }
776                 g_free(folder_path);
777                 }
778         else
779                 {
780                 print_term(TRUE, _("No path parameter given\n"));
781                 exit(EXIT_FAILURE);
782                 }
783 }
784
785 /*
786  *-----------------------------------------------------------------------------
787  * startup, init, and exit
788  *-----------------------------------------------------------------------------
789  */
790
791 #define RC_HISTORY_NAME "history"
792 #define RC_MARKS_NAME "marks"
793
794 static void setup_env_path(void)
795 {
796         const gchar *old_path = g_getenv("PATH");
797         gchar *path = g_strconcat(gq_bindir, ":", old_path, NULL);
798         g_setenv("PATH", path, TRUE);
799         g_free(path);
800 }
801
802 static void keys_load(void)
803 {
804         gchar *path;
805
806         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
807         history_list_load(path);
808         g_free(path);
809 }
810
811 static void keys_save(void)
812 {
813         gchar *path;
814
815         path = g_build_filename(get_rc_dir(), RC_HISTORY_NAME, NULL);
816         history_list_save(path);
817         g_free(path);
818 }
819
820 static void marks_load(void)
821 {
822         gchar *path;
823
824         path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL);
825         marks_list_load(path);
826         g_free(path);
827 }
828
829 static void marks_save(gboolean save)
830 {
831         gchar *path;
832
833         path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL);
834         marks_list_save(path, save);
835         g_free(path);
836 }
837
838 static void mkdir_if_not_exists(const gchar *path)
839 {
840         if (isdir(path)) return;
841
842         log_printf(_("Creating %s dir:%s\n"), GQ_APPNAME, path);
843
844         if (!recursive_mkdir_if_not_exists(path, 0755))
845                 {
846                 log_printf(_("Could not create dir:%s\n"), path);
847                 }
848 }
849
850
851 /* We add to duplicate and modify  gtk_accel_map_print() and gtk_accel_map_save()
852  * to improve the reliability in special cases (especially when disk is full)
853  * These functions are now using secure saving stuff.
854  */
855 static void gq_accel_map_print(
856                     gpointer    data,
857                     const gchar *accel_path,
858                     guint       accel_key,
859                     GdkModifierType accel_mods,
860                     gboolean    changed)
861 {
862         GString *gstring = g_string_new(changed ? NULL : "; ");
863         SecureSaveInfo *ssi = data;
864         gchar *tmp, *name;
865
866         g_string_append(gstring, "(gtk_accel_path \"");
867
868         tmp = g_strescape(accel_path, NULL);
869         g_string_append(gstring, tmp);
870         g_free(tmp);
871
872         g_string_append(gstring, "\" \"");
873
874         name = gtk_accelerator_name(accel_key, accel_mods);
875         tmp = g_strescape(name, NULL);
876         g_free(name);
877         g_string_append(gstring, tmp);
878         g_free(tmp);
879
880         g_string_append(gstring, "\")\n");
881
882         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
883
884         g_string_free(gstring, TRUE);
885 }
886
887 static gboolean gq_accel_map_save(const gchar *path)
888 {
889         gchar *pathl;
890         SecureSaveInfo *ssi;
891         GString *gstring;
892
893         pathl = path_from_utf8(path);
894         ssi = secure_open(pathl);
895         g_free(pathl);
896         if (!ssi)
897                 {
898                 log_printf(_("error saving file: %s\n"), path);
899                 return FALSE;
900                 }
901
902         gstring = g_string_new("; ");
903         if (g_get_prgname())
904                 g_string_append(gstring, g_get_prgname());
905         g_string_append(gstring, " GtkAccelMap rc-file         -*- scheme -*-\n");
906         g_string_append(gstring, "; this file is an automated accelerator map dump\n");
907         g_string_append(gstring, ";\n");
908
909         secure_fwrite(gstring->str, sizeof(*gstring->str), gstring->len, ssi);
910
911         g_string_free(gstring, TRUE);
912
913         gtk_accel_map_foreach((gpointer) ssi, gq_accel_map_print);
914
915         if (secure_close(ssi))
916                 {
917                 log_printf(_("error saving file: %s\nerror: %s\n"), path,
918                            secsave_strerror(secsave_errno));
919                 return FALSE;
920                 }
921
922         return TRUE;
923 }
924
925 static gchar *accep_map_filename(void)
926 {
927         return g_build_filename(get_rc_dir(), "accels", NULL);
928 }
929
930 static void accel_map_save(void)
931 {
932         gchar *path;
933
934         path = accep_map_filename();
935         gq_accel_map_save(path);
936         g_free(path);
937 }
938
939 static void accel_map_load(void)
940 {
941         gchar *path;
942         gchar *pathl;
943
944         path = accep_map_filename();
945         pathl = path_from_utf8(path);
946         gtk_accel_map_load(pathl);
947         g_free(pathl);
948         g_free(path);
949 }
950
951 static void gtkrc_load(void)
952 {
953         gchar *path;
954         gchar *pathl;
955
956         /* If a gtkrc file exists in the rc directory, add it to the
957          * list of files to be parsed at the end of gtk_init() */
958         path = g_build_filename(get_rc_dir(), "gtkrc", NULL);
959         pathl = path_from_utf8(path);
960         if (access(pathl, R_OK) == 0)
961                 gtk_rc_add_default_file(pathl);
962         g_free(pathl);
963         g_free(path);
964 }
965
966 static void exit_program_final(void)
967 {
968         LayoutWindow *lw = NULL;
969         GList *list;
970         LayoutWindow *tmp_lw;
971         gchar *archive_dir;
972         GFile *archive_file;
973
974          /* make sure that external editors are loaded, we would save incomplete configuration otherwise */
975         layout_editors_reload_finish();
976
977         remote_close(remote_connection);
978
979         collect_manager_flush();
980
981         /* Save the named windows */
982         if (layout_window_list && layout_window_list->next)
983                 {
984                 list = layout_window_list;
985                 while (list)
986                         {
987                         tmp_lw = list->data;
988                         if (!g_str_has_prefix(tmp_lw->options.id, "lw"))
989                                 {
990                                 save_layout(list->data);
991                                 }
992                         list = list->next;
993                         }
994                 }
995
996         save_options(options);
997         keys_save();
998         accel_map_save();
999
1000         if (layout_valid(&lw))
1001                 {
1002                 layout_free(lw);
1003                 }
1004
1005         /* Delete any files/folders in /tmp that have been created by the open archive function */
1006         archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, instance_identifier, NULL);
1007         if (isdir(archive_dir))
1008                 {
1009                 archive_file = g_file_new_for_path(archive_dir);
1010                 rmdir_recursive(archive_file, NULL, NULL);
1011                 g_free(archive_dir);
1012                 g_object_unref(archive_file);
1013                 }
1014
1015         /* If there are still sub-dirs created by another instance, this will fail
1016          * but that does not matter */
1017         archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, NULL);
1018         if (isdir(archive_dir))
1019                 {
1020                 archive_file = g_file_new_for_path(archive_dir);
1021                 g_file_delete(archive_file, NULL, NULL);
1022                 g_free(archive_dir);
1023                 g_object_unref(archive_file);
1024                 }
1025
1026         secure_close(command_line->ssi);
1027
1028         gtk_main_quit();
1029 }
1030
1031 static GenericDialog *exit_dialog = NULL;
1032
1033 static void exit_confirm_cancel_cb(GenericDialog *gd, gpointer UNUSED(data))
1034 {
1035         exit_dialog = NULL;
1036         generic_dialog_close(gd);
1037 }
1038
1039 static void exit_confirm_exit_cb(GenericDialog *gd, gpointer UNUSED(data))
1040 {
1041         exit_dialog = NULL;
1042         generic_dialog_close(gd);
1043         exit_program_final();
1044 }
1045
1046 static gint exit_confirm_dlg(void)
1047 {
1048         GtkWidget *parent;
1049         LayoutWindow *lw;
1050         gchar *msg;
1051
1052         if (exit_dialog)
1053                 {
1054                 gtk_window_present(GTK_WINDOW(exit_dialog->dialog));
1055                 return TRUE;
1056                 }
1057
1058         if (!collection_window_modified_exists()) return FALSE;
1059
1060         parent = NULL;
1061         lw = NULL;
1062         if (layout_valid(&lw))
1063                 {
1064                 parent = lw->window;
1065                 }
1066
1067         msg = g_strdup_printf("%s - %s", GQ_APPNAME, _("exit"));
1068         exit_dialog = generic_dialog_new(msg,
1069                                 "exit", parent, FALSE,
1070                                 exit_confirm_cancel_cb, NULL);
1071         g_free(msg);
1072         msg = g_strdup_printf(_("Quit %s"), GQ_APPNAME);
1073         generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION,
1074                                    msg, _("Collections have been modified. Quit anyway?"), TRUE);
1075         g_free(msg);
1076         generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE);
1077
1078         gtk_widget_show(exit_dialog->dialog);
1079
1080         return TRUE;
1081 }
1082
1083 static void exit_program_write_metadata_cb(gint success, const gchar *UNUSED(dest_path), gpointer UNUSED(data))
1084 {
1085         if (success) exit_program();
1086 }
1087
1088 void exit_program(void)
1089 {
1090         layout_image_full_screen_stop(NULL);
1091
1092         if (metadata_write_queue_confirm(FALSE, exit_program_write_metadata_cb, NULL)) return;
1093
1094         options->marks_save ? marks_save(TRUE) : marks_save(FALSE);
1095
1096         if (exit_confirm_dlg()) return;
1097
1098         exit_program_final();
1099 }
1100
1101 /* This code attempts to handle situation when a file mmaped by image_loader
1102  * or by exif loader is truncated by some other process.
1103  * This code is incorrect according to POSIX, because:
1104  *
1105  *   mmap is not async-signal-safe and thus may not be called from a signal handler
1106  * 
1107  *   mmap must be called with a valid file descriptor.  POSIX requires that
1108  *   a fildes argument of -1 must cause mmap to return EBADF.
1109  *
1110  * See https://github.com/BestImageViewer/geeqie/issues/1052 for discussion of
1111  * an alternative approach.
1112  */
1113 /** @FIXME this probably needs some better ifdefs. Please report any compilation problems */
1114
1115 #if defined(SIGBUS) && defined(SA_SIGINFO)
1116 static void sigbus_handler_cb(int UNUSED(signum), siginfo_t *info, void *UNUSED(context))
1117 {
1118         /*
1119          * @FIXME Design and implement a POSIX-acceptable approach,
1120          * after first documenting the sitations where SIGBUS occurs.
1121          * See https://github.com/BestImageViewer/geeqie/issues/1052 for discussion
1122          */
1123
1124         DEBUG_1("SIGBUS %p NOT HANDLED", info->si_addr);
1125         exit(EXIT_FAILURE);
1126 }
1127 #endif
1128
1129 static void setup_sigbus_handler(void)
1130 {
1131 #if defined(SIGBUS) && defined(SA_SIGINFO)
1132         struct sigaction sigbus_action;
1133         sigfillset(&sigbus_action.sa_mask);
1134         sigbus_action.sa_sigaction = sigbus_handler_cb;
1135         sigbus_action.sa_flags = SA_SIGINFO;
1136
1137         sigaction(SIGBUS, &sigbus_action, NULL);
1138 #endif
1139 }
1140
1141 static void setup_sigsegv_handler(void)
1142 {
1143         struct sigaction sigsegv_action;
1144         sigfillset(&sigsegv_action.sa_mask);
1145         sigsegv_action.sa_sigaction = sigsegv_handler_cb;
1146         sigsegv_action.sa_flags = SA_SIGINFO;
1147
1148         sigaction(SIGSEGV, &sigsegv_action, NULL);
1149 }
1150
1151 static void set_theme_bg_color()
1152 {
1153         GdkRGBA bg_color;
1154         GdkColor theme_color;
1155         GtkStyleContext *style_context;
1156         GList *work;
1157         LayoutWindow *lw;
1158
1159         if (!options->image.use_custom_border_color)
1160                 {
1161                 work = layout_window_list;
1162                 lw = work->data;
1163
1164                 style_context = gtk_widget_get_style_context(lw->window);
1165                 gtk_style_context_get_background_color(style_context, GTK_STATE_FLAG_NORMAL, &bg_color);
1166
1167                 theme_color.red = bg_color.red * 65535;
1168                 theme_color.green = bg_color.green * 65535;
1169                 theme_color.blue = bg_color.blue * 65535;
1170
1171                 while (work)
1172                         {
1173                         lw = work->data;
1174                         image_background_set_color(lw->image, &theme_color);
1175                         work = work->next;
1176                         }
1177                 }
1178
1179         view_window_colors_update();
1180 }
1181
1182 static gboolean theme_change_cb(GObject *UNUSED(gobject), GParamSpec *UNUSED(pspec), gpointer UNUSED(data))
1183 {
1184         set_theme_bg_color();
1185
1186         return FALSE;
1187 }
1188
1189 /**
1190  * @brief Set up the application paths
1191  * 
1192  * This function is required for use of AppImages. AppImages are
1193  * relocatable, and therefore cannot use fixed paths to various components.
1194  * These paths were originally #defines created during compilation.
1195  * They are now variables, all defined relative to one level above the
1196  * directory that the executable is run from.
1197  */
1198 static void create_application_paths()
1199 {
1200         gchar *dirname;
1201         gint length;
1202         gchar *path;
1203
1204         length = wai_getExecutablePath(NULL, 0, NULL);
1205         path = (gchar *)malloc(length + 1);
1206         wai_getExecutablePath(path, length, NULL);
1207         path[length] = '\0';
1208
1209         gq_executable_path = g_strdup(path);
1210         dirname = g_path_get_dirname(gq_executable_path);
1211         gq_prefix = g_path_get_dirname(dirname);
1212
1213         gq_localedir = g_build_filename(gq_prefix, GQ_LOCALEDIR, NULL);
1214         gq_helpdir = g_build_filename(gq_prefix, GQ_HELPDIR, NULL);
1215         gq_htmldir = g_build_filename(gq_prefix, GQ_HTMLDIR, NULL);
1216         gq_appdir = g_build_filename(gq_prefix, GQ_APPDIR, NULL);
1217         gq_bindir = g_build_filename(gq_prefix, GQ_BINDIR, NULL);
1218         desktop_file_template = g_build_filename(gq_appdir, "template.desktop", NULL);
1219
1220         g_free(dirname);
1221         g_free(path);
1222 }
1223
1224 gboolean stderr_channel_cb(GIOChannel *source, GIOCondition UNUSED(condition), gpointer UNUSED(data))
1225 {
1226         static GString *message_str = NULL;
1227         gchar buf[10] = {0};
1228         gsize count;
1229
1230         if (!message_str)
1231                 {
1232                 message_str = g_string_new(NULL);
1233                 }
1234
1235         g_io_channel_read_chars(source, buf, 1, &count, NULL);
1236
1237         if (count > 0)
1238                 {
1239                 if (buf[0] == '\n')
1240                         {
1241                         log_printf("%s", message_str->str);
1242                         g_string_free(message_str, TRUE);
1243                         message_str = NULL;
1244                         }
1245                 else
1246                         {
1247                         message_str = g_string_append_c(message_str, buf[0]);
1248                         }
1249                 return TRUE;
1250                 }
1251         else
1252                 {
1253                 return FALSE;
1254                 }
1255 }
1256
1257 gint main(gint argc, gchar *argv[])
1258 {
1259         CollectionData *first_collection = NULL;
1260         gchar *buf;
1261         CollectionData *cd = NULL;
1262         gboolean disable_clutter = FALSE;
1263         gboolean single_dir = TRUE;
1264         LayoutWindow *lw;
1265         GtkSettings *default_settings;
1266         gint fd_stderr[2];
1267         GIOChannel *stderr_channel;
1268
1269         gdk_set_allowed_backends("x11,*");
1270
1271         gdk_threads_init();
1272         gdk_threads_enter();
1273
1274         /* init execution time counter (debug only) */
1275         init_exec_time();
1276
1277         create_application_paths();
1278
1279         /* setup locale, i18n */
1280         setlocale(LC_ALL, "");
1281
1282 #ifdef ENABLE_NLS
1283         bindtextdomain(PACKAGE, gq_localedir);
1284         bind_textdomain_codeset(PACKAGE, "UTF-8");
1285         textdomain(PACKAGE);
1286 #endif
1287
1288         /* Tee stderr to log window */
1289         if (pipe(fd_stderr) == 0)
1290                 {
1291                 if (dup2(fd_stderr[1], fileno(stderr)) != -1)
1292                         {
1293                         close(fd_stderr[1]);
1294                         stderr_channel = g_io_channel_unix_new(fd_stderr[0]);
1295                         g_io_add_watch(stderr_channel, G_IO_IN, (GIOFunc)stderr_channel_cb, NULL);
1296                         }
1297                 }
1298
1299         exif_init();
1300
1301 #ifdef HAVE_LUA
1302         lua_init();
1303 #endif
1304
1305         /* setup random seed for random slideshow */
1306         srand(time(NULL));
1307
1308         /* seg. fault handler */
1309         setup_sigsegv_handler();
1310
1311 #if 0
1312         /* See later comment; this handler leads to UB. */
1313         setup_sigbus_handler();
1314 #endif
1315
1316         /* register global notify functions */
1317         file_data_register_notify_func(cache_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
1318         file_data_register_notify_func(thumb_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
1319         file_data_register_notify_func(histogram_notify_cb, NULL, NOTIFY_PRIORITY_HIGH);
1320         file_data_register_notify_func(collect_manager_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
1321         file_data_register_notify_func(metadata_notify_cb, NULL, NOTIFY_PRIORITY_LOW);
1322
1323
1324         gtkrc_load();
1325
1326         parse_command_line_for_debug_option(argc, argv);
1327         DEBUG_1("%s main: gtk_init", get_exec_time());
1328 #ifdef HAVE_CLUTTER
1329         if (parse_command_line_for_clutter_option(argc, argv))
1330                 {
1331                 disable_clutter = TRUE;
1332                 gtk_init(&argc, &argv);
1333                 }
1334         else
1335                 {
1336                 if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS)
1337                         {
1338                         log_printf("Can't initialize clutter-gtk.\nStart Geeqie with the option \"geeqie --disable-clutter\"");
1339                         runcmd("zenity --error --title=\"Geeqie\" --text \"Can't initialize clutter-gtk.\n\nStart Geeqie with the option:\n geeqie --disable-clutter\" --width=300");
1340                         exit(1);
1341                         }
1342                 }
1343 #else
1344         gtk_init(&argc, &argv);
1345 #endif
1346
1347         if (gtk_major_version < GTK_MAJOR_VERSION ||
1348             (gtk_major_version == GTK_MAJOR_VERSION && gtk_minor_version < GTK_MINOR_VERSION) )
1349                 {
1350                 log_printf("!!! This is a friendly warning.\n");
1351                 log_printf("!!! The version of GTK+ in use now is older than when %s was compiled.\n", GQ_APPNAME);
1352                 log_printf("!!!  compiled with GTK+-%d.%d\n", GTK_MAJOR_VERSION, GTK_MINOR_VERSION);
1353                 log_printf("!!!   running with GTK+-%d.%d\n", gtk_major_version, gtk_minor_version);
1354                 log_printf("!!! %s may quit unexpectedly with a relocation error.\n", GQ_APPNAME);
1355                 }
1356
1357         DEBUG_1("%s main: pixbuf_inline_register_stock_icons", get_exec_time());
1358         gtk_icon_theme_add_resource_path(gtk_icon_theme_get_default(), GQ_RESOURCE_PATH_ICONS);
1359         pixbuf_inline_register_stock_icons();
1360
1361         DEBUG_1("%s main: setting default options before commandline handling", get_exec_time());
1362         options = init_options(NULL);
1363         setup_default_options(options);
1364         if (disable_clutter)
1365                 {
1366                 options->disable_gpu = TRUE;
1367                 }
1368
1369         /* Generate a unique identifier used by the open archive function */
1370         instance_identifier = g_strdup_printf("%x", g_random_int());
1371
1372         DEBUG_1("%s main: mkdir_if_not_exists", get_exec_time());
1373         /* these functions don't depend on config file */
1374         mkdir_if_not_exists(get_rc_dir());
1375         mkdir_if_not_exists(get_collections_dir());
1376         mkdir_if_not_exists(get_thumbnails_cache_dir());
1377         mkdir_if_not_exists(get_metadata_cache_dir());
1378         mkdir_if_not_exists(get_window_layouts_dir());
1379
1380         setup_env_path();
1381
1382         if (parse_command_line_for_cache_maintenance_option(argc, argv))
1383                 {
1384                 process_command_line_for_cache_maintenance_option(argc, argv);
1385                 }
1386         else
1387                 {
1388                 DEBUG_1("%s main: parse_command_line", get_exec_time());
1389                 parse_command_line(argc, argv);
1390
1391                 keys_load();
1392                 accel_map_load();
1393
1394                 /* restore session from the config file */
1395
1396
1397                 DEBUG_1("%s main: load_options", get_exec_time());
1398                 if (!load_options(options))
1399                         {
1400                         /* load_options calls these functions after it parses global options, we have to call it here if it fails */
1401                         filter_add_defaults();
1402                         filter_rebuild();
1403                         }
1404
1405         #ifdef HAVE_CLUTTER
1406         /** @FIXME For the background of this see:
1407          * https://github.com/BestImageViewer/geeqie/issues/397
1408          * The feature CLUTTER_FEATURE_SWAP_EVENTS indictates if the
1409          * system is liable to exhibit this problem.
1410          * The user is provided with an override in Preferences/Behavior
1411          */
1412                 if (!options->override_disable_gpu && !options->disable_gpu)
1413                         {
1414                         DEBUG_1("CLUTTER_FEATURE_SWAP_EVENTS %d",clutter_feature_available(CLUTTER_FEATURE_SWAP_EVENTS));
1415                         if (clutter_feature_available(CLUTTER_FEATURE_SWAP_EVENTS) != 0)
1416                                 {
1417                                 options->disable_gpu = TRUE;
1418                                 }
1419                         }
1420         #endif
1421
1422                 /* handle missing config file and commandline additions*/
1423                 if (!layout_window_list)
1424                         {
1425                         /* broken or no config file or no <layout> section */
1426                         layout_new_from_default();
1427                         }
1428
1429                 layout_editors_reload_start();
1430
1431                 /* If no --list option, open a separate collection window for each
1432                  * .gqv file on the command line
1433                  */
1434                 if (command_line->collection_list && !command_line->startup_command_line_collection)
1435                         {
1436                         GList *work;
1437
1438                         work = command_line->collection_list;
1439                         while (work)
1440                                 {
1441                                 CollectWindow *cw;
1442                                 const gchar *path;
1443
1444                                 path = work->data;
1445                                 work = work->next;
1446
1447                                 cw = collection_window_new(path);
1448                                 if (!first_collection && cw) first_collection = cw->cd;
1449                                 }
1450                         }
1451
1452                 if (command_line->log_file)
1453                         {
1454                         gchar *pathl;
1455                         gchar *path = g_strdup(command_line->log_file);
1456
1457                         pathl = path_from_utf8(path);
1458                         command_line->ssi = secure_open(pathl);
1459                         }
1460
1461                 /* If there is a files list on the command line and no --list option,
1462                  * check if they are all in the same folder
1463                  */
1464                 if (command_line->cmd_list && !(command_line->startup_command_line_collection))
1465                         {
1466                         GList *work;
1467                         gchar *path = NULL;
1468
1469                         work = command_line->cmd_list;
1470
1471                         while (work && single_dir)
1472                                 {
1473                                 gchar *dirname;
1474
1475                                 dirname = g_path_get_dirname(work->data);
1476                                 if (!path)
1477                                         {
1478                                         path = g_strdup(dirname);
1479                                         }
1480                                 else
1481                                         {
1482                                         if (g_strcmp0(path, dirname) != 0)
1483                                                 {
1484                                                 single_dir = FALSE;
1485                                                 }
1486                                         }
1487                                 g_free(dirname);
1488                                 work = work->next;
1489                                 }
1490                         g_free(path);
1491                         }
1492
1493                 /* Files from multiple folders, or --list option given
1494                  * then open an unnamed collection and insert all files
1495                  */
1496                 if ((command_line->cmd_list && !single_dir) || (command_line->startup_command_line_collection && command_line->cmd_list))
1497                         {
1498                         GList *work;
1499                         CollectWindow *cw;
1500
1501                         cw = collection_window_new(NULL);
1502                         cd = cw->cd;
1503
1504                         collection_path_changed(cd);
1505
1506                         work = command_line->cmd_list;
1507                         while (work)
1508                                 {
1509                                 FileData *fd;
1510
1511                                 fd = file_data_new_simple(work->data);
1512                                 collection_add(cd, fd, FALSE);
1513                                 file_data_unref(fd);
1514                                 work = work->next;
1515                                 }
1516
1517                         work = command_line->collection_list;
1518                         while (work)
1519                                 {
1520                                 collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND);
1521                                 work = work->next;
1522                                 }
1523
1524                         if (cd->list) layout_image_set_collection(NULL, cd, cd->list->data);
1525
1526                         /* mem leak, we never unref this collection when !startup_command_line_collection
1527                          * (the image view of the main window does not hold a ref to the collection)
1528                          * this is sort of unavoidable, for if it did hold a ref, next/back
1529                          * may not work as expected when closing collection windows.
1530                          *
1531                          * collection_unref(cd);
1532                          */
1533
1534                         }
1535                 else if (first_collection)
1536                         {
1537                         layout_image_set_collection(NULL, first_collection,
1538                                                     collection_get_first(first_collection));
1539                         }
1540
1541                 /* If the files on the command line are from one folder, select those files
1542                  * unless it is a command line collection - then leave focus on collection window
1543                  */
1544                 lw = NULL;
1545                 layout_valid(&lw);
1546
1547                 if (single_dir && command_line->cmd_list && !command_line->startup_command_line_collection)
1548                         {
1549                         GList *work;
1550                         GList *selected;
1551                         FileData *fd;
1552
1553                         selected = NULL;
1554                         work = command_line->cmd_list;
1555                         while (work)
1556                                 {
1557                                 fd = file_data_new_simple((gchar *)work->data);
1558                                 selected = g_list_append(selected, fd);
1559                                 file_data_unref(fd);
1560                                 work = work->next;
1561                                 }
1562                         layout_select_list(lw, selected);
1563                         }
1564
1565                 buf = g_build_filename(get_rc_dir(), ".command", NULL);
1566                 remote_connection = remote_server_init(buf, cd);
1567                 g_free(buf);
1568
1569                 marks_load();
1570
1571                 default_settings = gtk_settings_get_default();
1572                 g_signal_connect(default_settings, "notify::gtk-theme-name", G_CALLBACK(theme_change_cb), NULL);
1573                 set_theme_bg_color();
1574                 }
1575
1576         /* Show a fade-out notification window if the server has a newer AppImage version */
1577         if (options->appimage_notifications && g_getenv("APPDIR") && strstr(g_getenv("APPDIR"), "/tmp/.mount_Geeqie"))
1578                 {
1579                 appimage_notification();
1580                 }
1581
1582         DEBUG_1("%s main: gtk_main", get_exec_time());
1583         gtk_main();
1584
1585         gdk_threads_leave();
1586         return 0;
1587 }
1588 /* vim: set shiftwidth=8 softtabstop=0 cindent cinoptions={1s: */