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