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