diff --git a/src/collect.cc b/src/collect.cc index 82a53a17..abf92a61 100644 --- a/src/collect.cc +++ b/src/collect.cc @@ -204,20 +204,17 @@ GList *collection_list_sort(GList *list, SortType method) GList *collection_list_randomize(GList *list) { - guint random, length, i; + guint length, i; gpointer tmp; GList *nlist, *olist; length = g_list_length(list); if (!length) return NULL; - srand((unsigned int)time(NULL)); // Initialize random generator (hasn't to be that much strong) - for (i = 0; i < length; i++) { - random = (guint) (1.0 * length * rand()/(RAND_MAX + 1.0)); olist = g_list_nth(list, i); - nlist = g_list_nth(list, random); + nlist = g_list_nth(list, g_random_int_range(0, length)); tmp = olist->data; olist->data = nlist->data; nlist->data = tmp; diff --git a/src/exif-common.cc b/src/exif-common.cc index 950958c8..3e0856d4 100644 --- a/src/exif-common.cc +++ b/src/exif-common.cc @@ -211,7 +211,7 @@ static gchar *exif_build_formatted_DateTime(ExifData *exif) memset(&tm, 0, sizeof(tm)); /* Uh, strptime could let garbage in tm! */ if (text && strptime(text, "%Y:%m:%d %H:%M:%S", &tm)) { - buflen = strftime(buf, sizeof(buf), "%x %X", &tm); + buflen = strftime(buf, sizeof(buf), "%F %X", &tm); if (buflen > 0) { tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); @@ -262,7 +262,7 @@ static gchar *exif_build_formatted_DateTimeDigitized(ExifData *exif) memset(&tm, 0, sizeof(tm)); /* Uh, strptime could let garbage in tm! */ if (text && strptime(text, "%Y:%m:%d %H:%M:%S", &tm)) { - buflen = strftime(buf, sizeof(buf), "%x %X", &tm); + buflen = strftime(buf, sizeof(buf), "%F %X", &tm); if (buflen > 0) { tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); @@ -788,7 +788,7 @@ static gchar *exif_build_formatted_localtime(ExifData *exif) tm_local = localtime(&stamp); /* Convert to localtime using locale */ - buflen = strftime(buf, sizeof(buf), "%x %X", tm_local); + buflen = strftime(buf, sizeof(buf), "%F %X", tm_local); if (buflen > 0) { tmp = g_locale_to_utf8(buf, buflen, NULL, NULL, &error); diff --git a/src/filedata.cc b/src/filedata.cc index daaabf73..13611128 100644 --- a/src/filedata.cc +++ b/src/filedata.cc @@ -136,7 +136,7 @@ const gchar *text_from_time(time_t t) btime = localtime(&t); /* the %x warning about 2 digit years is not an error */ - buflen = strftime(buf, sizeof(buf), "%x %X", btime); + buflen = strftime(buf, sizeof(buf), "%F %X", btime); if (buflen < 1) return ""; g_free(ret); @@ -704,7 +704,9 @@ void file_data_dump() { list = g_hash_table_get_values(file_data_pool); +#ifdef DEBUG_FILEDATA log_printf("%d", global_file_data_count); +#endif log_printf("%d", g_list_length(list)); while (list) diff --git a/src/filedata.h b/src/filedata.h index 49a1476f..39992652 100644 --- a/src/filedata.h +++ b/src/filedata.h @@ -22,9 +22,7 @@ #ifndef FILEDATA_H #define FILEDATA_H -#ifdef DEBUG -#define DEBUG_FILEDATA -#endif +#undef DEBUG_FILEDATA #define FD_MAGICK 0x12345678u diff --git a/src/image-load-heif.cc b/src/image-load-heif.cc index c877e5a0..6d5eecbb 100644 --- a/src/image-load-heif.cc +++ b/src/image-load-heif.cc @@ -63,7 +63,7 @@ static gboolean image_loader_heif_load(gpointer loader, const guchar *buf, gsize error_code = heif_context_read_from_memory_without_copy(ctx, buf, count, NULL); if (error_code.code) { - log_printf("warning: heif reader error: %s\n", error_code.message); + log_printf("warning: heif (heif_context_read_from_memory_without_copy) reader error: %s\n", error_code.message); heif_context_free(ctx); return FALSE; } @@ -80,7 +80,7 @@ static gboolean image_loader_heif_load(gpointer loader, const guchar *buf, gsize error_code = heif_context_get_image_handle(ctx, IDs[ld->page_num], &handle); if (error_code.code) { - log_printf("warning: heif reader error: %s\n", error_code.message); + log_printf("warning: heif (heif_context_get_image_handle) reader error: %s\n", error_code.message); heif_context_free(ctx); return FALSE; } @@ -89,7 +89,7 @@ static gboolean image_loader_heif_load(gpointer loader, const guchar *buf, gsize error_code = heif_decode_image(handle, &img, heif_colorspace_RGB, heif_chroma_interleaved_24bit, NULL); if (error_code.code) { - log_printf("warning: heif reader error: %s\n", error_code.message); + log_printf("warning: heif (heif_decode_image) reader error: %s\n", error_code.message); heif_context_free(ctx); return FALSE; } diff --git a/src/image-load.cc b/src/image-load.cc index 247ffc5d..acf1be8b 100644 --- a/src/image-load.cc +++ b/src/image-load.cc @@ -1062,7 +1062,7 @@ static gboolean image_loader_setup_source(ImageLoader *il) return FALSE; } - il->mapped_file = mmap(0, il->bytes_total, PROT_READ|PROT_WRITE, MAP_PRIVATE, load_fd, 0); + il->mapped_file = mmap(0, il->bytes_total, PROT_READ, MAP_PRIVATE, load_fd, 0); close(load_fd); if (il->mapped_file == MAP_FAILED) { diff --git a/src/main.cc b/src/main.cc index b3cc6983..7b5b4642 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1304,10 +1304,6 @@ gint main(gint argc, gchar *argv[]) #ifdef HAVE_LUA lua_init(); #endif - - /* setup random seed for random slideshow */ - srand(time(NULL)); - #if 0 /* See later comment; this handler leads to UB. */ setup_sigbus_handler(); diff --git a/src/remote.cc b/src/remote.cc index 8a487cca..9fdc0ef1 100644 --- a/src/remote.cc +++ b/src/remote.cc @@ -922,6 +922,200 @@ static void get_filelist(const gchar *text, GIOChannel *channel, gboolean recurs file_data_unref(dir_fd); } +static void gr_get_selection(const gchar *UNUSED(text), GIOChannel *channel, gboolean UNUSED(data)) +{ + if (!layout_valid(&lw_id)) return; + + GList *selected = layout_selection_list(lw_id); // Keep copy to free. + GString *out_string = g_string_new(NULL); + + GList *work = selected; + while (work) + { + FileData *fd = work->data; + g_assert(fd->magick == FD_MAGICK); + + g_string_append_printf(out_string, "%s %s\n", + fd->path, + format_class_list[filter_file_get_class(fd->path)]); + + work = work->next; + } + + g_io_channel_write_chars(channel, out_string->str, -1, NULL, NULL); + g_io_channel_write_chars(channel, "", -1, NULL, NULL); + + filelist_free(selected); + g_string_free(out_string, TRUE); +} + +static void gr_selection_add(const gchar *text, GIOChannel *UNUSED(channel), gpointer UNUSED(data)) +{ + if (!layout_valid(&lw_id)) return; + + FileData *fd_to_select = NULL; + if (strcmp(text, "") == 0) + { + // No file specified, use current fd. + fd_to_select = layout_image_get_fd(lw_id); + } + else + { + // Search through the current file list for a file matching the specified path. + // "Match" is either a basename match or a file path match. + gchar *path = expand_tilde(text); + gchar *filename = g_path_get_basename(path); + gchar *slash_plus_filename = g_strdup_printf("%s%s", G_DIR_SEPARATOR_S, filename); + + GList *file_list = layout_list(lw_id); + for (GList *work = file_list; work && !fd_to_select; work = work->next) + { + FileData *fd = work->data; + if (!strcmp(path, fd->path) || g_str_has_suffix(fd->path, slash_plus_filename)) + { + fd_to_select = file_data_ref(fd); + continue; // will exit loop. + } + + for (GList *sidecar = fd->sidecar_files; sidecar && !fd_to_select; sidecar = sidecar->next) + { + FileData *side_fd = sidecar->data; + if (!strcmp(path, side_fd->path) + || g_str_has_suffix(side_fd->path, slash_plus_filename)) + { + fd_to_select = file_data_ref(side_fd); + continue; // will exit both nested loops. + } + } + } + + if (!fd_to_select) + { + log_printf("remote sent --selection-add filename that could not be found: \"%s\"\n", + filename); + } + + filelist_free(file_list); + g_free(slash_plus_filename); + g_free(filename); + g_free(path); + } + + if (fd_to_select) + { + GList *to_select = g_list_append(NULL, fd_to_select); + // Using the "_list" variant doesn't clear the existing selection. + layout_select_list(lw_id, to_select); + filelist_free(to_select); + } +} + +static void gr_selection_clear(const gchar *UNUSED(text), GIOChannel *UNUSED(channel), gpointer UNUSED(data)) +{ + layout_select_none(lw_id); // Checks lw_id validity internally. +} + +static void gr_selection_remove(const gchar *text, GIOChannel *UNUSED(channel), gpointer UNUSED(data)) +{ + if (!layout_valid(&lw_id)) return; + + GList *selected = layout_selection_list(lw_id); // Keep copy to free. + if (!selected) + { + log_printf("remote sent --selection-remove with empty selection."); + return; + } + + FileData *fd_to_deselect = NULL; + gchar *path = NULL; + gchar *filename = NULL; + gchar *slash_plus_filename = NULL; + if (strcmp(text, "") == 0) + { + // No file specified, use current fd. + fd_to_deselect = layout_image_get_fd(lw_id); + if (!fd_to_deselect) + { + log_printf("remote sent \"--selection-remove:\" with no current image"); + filelist_free(selected); + return; + } + } + else + { + // Search through the selection list for a file matching the specified path. + // "Match" is either a basename match or a file path match. + path = expand_tilde(text); + filename = g_path_get_basename(path); + slash_plus_filename = g_strdup_printf("%s%s", G_DIR_SEPARATOR_S, filename); + } + + GList *prior_link = NULL; // Stash base for link removal to avoid a second traversal. + GList *link_to_remove = NULL; + for (GList *work = selected; work; prior_link = work, work = work->next) + { + FileData *fd = work->data; + if (fd_to_deselect) + { + if (fd == fd_to_deselect) + { + link_to_remove = work; + break; + } + } + else + { + // path, filename, and slash_plus_filename should be defined. + + if (!strcmp(path, fd->path) || g_str_has_suffix(fd->path, slash_plus_filename)) + { + link_to_remove = work; + break; + } + } + } + + if (!link_to_remove) + { + if (fd_to_deselect) + { + log_printf("remote sent \"--selection-remove:\" but current image is not selected"); + } + else + { + log_printf("remote sent \"--selection-remove:%s\" but that filename is not selected", + filename); + } + } + else + { + if (link_to_remove == selected) + { + // Remove first link. + selected = g_list_remove_link(selected, link_to_remove); + filelist_free(link_to_remove); + link_to_remove = NULL; + } + else + { + // Remove a subsequent link. + prior_link = g_list_remove_link(prior_link, link_to_remove); + filelist_free(link_to_remove); + link_to_remove = NULL; + } + + // Re-select all but the deselected item. + layout_select_none(lw_id); + layout_select_list(lw_id, selected); + } + + filelist_free(selected); + file_data_unref(fd_to_deselect); + g_free(slash_plus_filename); + g_free(filename); + g_free(path); +} + static void gr_collection(const gchar *text, GIOChannel *channel, gpointer UNUSED(data)) { GString *contents = g_string_new(NULL); @@ -1430,6 +1624,7 @@ static RemoteCommandEntry remote_commands[] = { { NULL, "--get-filelist-recurse:", gr_filelist_recurse, TRUE, FALSE, N_("[]"), N_("get list of files and class recursive") }, { NULL, "--get-rectangle", gr_rectangle, FALSE, FALSE, NULL, N_("get rectangle co-ordinates") }, { NULL, "--get-render-intent", gr_render_intent, FALSE, FALSE, NULL, N_("get render intent") }, + { NULL, "--get-selection", gr_get_selection, FALSE, FALSE, NULL, N_("get list of selected files") }, { NULL, "--get-sidecars:", gr_get_sidecars, TRUE, FALSE, N_(""), N_("get list of sidecars of FILE") }, { NULL, "--id:", gr_lw_id, TRUE, FALSE, N_(""), N_("window id for following commands") }, { NULL, "--last", gr_image_last, FALSE, FALSE, NULL, N_("last image") }, @@ -1446,6 +1641,9 @@ static RemoteCommandEntry remote_commands[] = { { "-q", "--quit", gr_quit, FALSE, FALSE, NULL, N_("quit") }, { NULL, "--raise", gr_raise, FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") }, { NULL, "raise", gr_raise, FALSE, FALSE, NULL, N_("bring the Geeqie window to the top") }, + { NULL, "--selection-add:", gr_selection_add, TRUE, FALSE, N_("[]"), N_("adds the current file (or the specified file) to the current selection") }, + { NULL, "--selection-clear", gr_selection_clear, FALSE, FALSE, NULL, N_("clears the current selection") }, + { NULL, "--selection-remove:", gr_selection_remove, TRUE, FALSE, N_("[]"), N_("removes the current file (or the specified file) from the current selection") }, { "-s", "--slideshow", gr_slideshow_toggle, FALSE, TRUE, NULL, N_("toggle slide show") }, { NULL, "--slideshow-recurse:", gr_slideshow_start_rec, TRUE, FALSE, N_(""), N_("start recursive slide show in FOLDER") }, { "-ss","--slideshow-start", gr_slideshow_start, FALSE, FALSE, NULL, N_("start slide show") }, diff --git a/src/slideshow.cc b/src/slideshow.cc index d863785f..1d8fabcc 100644 --- a/src/slideshow.cc +++ b/src/slideshow.cc @@ -102,10 +102,9 @@ static void ptr_array_random_shuffle(GPtrArray *array) { guint i; for (i = 0; i < array->len; ++i) - { - guint p = (double)rand() / ((double)RAND_MAX + 1.0) * array->len; - swap(array, i, p); - } + { + swap(array, i, g_random_int_range(0, array->len)); + } } static GList *generate_random_list(SlideShowData *ss) diff --git a/src/thumb-standard.cc b/src/thumb-standard.cc index f90ae96d..abc57afd 100644 --- a/src/thumb-standard.cc +++ b/src/thumb-standard.cc @@ -32,6 +32,7 @@ #include "metadata.h" #include "color-man.h" +#include /** * @file @@ -231,9 +232,13 @@ static gboolean thumb_loader_std_fail_check(ThumbLoaderStd *tl) if (pixbuf) { const gchar *mtime_str; + const gchar *size_str; mtime_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_MTIME); - if (mtime_str && strtol(mtime_str, NULL, 10) == tl->source_mtime) + size_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_SIZE); + if (mtime_str && size_str && + strtoll(mtime_str, NULL, 10) == tl->source_mtime && + strtoll(size_str, NULL, 10) == tl->source_size) { result = TRUE; DEBUG_1("thumb fail valid: %s", tl->fd->path); @@ -255,7 +260,9 @@ static gboolean thumb_loader_std_validate(ThumbLoaderStd *tl, GdkPixbuf *pixbuf) const gchar *valid_uri; const gchar *uri; const gchar *mtime_str; + const gchar *size_str; time_t mtime; + off_t fsize; gint w, h; if (!pixbuf) return FALSE; @@ -270,12 +277,16 @@ static gboolean thumb_loader_std_validate(ThumbLoaderStd *tl, GdkPixbuf *pixbuf) uri = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_URI); mtime_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_MTIME); + size_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_SIZE); - if (!mtime_str || !uri || !valid_uri) return FALSE; + if (!mtime_str || !size_str || !uri || !valid_uri) return FALSE; if (strcmp(uri, valid_uri) != 0) return FALSE; - mtime = strtol(mtime_str, NULL, 10); - if (tl->source_mtime != mtime) return FALSE; + errno = 0; + mtime = strtoll(mtime_str, NULL, 10); + if (errno || tl->source_mtime != mtime) return FALSE; + fsize = strtoll(size_str, NULL, 10); + if (errno || tl->source_size != fsize) return FALSE; return TRUE; } @@ -344,6 +355,7 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf) const gchar *mark_uri; gchar *mark_app; gchar *mark_mtime; + gchar *mark_size; gchar *pathl; gboolean success; @@ -351,10 +363,12 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf) mark_app = g_strdup_printf("%s %s", GQ_APPNAME, VERSION); mark_mtime = g_strdup_printf("%llu", (unsigned long long)tl->source_mtime); + mark_size = g_strdup_printf("%llu", (unsigned long long)tl->source_size); pathl = path_from_utf8(tmp_path); success = gdk_pixbuf_save(pixbuf, pathl, "png", NULL, THUMB_MARKER_URI, mark_uri, THUMB_MARKER_MTIME, mark_mtime, + THUMB_MARKER_SIZE, mark_size, THUMB_MARKER_APP, mark_app, NULL); if (success) @@ -366,6 +380,7 @@ static void thumb_loader_std_save(ThumbLoaderStd *tl, GdkPixbuf *pixbuf) g_free(pathl); g_free(mark_mtime); + g_free(mark_size); g_free(mark_app); g_free(tmp_path); @@ -931,10 +946,13 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *UNUSED( { const gchar *uri; const gchar *mtime_str; + const gchar *size_str; uri = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_URI); mtime_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_MTIME); - if (uri && mtime_str) + size_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_SIZE); + + if (uri && mtime_str && size_str) { if (strncmp(uri, "file:", strlen("file:")) == 0) { @@ -943,7 +961,8 @@ static void thumb_loader_std_thumb_file_validate_done_cb(ThumbLoaderStd *UNUSED( target = g_filename_from_uri(uri, NULL, NULL); if (stat(target, &st) == 0 && - st.st_mtime == strtol(mtime_str, NULL, 10)) + st.st_mtime == strtoll(mtime_str, NULL, 10) && + st.st_size == strtoll(size_str, NULL, 10)) { valid = TRUE; } @@ -1098,11 +1117,13 @@ static void thumb_std_maint_move_validate_cb(const gchar *UNUSED(path), gboolean { const gchar *uri; const gchar *mtime_str; + const gchar *size_str; uri = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_URI); mtime_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_MTIME); + size_str = gdk_pixbuf_get_option(pixbuf, THUMB_MARKER_SIZE); - if (uri && mtime_str && strcmp(uri, tm->source_uri) == 0) + if (uri && mtime_str && size_str && strcmp(uri, tm->source_uri) == 0) { gchar *pathl; @@ -1116,7 +1137,8 @@ static void thumb_std_maint_move_validate_cb(const gchar *UNUSED(path), gboolean tm->tl->cache_local = FALSE; file_data_unref(tm->tl->fd); tm->tl->fd = file_data_new_group(tm->dest); - tm->tl->source_mtime = strtol(mtime_str, NULL, 10); + tm->tl->source_mtime = strtoll(mtime_str, NULL, 10); + tm->tl->source_size = strtoll(size_str, NULL, 10); pathl = path_from_utf8(tm->tl->fd->path); g_free(tm->tl->thumb_uri);