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