X-Git-Url: http://geeqie.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fmain.c;h=91f8443ca7b525688c06238ee53534302430e861;hb=563f45652a914de2f62e5c098388914cca5a7adf;hp=c7ddf4b5955fc040b592790eab6d7ccabfcfc952;hpb=dcadde2b6220414d6fc833c97a31b33fa0995d87;p=geeqie.git diff --git a/src/main.c b/src/main.c index c7ddf4b5..91f8443c 100644 --- a/src/main.c +++ b/src/main.c @@ -1,13 +1,22 @@ /* - * Geeqie - * (C) 2006 John Ellis - * Copyright (C) 2008 - 2009 The Geeqie Team + * Copyright (C) 2006 John Ellis + * Copyright (C) 2008 - 2016 The Geeqie Team * * Author: John Ellis * - * This software is released under the GNU General Public License (GNU GPL). - * Please read the included file COPYING for more information. - * This software comes with no warranty of any kind, use at your own risk! + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include /* for keyboard values */ @@ -19,6 +28,7 @@ #ifdef G_OS_UNIX #include #endif +#include #include "main.h" @@ -28,10 +38,15 @@ #include "filedata.h" #include "filefilter.h" #include "history_list.h" +#include "image.h" #include "image-overlay.h" +#include "img-view.h" #include "layout.h" #include "layout_image.h" +#include "layout_util.h" +#include "misc.h" #include "options.h" +#include "rcfile.h" #include "remote.h" #include "secure_save.h" #include "similar.h" @@ -44,17 +59,223 @@ #include "exif.h" #include "histogram.h" #include "pixbuf_util.h" +#include "glua.h" +#include "whereami.h" -#ifdef HAVE_LIBCHAMPLAIN -#ifdef HAVE_LIBCHAMPLAIN_GTK -#include +#ifdef HAVE_CLUTTER +#include #endif + +#ifdef HAVE_GTHREAD +/** @FIXME see below */ +#include #endif +/** + * @page diagrams Diagrams + * @section options_overview Options Overview + * + * #_ConfOptions #_LayoutOptions + * + * @startuml + * + * object options.h + * object typedefs.h + * + * options.h : ConfOptions + * options.h : \n + * options.h : Options applicable to **all** Layout Windows + * options.h : These are in the section of geeqierc.xml + * options.h : Available to all modules via the global variable **options** + * typedefs.h : LayoutOptions + * typedefs.h : \n + * typedefs.h : Options applicable to **each** Layout Window + * typedefs.h : These are in the section of geeqierc.xml + * typedefs.h : There is one section for each Layout Window displayed + * typedefs.h : Available via **->options** + * + * @enduml + */ + +/** + * @page diagrams Diagrams + * @section options_diagrams_main Options - New Window From Main + * #main + * #init_options + * #layout_new_from_default + * #load_config_from_file + * #load_options + * #setup_default_options + * + * @startuml + * group main.c + * start + * group options.c + * : **init_options()** + * + * Set **options** = ConfOptions from hard-coded init values; + * end group + * + * group options.c + * : **setup_default_options()** + * + * set hard-coded ConfOptions: + * + * bookmarks: + * * dot dir + * * Home + * * Desktop + * * Collections + * safe delete path + * OSD template string + * sidecar extensions + * shell path and options + * marks tooltips + * help search engine; + * end group + * + * if (first entry + * or + * --new-instance) then (yes) + * group options.c + * : **load_options()** + * ; + * + * split + * : GQ_SYSTEM_WIDE_DIR + * /geeqierc.xml; + * split again + * : XDG_CONFIG_HOME + * /geeqierc.xml; + * split again + * : HOME + * /.geeqie/geeqierc.xml; + * end split + * + * group rcfile.c + * : **load_config_from_file()** + * + * set **options** from file + * and all ->options in file; + * end group + * + * end group + * + * if (broken config. file + * or no config file + * or no layout section loaded + * (i.e. session not saved)) then (yes) + * group layout.c + * : **layout_new_from_default()**; + * if (default.xml exists) then (yes) + * : Load user-saved + * layout_window default options + * from default.xml file; + * else (no) + * : Load hard-coded + * layout_window default options; + * endif + * end group + * endif + * + * else (no) + * : Send --new-window request to remote + * No return to this point + * This instance terminates; + * stop + * endif + * + * : Enter gtk main loop; + * + * end group + * @enduml + */ + +/** + * @page diagrams Diagrams + * @section options_diagrams_remote Options - New Window From Remote + * #layout_new_from_default + * @startuml + * + * group remote.c + * start + * group layout.c + * : **layout_new_from_default()**; + * if (default.xml exists) then (yes) + * : Load user-saved + * layout_window default options + * from default.xml file; + * else (no) + * : Load hard-coded + * layout_window default options; + * endif + * end group + * : set path from PWD; + * @enduml + */ + +/** + * @page diagrams Diagrams + * @section options_diagrams_menu Options - New Window From Menu + * #layout_menu_new_window_cb + * #layout_menu_window_from_current_cb + * #layout_new_from_default + * @startuml + * + * group layout_util.c + * start + * + * split + * : default; + * group layout.c + * : **layout_new_from_default()**; + * if (default.xml exists) then (yes) + * : Load user-saved + * layout_window default options + * from default.xml file; + * else (no) + * : Load hard-coded + * layout_window default options; + * endif + * end group + * + * split again + * : from current + * + * **layout_menu_window_from_current_cb()** + * copy layout_window options + * from current window; + * + * split again + * : named + * + * **layout_menu_new_window_cb()** + * load layout_window options + * from saved xml file list; + * end split + * + * end group + * @enduml + */ + /** + * @file + * @ref options_overview Options Overview + */ + gboolean thumb_format_changed = FALSE; static RemoteConnection *remote_connection = NULL; +gchar *gq_prefix; +gchar *gq_localedir; +gchar *gq_helpdir; +gchar *gq_htmldir; +gchar *gq_app_dir; +gchar *gq_bin_dir; +gchar *gq_executable_path; +gchar *desktop_file_template; +gchar *instance_identifier; + /* *----------------------------------------------------------------------------- * keyboard functions @@ -96,8 +317,8 @@ void keyboard_scroll_calc(gint *x, gint *y, GdkEventKey *event) delta = 8; } - *x = *x * delta; - *y = *y * delta; + *x = *x * delta * options->keyboard_scroll_step; + *y = *y * delta * options->keyboard_scroll_step; } @@ -124,21 +345,24 @@ static void parse_command_line_add_file(const gchar *file_path, gchar **path, gc { if (!*path) *path = remove_level_from_path(path_parsed); if (!*file) *file = g_strdup(path_parsed); - *list = g_list_prepend(*list, file_data_new_simple(path_parsed)); + *list = g_list_prepend(*list, path_parsed); } } static void parse_command_line_add_dir(const gchar *dir, gchar **path, gchar **file, GList **list) { +#if 0 + /* This is broken because file filter is not initialized yet. + */ GList *files; gchar *path_parsed; FileData *dir_fd; path_parsed = g_strdup(dir); parse_out_relatives(path_parsed); - dir_fd = file_data_new_simple(path_parsed); - + dir_fd = file_data_new_dir(path_parsed); + if (filelist_read(dir_fd, &files, NULL)) { @@ -163,6 +387,9 @@ static void parse_command_line_add_dir(const gchar *dir, gchar **path, gchar **f g_free(path_parsed); file_data_unref(dir_fd); +#else + DEBUG_1("multiple directories specified, ignoring: %s", dir); +#endif } static void parse_command_line_process_dir(const gchar *dir, gchar **path, gchar **file, @@ -205,11 +432,18 @@ static void parse_command_line(gint argc, gchar *argv[]) GList *remote_errors = NULL; gboolean remote_do = FALSE; gchar *first_dir = NULL; - + gchar *app_lock; + gchar *pwd; + gchar *current_dir; + gchar *geometry = NULL; + GtkWidget *dialog_warning; + GString *command_line_errors = g_string_new(NULL); + command_line = g_new0(CommandLine, 1); - + command_line->argc = argc; command_line->argv = argv; + command_line->regexp = NULL; if (argc > 1) { @@ -239,10 +473,26 @@ static void parse_command_line(gint argc, gchar *argv[]) parse_command_line_process_file(cmd_all, &command_line->path, &command_line->file, &list, &command_line->collection_list, &first_dir); } + else if (download_web_file(cmd_line, FALSE, NULL)) + { + } + else if (is_collection(cmd_line)) + { + gchar *path = NULL; + + path = collection_path(cmd_line); + parse_command_line_process_file(path, &command_line->path, &command_line->file, + &list, &command_line->collection_list, &first_dir); + g_free(path); + } else if (strncmp(cmd_line, "--debug", 7) == 0 && (cmd_line[7] == '\0' || cmd_line[7] == '=')) { /* do nothing but do not produce warnings */ } + else if (strncmp(cmd_line, "--disable-clutter", 17) == 0 && (cmd_line[17] == '\0')) + { + /* do nothing but do not produce warnings */ + } else if (strcmp(cmd_line, "+t") == 0 || strcmp(cmd_line, "--with-tools") == 0) { @@ -285,6 +535,35 @@ static void parse_command_line(gint argc, gchar *argv[]) remote_list = remote_build_list(remote_list, argc - i, &argv[i], &remote_errors); } } + else if ((strcmp(cmd_line, "+w") == 0) || + strcmp(cmd_line, "--show-log-window") == 0) + { + command_line->log_window_show = TRUE; + } + else if (strncmp(cmd_line, "-o:", 3) == 0) + { + command_line->log_file = g_strdup(cmd_line + 3); + } + else if (strncmp(cmd_line, "--log-file:", 11) == 0) + { + command_line->log_file = g_strdup(cmd_line + 11); + } + else if (strncmp(cmd_line, "-g:", 3) == 0) + { + set_regexp(g_strdup(cmd_line+3)); + } + else if (strncmp(cmd_line, "-grep:", 6) == 0) + { + set_regexp(g_strdup(cmd_line+3)); + } + else if (strncmp(cmd_line, "-n", 2) == 0) + { + command_line->new_instance = TRUE; + } + else if (strncmp(cmd_line, "--new-instance", 14) == 0) + { + command_line->new_instance = TRUE; + } else if (strcmp(cmd_line, "-rh") == 0 || strcmp(cmd_line, "--remote-help") == 0) { @@ -298,7 +577,7 @@ static void parse_command_line(gint argc, gchar *argv[]) else if (strcmp(cmd_line, "-v") == 0 || strcmp(cmd_line, "--version") == 0) { - printf_term("%s %s\n", GQ_APPNAME, VERSION); + printf_term(FALSE, "%s %s GTK%d\n", GQ_APPNAME, VERSION, gtk_major_version); exit(0); } else if (strcmp(cmd_line, "--alternate") == 0) @@ -310,40 +589,63 @@ static void parse_command_line(gint argc, gchar *argv[]) else if (strcmp(cmd_line, "-h") == 0 || strcmp(cmd_line, "--help") == 0) { - printf_term("%s %s\n", GQ_APPNAME, VERSION); - printf_term(_("Usage: %s [options] [path]\n\n"), GQ_APPNAME_LC); - print_term(_("valid options are:\n")); - print_term(_(" +t, --with-tools force show of tools\n")); - print_term(_(" -t, --without-tools force hide of tools\n")); - print_term(_(" -f, --fullscreen start in full screen mode\n")); - print_term(_(" -s, --slideshow start in slideshow mode\n")); - print_term(_(" -l, --list open collection window for command line\n")); - print_term(_(" --geometry=GEOMETRY set main window location\n")); - print_term(_(" -r, --remote send following commands to open window\n")); - print_term(_(" -rh,--remote-help print remote command list\n")); + printf_term(FALSE, "%s %s\n", GQ_APPNAME, VERSION); + printf_term(FALSE, _("Usage: %s [options] [path]\n\n"), GQ_APPNAME_LC); + print_term(FALSE, _("valid options are:\n")); + print_term(FALSE, _(" +t, --with-tools force show of tools\n")); + print_term(FALSE, _(" -t, --without-tools force hide of tools\n")); + print_term(FALSE, _(" -f, --fullscreen start in full screen mode\n")); + print_term(FALSE, _(" -s, --slideshow start in slideshow mode\n")); + print_term(FALSE, _(" -l, --list [files] [collections] open collection window for command line\n")); + print_term(FALSE, _(" --blank start with blank file list\n")); + print_term(FALSE, _(" --geometry=XxY+XOFF+YOFF set main window location\n")); + print_term(FALSE, _(" -n, --new-instance open a new instance of Geeqie\n")); + print_term(FALSE, _(" -r, --remote send following commands to open window\n")); + print_term(FALSE, _(" -rh,--remote-help print remote command list\n")); #ifdef DEBUG - print_term(_(" --debug[=level] turn on debug output\n")); + print_term(FALSE, _(" --debug[=level] turn on debug output\n")); + print_term(FALSE, _(" -g:, --grep: filter debug output\n")); #endif - print_term(_(" -v, --version print version info\n")); - print_term(_(" -h, --help show this message\n\n")); + print_term(FALSE, _(" +w, --show-log-window show log window\n")); + print_term(FALSE, _(" -o:, --log-file: save log data to file\n")); + print_term(FALSE, _(" -v, --version print version info\n")); + print_term(FALSE, _(" -h, --help show this message\n")); + print_term(FALSE, _(" --disable-clutter disable use of Clutter library (i.e. GPU accel.)\n")); + print_term(FALSE, _(" --cache-maintenance run cache maintenance in non-GUI mode\n\n")); #if 0 /* these options are not officially supported! * only for testing new features, no need to translate them */ - print_term( " --alternate use alternate similarity algorithm\n"); + print_term(FALSE, " --alternate use alternate similarity algorithm\n"); #endif + exit(0); } else if (!remote_do) { - printf_term(_("invalid or ignored: %s\nUse --help for options\n"), cmd_line); + command_line_errors = g_string_append(command_line_errors, cmd_line); + command_line_errors = g_string_append(command_line_errors, "\n"); } g_free(cmd_all); g_free(cmd_line); i++; } + + if (command_line_errors->len > 0) + { + dialog_warning = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "Invalid parameter(s):"); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_warning), "%s", command_line_errors->str); + gtk_window_set_title(GTK_WINDOW(dialog_warning), GQ_APPNAME); + gtk_window_set_keep_above(GTK_WINDOW(dialog_warning), TRUE); + gtk_dialog_run(GTK_DIALOG(dialog_warning)); + gtk_widget_destroy(dialog_warning); + g_string_free(command_line_errors, TRUE); + + exit(EXIT_FAILURE); + } + g_free(base_dir); parse_out_relatives(command_line->path); parse_out_relatives(command_line->file); @@ -360,26 +662,65 @@ static void parse_command_line(gint argc, gchar *argv[]) } g_free(first_dir); + if (!command_line->new_instance) + { + /* If Geeqie is already running, prevent a second instance + * from being started. Open a new window instead. + */ + app_lock = g_build_filename(get_rc_dir(), ".command", NULL); + if (remote_server_exists(app_lock) && !remote_do) + { + remote_do = TRUE; + if (command_line->geometry) + { + geometry = g_strdup_printf("--geometry=%s", command_line->geometry); + remote_list = g_list_prepend(remote_list, geometry); + } + remote_list = g_list_prepend(remote_list, "--new-window"); + } + g_free(app_lock); + } + if (remote_do) { if (remote_errors) { GList *work = remote_errors; - - printf_term(_("Invalid or ignored remote options: ")); + while (work) { gchar *opt = work->data; - - printf_term("%s%s", (work == remote_errors) ? "" : ", ", opt); + + command_line_errors = g_string_append(command_line_errors, opt); + command_line_errors = g_string_append(command_line_errors, "\n"); work = work->next; } - printf_term(_("\nUse --remote-help for valid remote options.\n")); + dialog_warning = gtk_message_dialog_new(NULL, GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, "%s", "Invalid parameter(s):"); + gtk_message_dialog_format_secondary_text(GTK_MESSAGE_DIALOG(dialog_warning), "%s", command_line_errors->str); + gtk_window_set_title(GTK_WINDOW(dialog_warning), GQ_APPNAME); + gtk_window_set_keep_above(GTK_WINDOW(dialog_warning), TRUE); + gtk_dialog_run(GTK_DIALOG(dialog_warning)); + gtk_widget_destroy(dialog_warning); + g_string_free(command_line_errors, TRUE); + + exit(EXIT_FAILURE); } + /* prepend the current dir the remote command was made from, + * for use by any remote command that needs it + */ + current_dir = g_get_current_dir(); + pwd = g_strconcat("--PWD:", current_dir, NULL); + remote_list = g_list_prepend(remote_list, pwd); + remote_control(argv[0], remote_list, command_line->path, list, command_line->collection_list); + /* There is no return to this point + */ + g_free(pwd); + g_free(current_dir); } + g_free(geometry); g_list_free(remote_list); if (list && list->next) @@ -388,7 +729,7 @@ static void parse_command_line(gint argc, gchar *argv[]) } else { - filelist_free(list); + string_list_free(list); command_line->cmd_list = NULL; } @@ -439,6 +780,120 @@ static void parse_command_line_for_debug_option(gint argc, gchar *argv[]) #endif } +#ifdef HAVE_CLUTTER +static gboolean parse_command_line_for_clutter_option(gint argc, gchar *argv[]) +{ + const gchar *clutter_option = "--disable-clutter"; + gint len = strlen(clutter_option); + gboolean ret = FALSE; + + if (argc > 1) + { + gint i; + + for (i = 1; i < argc; i++) + { + const gchar *cmd_line = argv[i]; + if (strncmp(cmd_line, clutter_option, len) == 0) + { + ret = TRUE; + } + } + } + + return ret; +} +#endif + +static gboolean parse_command_line_for_cache_maintenance_option(gint argc, gchar *argv[]) +{ + const gchar *cache_maintenance_option = "--cache-maintenance"; + gint len = strlen(cache_maintenance_option); + gboolean ret = FALSE; + + if (argc >= 2) + { + const gchar *cmd_line = argv[1]; + if (strncmp(cmd_line, cache_maintenance_option, len) == 0) + { + ret = TRUE; + } + } + + return ret; +} + +static void process_command_line_for_cache_maintenance_option(gint argc, gchar *argv[]) +{ + gchar *rc_path; + gchar *folder_path = NULL; + gsize size; + gsize i = 0; + gchar *buf_config_file; + gint diff_count; + + if (argc >= 3) + { + folder_path = expand_tilde(argv[2]); + + if (isdir(folder_path)) + { + rc_path = g_build_filename(get_rc_dir(), RC_FILE_NAME, NULL); + + if (isfile(rc_path)) + { + if (g_file_get_contents(rc_path, &buf_config_file, &size, NULL)) + { + while (i < size) + { + diff_count = strncmp("", &buf_config_file[i], 9); + if (diff_count == 0) + { + break; + } + i++; + } + /* Load only the section */ + load_config_from_buf(buf_config_file, i + 9, FALSE); + + if (options->thumbnails.enable_caching) + { + cache_maintenance(folder_path); + } + else + { + print_term(TRUE, "Caching not enabled\n"); + exit(EXIT_FAILURE); + } + g_free(buf_config_file); + } + else + { + print_term(TRUE, g_strconcat(_("Cannot load "), rc_path, "\n", NULL)); + exit(EXIT_FAILURE); + } + } + else + { + print_term(TRUE, g_strconcat(_("Configuration file path "), rc_path, _(" is not a file\n"), NULL)); + exit(EXIT_FAILURE); + } + g_free(rc_path); + } + else + { + print_term(TRUE, g_strconcat(argv[2], _(" is not a folder\n"), NULL)); + exit(EXIT_FAILURE); + } + g_free(folder_path); + } + else + { + print_term(TRUE, _("No path parameter given\n")); + exit(EXIT_FAILURE); + } +} + /* *----------------------------------------------------------------------------- * startup, init, and exit @@ -446,11 +901,12 @@ static void parse_command_line_for_debug_option(gint argc, gchar *argv[]) */ #define RC_HISTORY_NAME "history" +#define RC_MARKS_NAME "marks" static void setup_env_path(void) { const gchar *old_path = g_getenv("PATH"); - gchar *path = g_strconcat(GQ_BIN_DIR, ":", old_path, NULL); + gchar *path = g_strconcat(gq_bin_dir, ":", old_path, NULL); g_setenv("PATH", path, TRUE); g_free(path); } @@ -473,6 +929,24 @@ static void keys_save(void) g_free(path); } +static void marks_load(void) +{ + gchar *path; + + path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL); + marks_list_load(path); + g_free(path); +} + +static void marks_save(gboolean save) +{ + gchar *path; + + path = g_build_filename(get_rc_dir(), RC_MARKS_NAME, NULL); + marks_list_save(path, save); + g_free(path); +} + static void mkdir_if_not_exists(const gchar *path) { if (isdir(path)) return; @@ -536,7 +1010,7 @@ static gboolean gq_accel_map_save(const gchar *path) log_printf(_("error saving file: %s\n"), path); return FALSE; } - + gstring = g_string_new("; "); if (g_get_prgname()) g_string_append(gstring, g_get_prgname()); @@ -604,11 +1078,33 @@ static void gtkrc_load(void) static void exit_program_final(void) { LayoutWindow *lw = NULL; + GList *list; + LayoutWindow *tmp_lw; + gchar *archive_dir; + GFile *archive_file; + + /* make sure that external editors are loaded, we would save incomplete configuration otherwise */ + layout_editors_reload_finish(); remote_close(remote_connection); collect_manager_flush(); + /* Save the named windows */ + if (layout_window_list && layout_window_list->next) + { + list = layout_window_list; + while (list) + { + tmp_lw = list->data; + if (!g_str_has_prefix(tmp_lw->options.id, "lw")) + { + save_layout(list->data); + } + list = list->next; + } + } + save_options(options); keys_save(); accel_map_save(); @@ -618,6 +1114,29 @@ static void exit_program_final(void) layout_free(lw); } + /* Delete any files/folders in /tmp that have been created by the open archive function */ + archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, instance_identifier, NULL); + if (isdir(archive_dir)) + { + archive_file = g_file_new_for_path(archive_dir); + rmdir_recursive(archive_file, NULL, NULL); + g_free(archive_dir); + g_object_unref(archive_file); + } + + /* If there are still sub-dirs created by another instance, this will fail + * but that does not matter */ + archive_dir = g_build_filename(g_get_tmp_dir(), GQ_ARCHIVE_DIR, NULL); + if (isdir(archive_dir)) + { + archive_file = g_file_new_for_path(archive_dir); + g_file_delete(archive_file, NULL, NULL); + g_free(archive_dir); + g_object_unref(archive_file); + } + + secure_close(command_line->ssi); + gtk_main_quit(); } @@ -664,7 +1183,7 @@ static gint exit_confirm_dlg(void) g_free(msg); msg = g_strdup_printf(_("Quit %s"), GQ_APPNAME); generic_dialog_add_message(exit_dialog, GTK_STOCK_DIALOG_QUESTION, - msg, _("Collections have been modified. Quit anyway?")); + msg, _("Collections have been modified. Quit anyway?"), TRUE); g_free(msg); generic_dialog_add_button(exit_dialog, GTK_STOCK_QUIT, NULL, exit_confirm_exit_cb, TRUE); @@ -684,12 +1203,14 @@ void exit_program(void) if (metadata_write_queue_confirm(FALSE, exit_program_write_metadata_cb, NULL)) return; + options->marks_save ? marks_save(TRUE) : marks_save(FALSE); + if (exit_confirm_dlg()) return; exit_program_final(); } -/* This code is supposed to handle situation when a file mmaped by image_loader +/* This code is supposed to handle situation when a file mmaped by image_loader * or by exif loader is truncated by some other process. * This is probably not completely correct according to posix, because * mmap is not in the list of calls that can be used safely in signal handler, @@ -697,9 +1218,9 @@ void exit_program(void) * crash otherwise. * Ideas for improvement are welcome ;) */ -/* FIXME: this probably needs some better ifdefs. Please report any compilation problems */ +/** @FIXME this probably needs some better ifdefs. Please report any compilation problems */ -#ifdef SIGBUS +#if defined(SIGBUS) && defined(SA_SIGINFO) static void sigbus_handler_cb(int signum, siginfo_t *info, void *context) { unsigned long pagesize = sysconf(_SC_PAGE_SIZE); @@ -710,7 +1231,7 @@ static void sigbus_handler_cb(int signum, siginfo_t *info, void *context) static void setup_sigbus_handler(void) { -#ifdef SIGBUS +#if defined(SIGBUS) && defined(SA_SIGINFO) struct sigaction sigbus_action; sigfillset(&sigbus_action.sa_mask); sigbus_action.sa_sigaction = sigbus_handler_cb; @@ -720,39 +1241,172 @@ static void setup_sigbus_handler(void) #endif } +static void set_theme_bg_color() +{ +#if GTK_CHECK_VERSION(3,0,0) + GdkRGBA bg_color; + GdkColor theme_color; + GtkStyleContext *style_context; + GList *work; + LayoutWindow *lw; + + if (!options->image.use_custom_border_color) + { + work = layout_window_list; + lw = work->data; + + style_context = gtk_widget_get_style_context(lw->window); + gtk_style_context_get_background_color(style_context, GTK_STATE_FLAG_NORMAL, &bg_color); + + theme_color.red = bg_color.red * 65535; + theme_color.green = bg_color.green * 65535; + theme_color.blue = bg_color.blue * 65535; + + while (work) + { + lw = work->data; + image_background_set_color(lw->image, &theme_color); + work = work->next; + } + } + + view_window_colors_update(); +#endif +} + +static gboolean theme_change_cb(GObject *gobject, GParamSpec *pspec, gpointer data) +{ + set_theme_bg_color(); + + return FALSE; +} + +/** + * @brief Set up the application paths + * + * This function is required for use of AppImages. AppImages are + * relocatable, and therefore cannot use fixed paths to various components. + * These paths were originally #defines created during compilation. + * They are now variables, all defined relative to one level above the + * directory that the executable is run from. + */ +static void create_application_paths(gchar *argv[]) +{ + gchar *dirname; + gchar *tmp; + gint length; + gchar *path; + + length = wai_getExecutablePath(NULL, 0, NULL); + path = (gchar *)malloc(length + 1); + wai_getExecutablePath(path, length, NULL); + path[length] = '\0'; + + gq_executable_path = g_strdup(path); + dirname = g_path_get_dirname(gq_executable_path); // default is /usr/bin/ + gq_prefix = g_path_get_dirname(dirname); + + gq_localedir = g_build_filename(gq_prefix, "share", "locale", NULL); + tmp = g_build_filename(gq_prefix, "share", "doc", NULL); + gq_helpdir = g_strconcat(tmp, G_DIR_SEPARATOR_S, "geeqie-", VERSION, NULL); + gq_htmldir = g_build_filename(gq_helpdir, "html", NULL); + gq_app_dir = g_build_filename(gq_prefix, "share", "geeqie", NULL); + gq_bin_dir = g_build_filename(gq_prefix, "lib", "geeqie", NULL); + desktop_file_template = g_build_filename(gq_app_dir, "template.desktop", NULL); + + g_free(tmp); + g_free(dirname); + g_free(path); +} + +gboolean stderr_channel_cb(GIOChannel *source, GIOCondition condition, gpointer data) +{ + static GString *message_str = NULL; + gchar buf[10] = {0}; + gsize count; + + if (!message_str) + { + message_str = g_string_new(NULL); + } + + g_io_channel_read_chars(source, buf, 1, &count, NULL); + + if (count > 0) + { + if (buf[0] == '\n') + { + log_printf("%s", message_str->str); + g_string_free(message_str, TRUE); + message_str = NULL; + } + else + { + message_str = g_string_append_c(message_str, buf[0]); + } + return TRUE; + } + else + { + return FALSE; + } +} + gint main(gint argc, gchar *argv[]) { CollectionData *first_collection = NULL; gchar *buf; CollectionData *cd = NULL; + gboolean disable_clutter = FALSE; + gboolean single_dir = TRUE; + LayoutWindow *lw; + GtkSettings *default_settings; + gint fd_stderr[2]; + GIOChannel *stderr_channel; #ifdef HAVE_GTHREAD +#if !GLIB_CHECK_VERSION(2,32,0) g_thread_init(NULL); +#endif gdk_threads_init(); gdk_threads_enter(); + #endif - + /* init execution time counter (debug only) */ init_exec_time(); + create_application_paths(argv); + /* setup locale, i18n */ - gtk_set_locale(); + setlocale(LC_ALL, ""); #ifdef ENABLE_NLS - bindtextdomain(PACKAGE, GQ_LOCALEDIR); + bindtextdomain(PACKAGE, gq_localedir); bind_textdomain_codeset(PACKAGE, "UTF-8"); textdomain(PACKAGE); #endif + /* Tee stderr to log window */ + if (pipe(fd_stderr) == 0) + { + if (dup2(fd_stderr[1], fileno(stderr)) != -1) + { + close(fd_stderr[1]); + stderr_channel = g_io_channel_unix_new(fd_stderr[0]); + g_io_add_watch(stderr_channel, G_IO_IN, (GIOFunc)stderr_channel_cb, NULL); + } + } + exif_init(); - - /* setup random seed for random slideshow */ - srand(time(NULL)); -#if 1 - log_printf("%s %s, This is a beta release.\n", GQ_APPNAME, VERSION); +#ifdef HAVE_LUA + lua_init(); #endif + /* setup random seed for random slideshow */ + srand(time(NULL)); + setup_sigbus_handler(); /* register global notify functions */ @@ -761,16 +1415,27 @@ gint main(gint argc, gchar *argv[]) file_data_register_notify_func(histogram_notify_cb, NULL, NOTIFY_PRIORITY_HIGH); file_data_register_notify_func(collect_manager_notify_cb, NULL, NOTIFY_PRIORITY_LOW); file_data_register_notify_func(metadata_notify_cb, NULL, NOTIFY_PRIORITY_LOW); - + gtkrc_load(); - -#ifdef HAVE_LIBCHAMPLAIN -#ifdef HAVE_LIBCHAMPLAIN_GTK - gtk_clutter_init(&argc, &argv); -#else - gtk_init(&argc, &argv); -#endif + + parse_command_line_for_debug_option(argc, argv); + DEBUG_1("%s main: gtk_init", get_exec_time()); +#ifdef HAVE_CLUTTER + if (parse_command_line_for_clutter_option(argc, argv)) + { + disable_clutter = TRUE; + gtk_init(&argc, &argv); + } + else + { + if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS) + { + log_printf("Can't initialize clutter-gtk.\nStart Geeqie with the option \"geeqie --disable-clutter\""); + runcmd("zenity --error --title=\"Geeqie\" --text \"Can't initialize clutter-gtk.\n\nStart Geeqie with the option:\n geeqie --disable-clutter\" --width=300"); + exit(1); + } + } #else gtk_init(&argc, &argv); #endif @@ -785,120 +1450,225 @@ gint main(gint argc, gchar *argv[]) log_printf("!!! %s may quit unexpectedly with a relocation error.\n", GQ_APPNAME); } + DEBUG_1("%s main: pixbuf_inline_register_stock_icons", get_exec_time()); pixbuf_inline_register_stock_icons(); - parse_command_line_for_debug_option(argc, argv); - parse_command_line(argc, argv); + DEBUG_1("%s main: setting default options before commandline handling", get_exec_time()); + options = init_options(NULL); + setup_default_options(options); + if (disable_clutter) + { + options->disable_gpu = TRUE; + } + + /* Generate a unique identifier used by the open archive function */ + instance_identifier = g_strdup_printf("%x", g_random_int()); + DEBUG_1("%s main: mkdir_if_not_exists", get_exec_time()); /* these functions don't depend on config file */ mkdir_if_not_exists(get_rc_dir()); mkdir_if_not_exists(get_collections_dir()); mkdir_if_not_exists(get_thumbnails_cache_dir()); mkdir_if_not_exists(get_metadata_cache_dir()); + mkdir_if_not_exists(get_window_layouts_dir()); setup_env_path(); - keys_load(); - accel_map_load(); + if (parse_command_line_for_cache_maintenance_option(argc, argv)) + { + process_command_line_for_cache_maintenance_option(argc, argv); + } + else + { + DEBUG_1("%s main: parse_command_line", get_exec_time()); + parse_command_line(argc, argv); - /* restore session from the config file */ + keys_load(); + accel_map_load(); - options = init_options(NULL); - setup_default_options(options); + /* restore session from the config file */ - if (!load_options(options)) - { - /* load_options calls these functions after it parses global options, we have to call it here if it fails */ - filter_add_defaults(); - filter_rebuild(); - editor_load_descriptions(); - } + DEBUG_1("%s main: load_options", get_exec_time()); + if (!load_options(options)) + { + /* load_options calls these functions after it parses global options, we have to call it here if it fails */ + filter_add_defaults(); + filter_rebuild(); + } - /* handle missing config file and commandline additions*/ - if (!layout_window_list) - { - /* broken or no config file */ - layout_new_from_config(NULL, NULL, TRUE); - } + #ifdef HAVE_CLUTTER + /** @FIXME For the background of this see: + * https://github.com/BestImageViewer/geeqie/issues/397 + * The feature CLUTTER_FEATURE_SWAP_EVENTS indictates if the + * system is liable to exhibit this problem. + * The user is provided with an override in Preferences/Behavior + */ + if (!options->override_disable_gpu && !options->disable_gpu) + { + DEBUG_1("CLUTTER_FEATURE_SWAP_EVENTS %d",clutter_feature_available(CLUTTER_FEATURE_SWAP_EVENTS)); + if (clutter_feature_available(CLUTTER_FEATURE_SWAP_EVENTS) != 0) + { + options->disable_gpu = TRUE; + } + } + #endif - if (command_line->collection_list && !command_line->startup_command_line_collection) - { - GList *work; + /* handle missing config file and commandline additions*/ + if (!layout_window_list) + { + /* broken or no config file or no section */ + layout_new_from_default(); + } - work = command_line->collection_list; - while (work) + layout_editors_reload_start(); + + /* If no --list option, open a separate collection window for each + * .gqv file on the command line + */ + if (command_line->collection_list && !command_line->startup_command_line_collection) { - CollectWindow *cw; - const gchar *path; + GList *work; - path = work->data; - work = work->next; + work = command_line->collection_list; + while (work) + { + CollectWindow *cw; + const gchar *path; - cw = collection_window_new(path); - if (!first_collection && cw) first_collection = cw->cd; - } - } + path = work->data; + work = work->next; - if (command_line->cmd_list || - (command_line->startup_command_line_collection && command_line->collection_list)) - { - GList *work; + cw = collection_window_new(path); + if (!first_collection && cw) first_collection = cw->cd; + } + } - if (command_line->startup_command_line_collection) + if (command_line->log_file) { - CollectWindow *cw; + gchar *pathl; + gchar *path = g_strdup(command_line->log_file); - cw = collection_window_new(""); - cd = cw->cd; + pathl = path_from_utf8(path); + command_line->ssi = secure_open(pathl); } - else + + /* If there is a files list on the command line and no --list option, + * check if they are all in the same folder + */ + if (command_line->cmd_list && !(command_line->startup_command_line_collection)) { - cd = collection_new(""); /* if we pass NULL, untitled counter is falsely increm. */ + GList *work; + gchar *path = NULL; + + work = command_line->cmd_list; + + while (work && single_dir) + { + gchar *dirname; + + dirname = g_path_get_dirname(work->data); + if (!path) + { + path = g_strdup(dirname); + } + else + { + if (g_strcmp0(path, dirname) != 0) + { + single_dir = FALSE; + } + } + g_free(dirname); + work = work->next; + } + g_free(path); } - g_free(cd->path); - cd->path = NULL; - g_free(cd->name); - cd->name = g_strdup(_("Command line")); + /* Files from multiple folders, or --list option given + * then open an unnamed collection and insert all files + */ + if ((command_line->cmd_list && !single_dir) || (command_line->startup_command_line_collection && command_line->cmd_list)) + { + GList *work; + CollectWindow *cw; + + cw = collection_window_new(NULL); + cd = cw->cd; - collection_path_changed(cd); + collection_path_changed(cd); - work = command_line->cmd_list; - while (work) + work = command_line->cmd_list; + while (work) + { + FileData *fd; + + fd = file_data_new_simple(work->data); + collection_add(cd, fd, FALSE); + file_data_unref(fd); + work = work->next; + } + + work = command_line->collection_list; + while (work) + { + collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND); + work = work->next; + } + + if (cd->list) layout_image_set_collection(NULL, cd, cd->list->data); + + /* mem leak, we never unref this collection when !startup_command_line_collection + * (the image view of the main window does not hold a ref to the collection) + * this is sort of unavoidable, for if it did hold a ref, next/back + * may not work as expected when closing collection windows. + * + * collection_unref(cd); + */ + + } + else if (first_collection) { - collection_add(cd, (FileData *)work->data, FALSE); - work = work->next; + layout_image_set_collection(NULL, first_collection, + collection_get_first(first_collection)); } - work = command_line->collection_list; - while (work) + /* If the files on the command line are from one folder, select those files + * unless it is a command line collection - then leave focus on collection window + */ + lw = NULL; + layout_valid(&lw); + + if (single_dir && command_line->cmd_list && !command_line->startup_command_line_collection) { - collection_load(cd, (gchar *)work->data, COLLECTION_LOAD_APPEND); - work = work->next; + GList *work; + GList *selected; + FileData *fd; + + selected = NULL; + work = command_line->cmd_list; + while (work) + { + fd = file_data_new_simple((gchar *)work->data); + selected = g_list_append(selected, fd); + file_data_unref(fd); + work = work->next; + } + layout_select_list(lw, selected); } - if (cd->list) layout_image_set_collection(NULL, cd, cd->list->data); + buf = g_build_filename(get_rc_dir(), ".command", NULL); + remote_connection = remote_server_init(buf, cd); + g_free(buf); - /* mem leak, we never unref this collection when !startup_command_line_collection - * (the image view of the main window does not hold a ref to the collection) - * this is sort of unavoidable, for if it did hold a ref, next/back - * may not work as expected when closing collection windows. - * - * collection_unref(cd); - */ + marks_load(); + } - } - else if (first_collection) - { - layout_image_set_collection(NULL, first_collection, - collection_get_first(first_collection)); - } + default_settings = gtk_settings_get_default(); + g_signal_connect(default_settings, "notify::gtk-theme-name", G_CALLBACK(theme_change_cb), NULL); + set_theme_bg_color(); - buf = g_build_filename(get_rc_dir(), ".command", NULL); - remote_connection = remote_server_init(buf, cd); - g_free(buf); - + DEBUG_1("%s main: gtk_main", get_exec_time()); gtk_main(); #ifdef HAVE_GTHREAD gdk_threads_leave();