From bfab3a76a420eada64d8fcddea7f5ead4819408f Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 29 Jun 2023 23:40:52 +0000 Subject: [PATCH 01/27] config.mk: update openbsd configuration (#453) based on the openbsd port: https://github.com/openbsd/ports/blob/cf3cdaf09214bf85907dc9147bab7da77e0281d8/graphics/nsxiv/Makefile Tested-by: Seninha Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/453 --- config.mk | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/config.mk b/config.mk index 20bb2b2..7ffb08d 100644 --- a/config.mk +++ b/config.mk @@ -35,8 +35,8 @@ ICONS = 16x16.png 32x32.png 48x48.png 64x64.png 128x128.png # Uncomment on OpenBSD # HAVE_INOTIFY = 0 # lib_fonts_bsd_0 = -# lib_fonts_bsd_1 = -lfreetype +# lib_fonts_bsd_1 = -lfreetype -L/usr/X11R6/lib/freetype2 # inc_fonts_bsd_0 = # inc_fonts_bsd_1 = -I/usr/X11R6/include/freetype2 -# LDLIBS = -lz -L/usr/local/lib -L/usr/X11R6/lib $(lib_fonts_bsd_$(HAVE_LIBFONTS)) -# CPPFLAGS = -I/usr/local/include -I/usr/X11R6/include $(inc_fonts_bsd_$(HAVE_LIBFONTS)) +# CPPFLAGS = -I/usr/X11R6/include -I/usr/local/include $(inc_fonts_bsd_$(HAVE_LIBFONTS)) +# LDLIBS = -L/usr/X11R6/lib -L/usr/local/lib $(lib_fonts_bsd_$(HAVE_LIBFONTS)) From 9b122c82fac0f27f8fbde22b818cfa8befaaf405 Mon Sep 17 00:00:00 2001 From: NRK Date: Sat, 8 Jul 2023 06:53:57 +0000 Subject: [PATCH 02/27] add compiled features into --version output (#462) a lot of application which allow selecting features at build time seem to output the build config with `--version` or similar (e.g ffmpeg). aside from giving the user information about the feature set the binary was compiled with (in case the user didn't compile it themselves, e.g on a binary distro) it can also (possibly) help when submitting bug reports. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/462 Reviewed-by: explosion-mental --- image.c | 9 --------- nsxiv.h | 9 +++++++++ options.c | 21 +++++++++++++++++++++ 3 files changed, 30 insertions(+), 9 deletions(-) diff --git a/image.c b/image.c index 426a05a..e7e4455 100644 --- a/image.c +++ b/image.c @@ -33,15 +33,6 @@ #include #endif -#ifdef IMLIB2_VERSION - #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 8, 0) - #define HAVE_IMLIB2_MULTI_FRAME 1 - #endif -#endif -#ifndef HAVE_IMLIB2_MULTI_FRAME - #define HAVE_IMLIB2_MULTI_FRAME 0 -#endif - #if HAVE_LIBGIF && !HAVE_IMLIB2_MULTI_FRAME #include enum { DEF_GIF_DELAY = 75 }; diff --git a/nsxiv.h b/nsxiv.h index 7e373c2..574ab1a 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -159,6 +159,15 @@ typedef keymap_t button_t; /* image.c */ +#ifdef IMLIB2_VERSION + #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 8, 0) + #define HAVE_IMLIB2_MULTI_FRAME 1 + #endif +#endif +#ifndef HAVE_IMLIB2_MULTI_FRAME + #define HAVE_IMLIB2_MULTI_FRAME 0 +#endif + typedef struct { Imlib_Image im; unsigned int delay; diff --git a/options.c b/options.c index ef874ac..89dfe20 100644 --- a/options.c +++ b/options.c @@ -50,6 +50,27 @@ void print_usage(void) static void print_version(void) { printf("%s %s\n", progname, VERSION); + fputs("features: " +#if HAVE_INOTIFY + "+inotify " +#endif +#if HAVE_LIBFONTS + "+statusbar " +#endif +#if HAVE_LIBEXIF + "+exif " +#endif +#if HAVE_IMLIB2_MULTI_FRAME + "+multiframe " +#else + #if HAVE_LIBGIF + "+giflib " + #endif + #if HAVE_LIBWEBP + "+libwebp " + #endif +#endif + "\n", stdout); } void parse_options(int argc, char **argv) From c1b36b8f0942a8ec34f97f28c8c77c84adf0c2c4 Mon Sep 17 00:00:00 2001 From: NRK Date: Sat, 8 Jul 2023 07:05:57 +0000 Subject: [PATCH 03/27] add some upgrade comments (#457) these document improvements and/or removal of unnecessary code for when we will require a higher minimum version of Imlib2. all the comments have been prefixed with "UPGRADE: " for easy grepping. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/457 Reviewed-by: explosion-mental --- image.c | 3 +++ nsxiv.h | 2 +- thumbs.c | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/image.c b/image.c index e7e4455..e8fc2a9 100644 --- a/image.c +++ b/image.c @@ -563,6 +563,8 @@ Imlib_Image img_open(const fileinfo_t *file) { imlib_context_set_image(im); } + /* UPGRADE: Imlib2 v1.10.0: better error reporting with + * imlib_get_error() + imlib_strerror() */ if (im == NULL && (file->flags & FF_WARN)) error(0, 0, "%s: Error opening image", file->name); return im; @@ -581,6 +583,7 @@ bool img_load(img_t *img, const fileinfo_t *file) */ imlib_image_set_changes_on_disk(); +/* UPGRADE: Imlib2 v1.7.5: remove these exif related ifdefs */ /* since v1.7.5, Imlib2 can parse exif orientation from jpeg files. * this version also happens to be the first one which defines the * IMLIB2_VERSION macro. diff --git a/nsxiv.h b/nsxiv.h index 574ab1a..3ff90b2 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -159,7 +159,7 @@ typedef keymap_t button_t; /* image.c */ -#ifdef IMLIB2_VERSION +#ifdef IMLIB2_VERSION /* UPGRADE: Imlib2 v1.8.0: remove all HAVE_IMLIB2_MULTI_FRAME ifdefs */ #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 8, 0) #define HAVE_IMLIB2_MULTI_FRAME 1 #endif diff --git a/thumbs.c b/thumbs.c index c1778b5..d5a6c6b 100644 --- a/thumbs.c +++ b/thumbs.c @@ -268,6 +268,7 @@ bool tns_load(tns_t *tns, int n, bool force, bool cache_only) char tmppath[] = "/tmp/nsxiv-XXXXXX"; Imlib_Image tmpim; + /* UPGRADE: Imlib2 v1.10.0: avoid tempfile and use imlib_load_image_mem() */ if ((ed = exif_data_new_from_file(file->path)) != NULL) { if (ed->data != NULL && ed->size > 0 && (tmpfd = mkstemp(tmppath)) >= 0) From 112ba3a2fc934113c37e0ad274eaeea13d8c3359 Mon Sep 17 00:00:00 2001 From: NRK Date: Sun, 16 Jul 2023 18:16:23 +0000 Subject: [PATCH 04/27] config.mk: do not explicitly set CC (#455) 1. `c99` doesn't exist on openbsd 2. `c99` behaves weirdly on macos (doesn't support -Wall) since make will already set CC (required by POSIX if the implementation supports C, see `man 1p make`) to something appropriate (or it might be set in the environment) go ahead and remove explicitly setting it on our end. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/455 Reviewed-by: TAAPArthur --- .github/workflows/build.yml | 4 ++-- config.mk | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3c3b3c1..97a7c65 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,7 @@ jobs: - name: build run: | # libinotify-kqueue isn't available on homebrew - make clean && make -s CC=gcc OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 + make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 # force uninstallation with --ignore-dependencies brew uninstall --ignore-dependencies libxft libexif giflib webp - make clean && make -s CC=gcc OPT_DEP_DEFAULT=0 + make clean && make -s OPT_DEP_DEFAULT=0 diff --git a/config.mk b/config.mk index 7ffb08d..fb28086 100644 --- a/config.mk +++ b/config.mk @@ -21,8 +21,6 @@ HAVE_LIBEXIF = $(OPT_DEP_DEFAULT) HAVE_LIBGIF = $(OPT_DEP_DEFAULT) HAVE_LIBWEBP = $(OPT_DEP_DEFAULT) -# Compiler and linker -CC = c99 # CFLAGS, any additional compiler flags goes here CFLAGS = -Wall -pedantic -O2 -DNDEBUG # Uncomment for a debug build using gcc/clang From cc132dd365105bb80cf683a0ce1b11d038db1c25 Mon Sep 17 00:00:00 2001 From: padv Date: Tue, 22 Aug 2023 18:08:29 +0000 Subject: [PATCH 05/27] fix: brightness keybindings on manpage (#467) Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/467 Reviewed-by: NRK Reviewed-by: explosion-mental Co-authored-by: padv Co-committed-by: padv --- etc/nsxiv.1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/nsxiv.1 b/etc/nsxiv.1 index e2fb4c8..9b5c243 100644 --- a/etc/nsxiv.1 +++ b/etc/nsxiv.1 @@ -219,12 +219,12 @@ steps. .B Ctrl-g Reset gamma correction. .TP -.B [ +.B Ctrl-[ Decrease brightness correction by .I count steps. .TP -.B ] +.B Ctrl-] Increase brightness correction by .I count steps. From 10a6228538a58bf40995cb7d57d47ef6ca93767d Mon Sep 17 00:00:00 2001 From: David Gowers Date: Mon, 28 Aug 2023 10:28:57 +0000 Subject: [PATCH 06/27] set autoreload timeout based on the latest event (#459) currently the autoreload feature of nsxiv is a bit unreliable because we try to load at the very first event we received. however, the writer might not be done writing and so we might try to load a truncated image (and fail). in the following ascii diagram, function S represents sleep and `+` sign represents writes by the writer. because we set the sleep (of 10ms) at the first event, subsequent writes by the writer doesn't influence our reload logic: S(10) load() nsxiv | | writer + + + + (done) time(ms): 00 05 10 15 after this patch, (assuming function T (re)sets a timeout), we will keep (re)setting a timeout on new events giving the writer more time to finish: T(10) T(10) T(10) T(10) load() nsxiv | | | | | writer + + + + (done) time(ms): 00 05 10 15 20 25 while this patch makes things significantly more robust, the problem here is inherently unsolvable since there's no way to tell whether the writer is done writing or not. for example, if user does something like `curl 'some.png' > test.png` then curl might stop for a second or two in the middle of writing due to internet issues - which will make nsxiv drop the image. this patch also increases the autoreload delay from 10ms to now 128ms instead to decrease chances of false failures. ref: https://github.com/0ion9/sxiv/commit/6ae2df6ed549c2cc119bd7d235b75154fc042d2d partially-fixes: https://codeberg.org/nsxiv/nsxiv/issues/456 commit-message-by: NRK Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/459 Reviewed-by: eylles --- image.c | 1 + main.c | 33 +++++++++++++++++++++++---------- nsxiv.h | 2 ++ 3 files changed, 26 insertions(+), 10 deletions(-) diff --git a/image.c b/image.c index e8fc2a9..7304f4a 100644 --- a/image.c +++ b/image.c @@ -87,6 +87,7 @@ void img_init(img_t *img, win_t *win) img->dirty = false; img->anti_alias = options->anti_alias; img->alpha_layer = options->alpha_layer; + img->autoreload_pending = false; img->multi.cap = img->multi.cnt = 0; img->multi.animate = options->animate; img->multi.framedelay = options->framerate > 0 ? 1000 / options->framerate : 0; diff --git a/main.c b/main.c index fed9528..2514237 100644 --- a/main.c +++ b/main.c @@ -22,6 +22,7 @@ #include "commands.h" #include "config.h" +#include #include #include #include @@ -72,6 +73,8 @@ int prefix; bool title_dirty; const XButtonEvent *xbutton_ev; +static void autoreload(void); + static bool extprefix; static bool resized = false; @@ -91,6 +94,7 @@ static struct { struct timeval when; bool active; } timeouts[] = { + { autoreload }, { redraw }, { reset_cursor }, { slideshow }, @@ -269,6 +273,18 @@ static bool check_timeouts(int *t) return tmin > 0; } +static void autoreload(void) +{ + if (img.autoreload_pending) { + img_close(&img, true); + /* load_image() sets autoreload_pending to false */ + load_image(fileidx); + redraw(); + } else { + assert(!"unreachable"); + } +} + static void kill_close(pid_t pid, int *fd) { if (fd != NULL && *fd != -1) { @@ -367,10 +383,13 @@ void load_image(int new) if (win.xwin != None) win_set_cursor(&win, CURSOR_WATCH); + reset_timeout(autoreload); reset_timeout(slideshow); - if (new != current) + if (new != current) { alternate = current; + img.autoreload_pending = false; + } img_close(&img, false); while (!img_load(&img, &files[new])) { @@ -735,7 +754,6 @@ static void run(void) enum { FD_X, FD_INFO, FD_TITLE, FD_ARL, FD_CNT }; struct pollfd pfd[FD_CNT]; int timeout = 0; - const struct timespec ten_ms = { 0, 10000000 }; bool discard, init_thumb, load_thumb, to_set; XEvent ev, nextev; @@ -775,14 +793,9 @@ static void run(void) read_info(); if (pfd[FD_TITLE].revents & POLLIN) read_title(); - if (pfd[FD_ARL].revents & POLLIN) { - if (arl_handle(&arl)) { - /* when too fast, imlib2 can't load the image */ - nanosleep(&ten_ms, NULL); - img_close(&img, true); - load_image(fileidx); - redraw(); - } + if ((pfd[FD_ARL].revents & POLLIN) && arl_handle(&arl)) { + img.autoreload_pending = true; + set_timeout(autoreload, TO_AUTORELOAD, true); } } continue; diff --git a/nsxiv.h b/nsxiv.h index 3ff90b2..e68b215 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -107,6 +107,7 @@ typedef struct { /* timeouts in milliseconds: */ enum { + TO_AUTORELOAD = 128, TO_REDRAW_RESIZE = 75, TO_REDRAW_THUMBS = 200, TO_CURSOR_HIDE = 1200, @@ -204,6 +205,7 @@ struct img { bool dirty; bool anti_alias; bool alpha_layer; + bool autoreload_pending; struct { bool on; From d19924f288f049228dfc691ccdf13721be3c98f9 Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 31 Aug 2023 18:06:43 +0000 Subject: [PATCH 07/27] various README nits (#470) don't capitalize "berg" in "Codeberg" spelling. Codeberg's official sites does not seem to do so. switch from "GPLv2" to "GPL-2.0-or-later" according to the spdx short identifier: https://spdx.org/licenses/GPL-2.0-or-later.html explicitly mention that Imlib2 needs to be built with X11 support. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/470 Reviewed-by: explosion-mental --- .github/workflows/lock.yml | 4 ++-- README.md | 14 +++++++------- etc/CHANGELOG.md | 4 ++-- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/.github/workflows/lock.yml b/.github/workflows/lock.yml index cf187d4..eef34e1 100644 --- a/.github/workflows/lock.yml +++ b/.github/workflows/lock.yml @@ -15,7 +15,7 @@ jobs: with: pr-comment: > Hi, thanks for the Pull-Request. - However this repository is a read-only mirror, main development of nsxiv happens over at [CodeBerg](https://codeberg.org/nsxiv/nsxiv). - Please open your Pull-Request over on the CodeBerg Repo. + However this repository is a read-only mirror, main development of nsxiv happens over at [Codeberg](https://codeberg.org/nsxiv/nsxiv). + Please open your Pull-Request over on the Codeberg Repo. Otherwise you may also e-mail the patch (obtained via `git format-patch`) to any of the [active maintainers](https://nsxiv.codeberg.page/man/#CURRENT%20MAINTAINERS) instead. diff --git a/README.md b/README.md index 70376d7..3a3b2d1 100644 --- a/README.md +++ b/README.md @@ -1,8 +1,8 @@ [![nsxiv](https://codeberg.org/nsxiv/pages/raw/branch/master/img/logo.png)](https://codeberg.org/nsxiv/nsxiv) -[![CodeBerg](https://img.shields.io/badge/Hosted_at-Codeberg-%232185D0?style=flat-square&logo=CodeBerg)](https://codeberg.org/nsxiv/nsxiv) +[![Codeberg](https://img.shields.io/badge/Hosted_at-Codeberg-%232185D0?style=flat-square&logo=CodeBerg)](https://codeberg.org/nsxiv/nsxiv) [![tags](https://img.shields.io/github/v/tag/nsxiv/nsxiv?style=flat-square)](https://codeberg.org/nsxiv/nsxiv/tags) -[![license](https://img.shields.io/badge/license-GPL--2.0-lightgreen?style=flat-square)](https://codeberg.org/nsxiv/nsxiv/src/branch/master/LICENSE) +[![license](https://img.shields.io/badge/license-GPL--2.0%2B-lightgreen?style=flat-square)](https://codeberg.org/nsxiv/nsxiv/src/branch/master/LICENSE) [![loc](https://img.shields.io/tokei/lines/github/nsxiv/nsxiv?color=red&style=flat-square)](https://codeberg.org/nsxiv/nsxiv) **Neo (or New or Not) Simple (or Small or Suckless) X Image Viewer** @@ -11,10 +11,10 @@ nsxiv is a fork of the now-unmaintained [sxiv](https://github.com/xyb3rt/sxiv) with the purpose of being a (mostly) drop-in replacement for sxiv, maintaining its interface and adding simple, sensible features. nsxiv is free software licensed -under GPLv2 and aims to be easy to modify and customize. +under GPL-2.0-or-later and aims to be easy to modify and customize. Please file a bug report if something does not work as documented or expected on -[Codeberg] after making sure you are using the latest release. Contributions +[Codeberg] after making sure you are using the latest release. Contributions are welcome, see [CONTRIBUTING] to get started. [Codeberg]: https://codeberg.org/nsxiv/nsxiv/issues/new @@ -64,10 +64,10 @@ Dependencies nsxiv requires the following software to be installed: - * Imlib2 * X11 + * Imlib2 (built with X11 support) -The following dependencies are optional. +The following dependencies are optional: * `inotify`\*: Used for auto-reloading images on change. Disabled via `HAVE_INOTIFY=0`. @@ -206,7 +206,7 @@ Download -------- You can [browse](https://codeberg.org/nsxiv/nsxiv) the source code repository -on CodeBerg or get a copy using git with the following command: +on Codeberg or get a copy using git with the following command: $ git clone https://codeberg.org/nsxiv/nsxiv.git diff --git a/etc/CHANGELOG.md b/etc/CHANGELOG.md index 5ea1259..f89cc95 100644 --- a/etc/CHANGELOG.md +++ b/etc/CHANGELOG.md @@ -57,11 +57,11 @@ there may be more changes. Please use `git log` to view them. - - - ***SPECIAL NOTE***: Due to [this incident](https://codeberg.org/nsxiv/nsxiv-tmp/issues/1) -we have moved development over to [CodeBerg](https://codeberg.org/nsxiv/nsxiv). +we have moved development over to [Codeberg](https://codeberg.org/nsxiv/nsxiv). A lot of the references *below* may now be 404 on GitHub. Any threads which survived the wipe have been migrated over to [nsxiv-record]. All of the -references *above* can be found on the new main nsxiv repository on CodeBerg. +references *above* can be found on the new main nsxiv repository on Codeberg. [nsxiv-record]: https://codeberg.org/nsxiv/nsxiv-record/ From 69d4957a92030bf89323d9174c45263727483de4 Mon Sep 17 00:00:00 2001 From: NRK Date: Sun, 2 Jul 2023 19:16:55 +0600 Subject: [PATCH 08/27] remove faulty assertions these assertions did not hold true in practice when the underlying file ends up changing during load. --- image.c | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/image.c b/image.c index 7304f4a..4e7d645 100644 --- a/image.c +++ b/image.c @@ -490,9 +490,22 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file) bool has_alpha; imlib_context_set_image(m->cnt < 1 ? blank : m->frames[m->cnt - 1].im); - if ((canvas = imlib_clone_image()) == NULL || - (frame = imlib_load_image_frame(file->path, n)) == NULL) + canvas = imlib_clone_image(); + if ((frame = imlib_load_image_frame(file->path, n)) != NULL) { + imlib_context_set_image(frame); + imlib_image_set_changes_on_disk(); /* see img_load() for rationale */ + imlib_image_get_frame_info(&finfo); + } + /* NOTE: the underlying file can end up changing during load. + * so check if frame_count, w, h are all still the same or not. + */ + if (canvas == NULL || frame == NULL || finfo.frame_count != (int)fcnt || + finfo.canvas_w != img->w || finfo.canvas_h != img->h) { + if (frame != NULL) { + imlib_context_set_image(frame); + imlib_free_image(); + } if (canvas != NULL) { imlib_context_set_image(canvas); imlib_free_image(); @@ -501,11 +514,6 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file) break; } - imlib_context_set_image(frame); - imlib_image_set_changes_on_disk(); /* see img_load() for rationale */ - imlib_image_get_frame_info(&finfo); - assert(finfo.frame_count == (int)fcnt); - assert(finfo.canvas_w == img->w && finfo.canvas_h == img->h); sx = finfo.frame_x; sy = finfo.frame_y; sw = finfo.frame_w; From 53a43cb38838bd65512b3b8902cc8a3ecfaaca56 Mon Sep 17 00:00:00 2001 From: NRK Date: Sun, 2 Jul 2023 20:15:53 +0600 Subject: [PATCH 09/27] introduce img_free() this removes some repetitive code. --- image.c | 40 +++++++++++++++++----------------------- nsxiv.h | 1 + thumbs.c | 23 ++++++----------------- 3 files changed, 24 insertions(+), 40 deletions(-) diff --git a/image.c b/image.c index 4e7d645..a8b91a0 100644 --- a/image.c +++ b/image.c @@ -148,12 +148,10 @@ void exif_auto_orientate(const fileinfo_t *file) static void img_multiframe_context_set(img_t *img) { if (img->multi.cnt > 1) { - imlib_context_set_image(img->im); - imlib_free_image(); + img_free(img->im, false); img->im = img->multi.frames[0].im; } else if (img->multi.cnt == 1) { - imlib_context_set_image(img->multi.frames[0].im); - imlib_free_image(); + img_free(img->multi.frames[0].im, false); img->multi.cnt = 0; } @@ -502,14 +500,8 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file) if (canvas == NULL || frame == NULL || finfo.frame_count != (int)fcnt || finfo.canvas_w != img->w || finfo.canvas_h != img->h) { - if (frame != NULL) { - imlib_context_set_image(frame); - imlib_free_image(); - } - if (canvas != NULL) { - imlib_context_set_image(canvas); - imlib_free_image(); - } + img_free(frame, false); + img_free(canvas, false); error(0, 0, "%s: failed to load frame %d", file->name, n); break; } @@ -546,11 +538,9 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file) m->frames[m->cnt].delay = finfo.frame_delay ? finfo.frame_delay : DEF_ANIM_DELAY; m->length += m->frames[m->cnt].delay; m->cnt++; - imlib_context_set_image(frame); - imlib_free_image(); + img_free(frame, false); } - imlib_context_set_image(blank); - imlib_free_image(); + img_free(blank, false); img_multiframe_context_set(img); imlib_context_set_color_modifier(img->cmod); /* restore cmod */ return m->cnt > 0; @@ -632,21 +622,25 @@ bool img_load(img_t *img, const fileinfo_t *file) return true; } +CLEANUP void img_free(Imlib_Image im, bool decache) +{ + if (im != NULL) { + imlib_context_set_image(im); + decache ? imlib_free_image_and_decache() : imlib_free_image(); + } +} + CLEANUP void img_close(img_t *img, bool decache) { unsigned int i; - void (*free_img)(void) = decache ? imlib_free_image_and_decache : imlib_free_image; if (img->multi.cnt > 0) { - for (i = 0; i < img->multi.cnt; i++) { - imlib_context_set_image(img->multi.frames[i].im); - free_img(); - } + for (i = 0; i < img->multi.cnt; i++) + img_free(img->multi.frames[i].im, decache); img->multi.cnt = 0; img->im = NULL; } else if (img->im != NULL) { - imlib_context_set_image(img->im); - free_img(); + img_free(img->im, decache); img->im = NULL; } } diff --git a/nsxiv.h b/nsxiv.h index e68b215..949fc4b 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -217,6 +217,7 @@ struct img { void img_init(img_t*, win_t*); bool img_load(img_t*, const fileinfo_t*); +CLEANUP void img_free(Imlib_Image, bool); CLEANUP void img_close(img_t*, bool); void img_render(img_t*); bool img_fit_win(img_t*, scalemode_t); diff --git a/thumbs.c b/thumbs.c index d5a6c6b..d2f91a7 100644 --- a/thumbs.c +++ b/thumbs.c @@ -179,12 +179,8 @@ CLEANUP void tns_free(tns_t *tns) int i; if (tns->thumbs != NULL) { - for (i = 0; i < *tns->cnt; i++) { - if (tns->thumbs[i].im != NULL) { - imlib_context_set_image(tns->thumbs[i].im); - imlib_free_image(); - } - } + for (i = 0; i < *tns->cnt; i++) + img_free(tns->thumbs[i].im, false); free(tns->thumbs); tns->thumbs = NULL; } @@ -233,12 +229,8 @@ bool tns_load(tns_t *tns, int n, bool force, bool cache_only) return false; t = &tns->thumbs[n]; - - if (t->im != NULL) { - imlib_context_set_image(t->im); - imlib_free_image(); - t->im = NULL; - } + img_free(t->im, false); + t->im = NULL; if (!force) { if ((im = tns_cache_load(file->path, &force)) != NULL) { @@ -363,11 +355,8 @@ void tns_unload(tns_t *tns, int n) assert(n >= 0 && n < *tns->cnt); t = &tns->thumbs[n]; - if (t->im != NULL) { - imlib_context_set_image(t->im); - imlib_free_image(); - t->im = NULL; - } + img_free(t->im, false); + t->im = NULL; } static void tns_check_view(tns_t *tns, bool scrolled) From 80a71315de2bec9924f2e06a825d4400f2b5a4f8 Mon Sep 17 00:00:00 2001 From: NRK Date: Tue, 4 Jul 2023 11:10:07 +0600 Subject: [PATCH 10/27] fix: re-loading stale multi-frame images 4b67816 only partially fixed this issue. because imlib2's cache may not have sub-second granularity, there still exists a time-frame while the `mtime` has not yet been updated but we might be trying to reload the image due to receiving an inotify event in which case imlib2 will end up giving us the old frame. imlib2 v1.12.0 adds a function that allows us to decache any frames associated with a filename. this allows us to invalidate the cache manually instead of relying on `mtime`. but if that's not available due to older imlib2, then forcefully reload the raw frames and decache them. this has the unfortunate cost that if `mtime` *was* updated properly then we'll end up loading that image twice. fixes: https://codeberg.org/nsxiv/nsxiv/issues/456 --- image.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/image.c b/image.c index a8b91a0..5e2a7a0 100644 --- a/image.c +++ b/image.c @@ -637,6 +637,22 @@ CLEANUP void img_close(img_t *img, bool decache) if (img->multi.cnt > 0) { for (i = 0; i < img->multi.cnt; i++) img_free(img->multi.frames[i].im, decache); + /* NOTE: the above only decaches the "composed frames", + * and not the "raw frame" that's associated with the file. + * which leads to issues like: https://codeberg.org/nsxiv/nsxiv/issues/456 + */ +#if HAVE_IMLIB2_MULTI_FRAME + #if IMLIB2_VERSION >= IMLIB2_VERSION_(1, 12, 0) + if (decache) + imlib_image_decache_file(files[fileidx].path); + #else /* UPGRADE: Imlib2 v1.12.0: remove this hack */ + /* HACK: try to reload all the frames and forcefully decache them + * if imlib_image_decache_file() isn't available. + */ + for (i = 0; decache && i < img->multi.cnt; i++) + img_free(imlib_load_image_frame(files[fileidx].path, i + 1), true); + #endif +#endif img->multi.cnt = 0; img->im = NULL; } else if (img->im != NULL) { From 3659361e76c5c994bee3467787c00e89780cccbc Mon Sep 17 00:00:00 2001 From: NRK Date: Sat, 30 Sep 2023 08:53:32 +0000 Subject: [PATCH 11/27] centralize script handling logic (#477) currently the logic of when to open/close script is scattered around the entire code-base which is both ugly and error-prone. this patch centralizes script handling by remembering the relevant information on each redraw and then comparing it with the previous information to figure out whether something changed or not. this also fixes a bug where scripts weren't being called in thumbnail mode when mouse was used for selecting a different image. Closes: https://codeberg.org/nsxiv/nsxiv/issues/475 Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/477 Reviewed-by: eylles --- commands.c | 10 +--------- image.c | 4 ++-- main.c | 34 +++++++++++++++++++++++++--------- nsxiv.h | 1 - thumbs.c | 2 -- 5 files changed, 28 insertions(+), 23 deletions(-) diff --git a/commands.c b/commands.c index 97cadf2..3257b1e 100644 --- a/commands.c +++ b/commands.c @@ -83,9 +83,6 @@ bool cg_switch_mode(arg_t _) load_image(fileidx); mode = MODE_IMAGE; } - close_info(); - open_info(); - title_dirty = true; return true; } @@ -415,12 +412,7 @@ bool ci_slideshow(arg_t _) bool ct_move_sel(arg_t dir) { - bool dirty = tns_move_selection(&tns, dir, prefix); - if (dirty) { - close_info(); - open_info(); - } - return dirty; + return tns_move_selection(&tns, dir, prefix); } bool ct_reload_all(arg_t _) diff --git a/image.c b/image.c index 5e2a7a0..6020e93 100644 --- a/image.c +++ b/image.c @@ -717,7 +717,7 @@ static bool img_fit(img_t *img) if (ABS(img->zoom - z) > 1.0 / MAX(img->w, img->h)) { img->zoom = z; - img->dirty = title_dirty = true; + img->dirty = true; return true; } else { return false; @@ -852,7 +852,7 @@ bool img_zoom_to(img_t *img, float z) img->y = y - (y - img->y) * z / img->zoom; img->zoom = z; img->scalemode = SCALE_ZOOM; - img->dirty = img->checkpan = title_dirty = true; + img->dirty = img->checkpan = true; return true; } else { return false; diff --git a/main.c b/main.c index 2514237..a050aa8 100644 --- a/main.c +++ b/main.c @@ -70,7 +70,6 @@ int alternate; int markcnt; int markidx; int prefix; -bool title_dirty; const XButtonEvent *xbutton_ev; static void autoreload(void); @@ -316,7 +315,7 @@ static void open_title(void) char *argv[8]; char w[12] = "", h[12] = "", z[12] = "", fidx[12], fcnt[12]; - if (wintitle.f.err || !title_dirty) + if (wintitle.f.err) return; close_title(); @@ -331,7 +330,6 @@ static void open_title(void) fidx, fcnt, w, h, z, NULL); if ((wintitle.pid = spawn(&wintitle.fd, NULL, argv)) > 0) fcntl(wintitle.fd, F_SETFL, O_NONBLOCK); - title_dirty = false; } void close_info(void) @@ -402,10 +400,7 @@ void load_image(int new) files[new].flags &= ~FF_WARN; fileidx = current = new; - close_info(); - open_info(); arl_add(&arl, files[fileidx].path); - title_dirty = true; if (img.multi.cnt > 0 && img.multi.animate) set_timeout(animate, img.multi.frames[img.multi.sel].delay, true); @@ -443,9 +438,33 @@ static void update_info(void) const char *mark; win_bar_t *l = &win.bar.l, *r = &win.bar.r; + static struct { + const char *filepath; + int fileidx; + float zoom; + appmode_t mode; + } prev; + + if (prev.fileidx != fileidx || prev.mode != mode || + (prev.filepath == NULL || !STREQ(prev.filepath, files[fileidx].path))) + { + close_info(); + open_info(); + open_title(); + } else if (mode == MODE_IMAGE && prev.zoom != img.zoom) { + open_title(); + } + /* update bar contents */ if (win.bar.h == 0 || extprefix) return; + + free((char *)prev.filepath); + prev.filepath = estrdup(files[fileidx].path); + prev.fileidx = fileidx; + prev.zoom = img.zoom; + prev.mode = mode; + for (fw = 0, i = filecnt; i > 0; fw++, i /= 10) ; mark = files[fileidx].flags & FF_MARK ? "* " : ""; @@ -520,7 +539,6 @@ void redraw(void) tns_render(&tns); } update_info(); - open_title(); win_draw(&win); reset_timeout(redraw); reset_cursor(); @@ -669,8 +687,6 @@ static bool run_key_handler(const char *key, unsigned int mask) if (mode == MODE_IMAGE && changed) { img_close(&img, true); load_image(fileidx); - } else { - open_info(); } free(oldst); reset_cursor(); diff --git a/nsxiv.h b/nsxiv.h index 949fc4b..e137bfe 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -468,6 +468,5 @@ extern int alternate; extern int markcnt; extern int markidx; extern int prefix; -extern bool title_dirty; #endif /* NSXIV_H */ diff --git a/thumbs.c b/thumbs.c index d2f91a7..500446f 100644 --- a/thumbs.c +++ b/thumbs.c @@ -447,7 +447,6 @@ void tns_render(tns_t *tns) } tns->dirty = false; tns_highlight(tns, *tns->sel, true); - title_dirty = true; } void tns_mark(tns_t *tns, int n, bool mark) @@ -516,7 +515,6 @@ bool tns_move_selection(tns_t *tns, direction_t dir, int cnt) tns_check_view(tns, false); if (!tns->dirty) tns_highlight(tns, *tns->sel, true); - title_dirty = true; } return *tns->sel != old; } From 2093f36661194f6f820c1233ff8857656a4d1dfe Mon Sep 17 00:00:00 2001 From: NRK Date: Mon, 8 May 2023 03:36:17 +0600 Subject: [PATCH 12/27] make thumbnail caching safer against concurrent writes by writing to a tmpfile first, and then renaming it to the desired target - multiple nsxiv instances writing thumbnail at the same time are guaranteed not to stomp over one another. rename() is guaranteed to be atomic by POSIX. however, it can fail with EXDEV if both the files don't reside in the same filesystem. and so we cannot make the tmpfile something like "/tmp/nsxiv-XXXXXX". instead, create the tmpfile inside the cache_dir to reduce chances of EXDEV occuring. --- thumbs.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/thumbs.c b/thumbs.c index 500446f..bd1e314 100644 --- a/thumbs.c +++ b/thumbs.c @@ -35,6 +35,8 @@ #endif static char *cache_dir; +static char *cache_tmpfile, *cache_tmpfile_base; +static const char TMP_NAME[] = "/nsxiv-XXXXXX"; static char *tns_cache_filepath(const char *filepath) { @@ -76,6 +78,7 @@ static Imlib_Image tns_cache_load(const char *filepath, bool *outdated) static void tns_cache_write(Imlib_Image im, const char *filepath, bool force) { char *cfile, *dirend; + int tmpfd; struct stat cstats, fstats; struct utimbuf times; Imlib_Load_Error err; @@ -103,12 +106,17 @@ static void tns_cache_write(Imlib_Image im, const char *filepath, bool force) imlib_image_set_format("jpg"); imlib_image_attach_data_value("quality", NULL, 90, NULL); } - imlib_save_image_with_error_return(cfile, &err); - if (err) + memcpy(cache_tmpfile_base, TMP_NAME, sizeof(TMP_NAME)); + if ((tmpfd = mkstemp(cache_tmpfile)) < 0) goto end; + close(tmpfd); + /* UPGRADE: Imlib2 v1.11.0: use imlib_save_image_fd() */ + imlib_save_image_with_error_return(cache_tmpfile, &err); times.actime = fstats.st_atime; times.modtime = fstats.st_mtime; - utime(cfile, ×); + utime(cache_tmpfile, ×); + if (err || rename(cache_tmpfile, cfile) < 0) + unlink(cache_tmpfile); } end: free(cfile); @@ -169,6 +177,9 @@ void tns_init(tns_t *tns, fileinfo_t *tns_files, const int *cnt, int *sel, win_t len = strlen(homedir) + strlen(dsuffix) + strlen(s) + 1; cache_dir = emalloc(len); snprintf(cache_dir, len, "%s%s%s", homedir, dsuffix, s); + cache_tmpfile = emalloc(len + sizeof(TMP_NAME)); + memcpy(cache_tmpfile, cache_dir, len - 1); + cache_tmpfile_base = cache_tmpfile + len - 1; } else { error(0, 0, "Cache directory not found"); } From 1fc28278b55a5835bfd57821619141bd92eed91d Mon Sep 17 00:00:00 2001 From: NRK Date: Sun, 1 Oct 2023 17:06:04 +0600 Subject: [PATCH 13/27] add option to update cache in a background process the cli flag is undocumented for now, since it's experimental. Closes: https://codeberg.org/nsxiv/nsxiv/issues/416 Closes: https://codeberg.org/nsxiv/nsxiv/pulls/425 Co-authored-by: explosion-mental --- main.c | 17 +++++++++++++++++ nsxiv.h | 1 + options.c | 11 ++++++++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/main.c b/main.c index a050aa8..e0ff5b3 100644 --- a/main.c +++ b/main.c @@ -936,6 +936,23 @@ int main(int argc, char *argv[]) filecnt = fileidx; fileidx = options->startnum < filecnt ? options->startnum : 0; + if (options->background_cache && !options->private_mode) { + pid_t ppid = getpid(); /* to check if parent is still alive or not */ + switch (fork()) { + case 0: + tns_init(&tns, files, &filecnt, &fileidx, NULL); + while (filecnt > 0 && getppid() == ppid) { + tns_load(&tns, filecnt - 1, false, true); + remove_file(filecnt - 1, true); + } + exit(0); + break; + case -1: + error(0, errno, "fork failed"); + break; + } + } + win_init(&win); img_init(&img, &win); arl_init(&arl); diff --git a/nsxiv.h b/nsxiv.h index e137bfe..3ffc113 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -274,6 +274,7 @@ struct opt { bool thumb_mode; bool clean_cache; bool private_mode; + bool background_cache; }; extern const opt_t *options; diff --git a/options.c b/options.c index 89dfe20..2835967 100644 --- a/options.c +++ b/options.c @@ -81,7 +81,8 @@ void parse_options(int argc, char **argv) */ OPT_START = UCHAR_MAX, OPT_AA, - OPT_AL + OPT_AL, + OPT_BG }; static const struct optparse_long longopts[] = { { "framerate", 'A', OPTPARSE_REQUIRED }, @@ -109,6 +110,8 @@ void parse_options(int argc, char **argv) { "null", '0', OPTPARSE_NONE }, { "anti-alias", OPT_AA, OPTPARSE_OPTIONAL }, { "alpha-layer", OPT_AL, OPTPARSE_OPTIONAL }, + /* TODO: document this when it's stable */ + { "bg-cache", OPT_BG, OPTPARSE_OPTIONAL }, { 0 }, /* end */ }; @@ -145,6 +148,7 @@ void parse_options(int argc, char **argv) _options.thumb_mode = false; _options.clean_cache = false; _options.private_mode = false; + _options.background_cache = false; if (argc > 0) { s = strrchr(argv[0], '/'); @@ -264,6 +268,11 @@ void parse_options(int argc, char **argv) error(EXIT_FAILURE, 0, "Invalid argument for option --alpha-layer: %s", op.optarg); _options.alpha_layer = op.optarg == NULL; break; + case OPT_BG: + if (op.optarg != NULL && !STREQ(op.optarg, "no")) + error(EXIT_FAILURE, 0, "Invalid argument for option --bg-cache: %s", op.optarg); + _options.background_cache = op.optarg == NULL; + break; } } From e1b851c4886ab3976f0591be5b740f2a3a8cb7d1 Mon Sep 17 00:00:00 2001 From: NRK Date: Sun, 27 Aug 2023 23:20:39 +0600 Subject: [PATCH 14/27] remove legacy multi-frame loaders this removes the legacy gif and webp loaders. moving forward multi-frame/animated images will be loaded by imlib2 itself. Closes: https://codeberg.org/nsxiv/nsxiv/issues/397 --- .github/workflows/build.yml | 4 +- Makefile | 10 +- README.md | 11 +- config.mk | 5 - etc/woodpecker/analysis.yml | 2 +- etc/woodpecker/build.yml | 4 +- image.c | 298 +----------------------------------- options.c | 7 - 8 files changed, 9 insertions(+), 332 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 97a7c65..6b6af3f 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -43,11 +43,11 @@ jobs: run: | brew update # see: https://github.com/actions/setup-python/issues/577 - brew install imlib2 libx11 libxft libexif giflib webp || true + brew install imlib2 libx11 libxft libexif || true - name: build run: | # libinotify-kqueue isn't available on homebrew make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 # force uninstallation with --ignore-dependencies - brew uninstall --ignore-dependencies libxft libexif giflib webp + brew uninstall --ignore-dependencies libxft libexif make clean && make -s OPT_DEP_DEFAULT=0 diff --git a/Makefile b/Makefile index 3527741..b7ee37b 100644 --- a/Makefile +++ b/Makefile @@ -8,20 +8,14 @@ lib_fonts_0 = lib_fonts_1 = -lXft -lfontconfig lib_exif_0 = lib_exif_1 = -lexif -lib_gif_0 = -lib_gif_1 = -lgif -lib_webp_0 = -lib_webp_1 = -lwebpdemux -lwebp nsxiv_cppflags = -D_XOPEN_SOURCE=700 \ - -DHAVE_LIBGIF=$(HAVE_LIBGIF) -DHAVE_LIBEXIF=$(HAVE_LIBEXIF) \ - -DHAVE_LIBWEBP=$(HAVE_LIBWEBP) -DHAVE_LIBFONTS=$(HAVE_LIBFONTS) \ + -DHAVE_LIBEXIF=$(HAVE_LIBEXIF) -DHAVE_LIBFONTS=$(HAVE_LIBFONTS) \ -DHAVE_INOTIFY=$(HAVE_INOTIFY) $(inc_fonts_$(HAVE_LIBFONTS)) \ $(CPPFLAGS) nsxiv_ldlibs = -lImlib2 -lX11 \ - $(lib_exif_$(HAVE_LIBEXIF)) $(lib_gif_$(HAVE_LIBGIF)) \ - $(lib_webp_$(HAVE_LIBWEBP)) $(lib_fonts_$(HAVE_LIBFONTS)) \ + $(lib_exif_$(HAVE_LIBEXIF)) $(lib_fonts_$(HAVE_LIBFONTS)) \ $(LDLIBS) objs = autoreload.o commands.o image.o main.o options.o \ diff --git a/README.md b/README.md index 3a3b2d1..9a9c1d4 100644 --- a/README.md +++ b/README.md @@ -25,7 +25,7 @@ Features -------- * Basic image operations like zooming, panning, rotating -* Basic support for animated/multi-frame images +* Basic support for animated/multi-frame images (**requires Imlib2 v1.8.0 or above**) * Thumbnail mode: grid of selectable previews of all images * Ability to cache thumbnails for fast re-loading * Automatically refreshing modified images @@ -76,15 +76,6 @@ The following dependencies are optional: * `libexif`: Used for auto-orientation and exif thumbnails. Disable via `HAVE_LIBEXIF=0`. -The following dependencies are only used if your imlib2 version is lower than -v1.8.0. If your imlib2 version is v1.8.0 (or above) then the following -dependencies are unused and won't be built (even if you enable it explicitly). - - * `giflib`: Used for animated gif playback. Disabled via `HAVE_LIBGIF=0`. - * `libwebp`: Used for animated webp playback. - (***NOTE***: animated webp also requires Imlib2 v1.7.5 or above) - Disabled via `HAVE_LIBWEBP=0`. - Please make sure to install the corresponding development packages in case that you want to build nsxiv on a distribution with separate runtime and development packages (e.g. \*-dev on Debian). diff --git a/config.mk b/config.mk index fb28086..b1faec6 100644 --- a/config.mk +++ b/config.mk @@ -16,11 +16,6 @@ HAVE_INOTIFY = $(OPT_DEP_DEFAULT) HAVE_LIBFONTS = $(OPT_DEP_DEFAULT) HAVE_LIBEXIF = $(OPT_DEP_DEFAULT) -# unused if imlib2 version is 1.8.0 or higher. -# these options will be removed eventually. -HAVE_LIBGIF = $(OPT_DEP_DEFAULT) -HAVE_LIBWEBP = $(OPT_DEP_DEFAULT) - # CFLAGS, any additional compiler flags goes here CFLAGS = -Wall -pedantic -O2 -DNDEBUG # Uncomment for a debug build using gcc/clang diff --git a/etc/woodpecker/analysis.yml b/etc/woodpecker/analysis.yml index 9ebcc4f..b08dc36 100644 --- a/etc/woodpecker/analysis.yml +++ b/etc/woodpecker/analysis.yml @@ -6,6 +6,6 @@ pipeline: commands: | apk add --no-cache build-base cppcheck clang-extra-tools git \ imlib2-dev xorgproto \ - libxft-dev libexif-dev giflib-dev libwebp-dev >/dev/null + libxft-dev libexif-dev >/dev/null make config.h version.h ./etc/woodpecker/analysis.sh diff --git a/etc/woodpecker/build.yml b/etc/woodpecker/build.yml index 7c79a90..1f447a3 100644 --- a/etc/woodpecker/build.yml +++ b/etc/woodpecker/build.yml @@ -9,7 +9,7 @@ pipeline: commands: | apk add --no-cache \ imlib2 imlib2-dev xorgproto \ - libxft libxft-dev libexif libexif-dev giflib giflib-dev libwebp libwebp-dev \ + libxft libxft-dev libexif libexif-dev \ gcc clang llvm llvm-dev build-base wget ca-certificates bc >/dev/null wget "https://github.com/TinyCC/tinycc/archive/$TCC_SHA.tar.gz" >/dev/null tar xzf "$TCC_SHA.tar.gz" >/dev/null @@ -26,5 +26,5 @@ pipeline: # full-build with gcc and clang # build "1" "full" # ensure minimal-build works without opt deps installed - apk del libxft libxft-dev libexif libexif-dev giflib giflib-dev libwebp libwebp-dev >/dev/null + apk del libxft libxft-dev libexif libexif-dev >/dev/null build "0" "minimal" diff --git a/image.c b/image.c index 6020e93..2a01954 100644 --- a/image.c +++ b/image.c @@ -33,18 +33,6 @@ #include #endif -#if HAVE_LIBGIF && !HAVE_IMLIB2_MULTI_FRAME -#include -enum { DEF_GIF_DELAY = 75 }; -#endif - -#if HAVE_LIBWEBP && !HAVE_IMLIB2_MULTI_FRAME -#include -#include -#include -enum { DEF_WEBP_DELAY = 75 }; -#endif - #if HAVE_IMLIB2_MULTI_FRAME enum { DEF_ANIM_DELAY = 75 }; #endif @@ -144,7 +132,7 @@ void exif_auto_orientate(const fileinfo_t *file) } #endif -#if HAVE_LIBGIF || HAVE_LIBWEBP || HAVE_IMLIB2_MULTI_FRAME +#if HAVE_IMLIB2_MULTI_FRAME static void img_multiframe_context_set(img_t *img) { if (img->multi.cnt > 1) { @@ -157,283 +145,7 @@ static void img_multiframe_context_set(img_t *img) imlib_context_set_image(img->im); } -#endif -#if (HAVE_LIBGIF || HAVE_LIBWEBP) && !HAVE_IMLIB2_MULTI_FRAME -static void img_multiframe_deprecation_notice(void) -{ - static bool warned; - if (!warned) { - error(0, 0, "\n" - "################################################################\n" - "# DEPRECATION NOTICE #\n" - "################################################################\n" - "# Internal multi-frame gif and webp loaders are deprecated and #\n" - "# will be removed soon. Please upgrade to Imlib2 v1.8.0 for #\n" - "# multi-frame/animated image support. #\n" - "################################################################"); - warned = true; - } -} -#endif - -#if HAVE_LIBGIF && !HAVE_IMLIB2_MULTI_FRAME -static bool img_load_gif(img_t *img, const fileinfo_t *file) -{ - GifFileType *gif; - GifRowType *rows = NULL; - GifRecordType rec; - ColorMapObject *cmap; - uint32_t bgpixel = 0, *data, *ptr; - uint32_t *prev_frame = NULL; - Imlib_Image im; - int i, j, bg, r, g, b; - int x, y, w, h, sw, sh; - int px, py, pw, ph; - int intoffset[] = { 0, 4, 2, 1 }; - int intjump[] = { 8, 8, 4, 2 }; - int transp = -1; - unsigned int disposal = 0, prev_disposal = 0; - unsigned int delay = 0; - bool err = false; - multi_img_t *m = &img->multi; - - img_multiframe_deprecation_notice(); - -#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5 - gif = DGifOpenFileName(file->path, NULL); -#else - gif = DGifOpenFileName(file->path); -#endif - if (gif == NULL) { - error(0, 0, "%s: Error opening gif image", file->name); - return false; - } - bg = gif->SBackGroundColor; - sw = gif->SWidth; - sh = gif->SHeight; - px = py = pw = ph = 0; - - m->length = m->cnt = m->sel = 0; - do { - if (DGifGetRecordType(gif, &rec) == GIF_ERROR) { - err = true; - break; - } - if (rec == EXTENSION_RECORD_TYPE) { - int ext_code; - GifByteType *ext = NULL; - - DGifGetExtension(gif, &ext_code, &ext); - while (ext) { - if (ext_code == GRAPHICS_EXT_FUNC_CODE) { - if (ext[1] & 1) - transp = (int)ext[4]; - else - transp = -1; - - delay = 10 * ((unsigned int)ext[3] << 8 | (unsigned int)ext[2]); - disposal = (unsigned int)ext[1] >> 2 & 0x7; - } - ext = NULL; - DGifGetExtensionNext(gif, &ext); - } - } else if (rec == IMAGE_DESC_RECORD_TYPE) { - if (DGifGetImageDesc(gif) == GIF_ERROR) { - err = true; - break; - } - x = gif->Image.Left; - y = gif->Image.Top; - w = gif->Image.Width; - h = gif->Image.Height; - - rows = emalloc(h * sizeof(*rows)); - for (i = 0; i < h; i++) - rows[i] = emalloc(w * sizeof(*rows[i])); - if (gif->Image.Interlace) { - for (i = 0; i < 4; i++) { - for (j = intoffset[i]; j < h; j += intjump[i]) - DGifGetLine(gif, rows[j], w); - } - } else { - for (i = 0; i < h; i++) - DGifGetLine(gif, rows[i], w); - } - - ptr = data = emalloc(sw * sh * sizeof(*data)); - cmap = gif->Image.ColorMap ? gif->Image.ColorMap : gif->SColorMap; - /* if bg > cmap->ColorCount, it is transparent black already */ - if (cmap && bg >= 0 && bg < cmap->ColorCount) { - r = cmap->Colors[bg].Red; - g = cmap->Colors[bg].Green; - b = cmap->Colors[bg].Blue; - bgpixel = 0x00ffffff & (r << 16 | g << 8 | b); - } - - for (i = 0; i < sh; i++) { - for (j = 0; j < sw; j++) { - if (i < y || i >= y + h || j < x || j >= x + w || - rows[i - y][j - x] == transp) - { - if (prev_frame != NULL && - (prev_disposal != 2 || i < py || i >= py + ph || - j < px || j >= px + pw)) - { - *ptr = prev_frame[i * sw + j]; - } else { - *ptr = bgpixel; - } - } else { - assert(cmap != NULL); - r = cmap->Colors[rows[i - y][j - x]].Red; - g = cmap->Colors[rows[i - y][j - x]].Green; - b = cmap->Colors[rows[i - y][j - x]].Blue; - *ptr = 0xffu << 24 | r << 16 | g << 8 | b; - } - ptr++; - } - } - - im = imlib_create_image_using_copied_data(sw, sh, data); - - for (i = 0; i < h; i++) - free(rows[i]); - free(rows); - free(data); - - if (im == NULL) { - err = true; - break; - } - - imlib_context_set_image(im); - imlib_image_set_format("gif"); - if (transp >= 0) - imlib_image_set_has_alpha(1); - - if (disposal != 3) - prev_frame = imlib_image_get_data_for_reading_only(); - prev_disposal = disposal; - px = x, py = y, pw = w, ph = h; - - assert(m->cnt <= m->cap); - if (m->cnt == m->cap) { - m->cap = m->cap == 0 ? 16 : (m->cap * 2); - m->frames = erealloc(m->frames, m->cap * sizeof(*m->frames)); - } - m->frames[m->cnt].im = im; - delay = m->framedelay > 0 ? m->framedelay : delay; - m->frames[m->cnt].delay = delay > 0 ? delay : DEF_GIF_DELAY; - m->length += m->frames[m->cnt].delay; - m->cnt++; - } - } while (rec != TERMINATE_RECORD_TYPE); - -#if defined(GIFLIB_MAJOR) && GIFLIB_MAJOR >= 5 && GIFLIB_MINOR >= 1 - DGifCloseFile(gif, NULL); -#else - DGifCloseFile(gif); -#endif - - if (err && (file->flags & FF_WARN)) - error(0, 0, "%s: Corrupted gif file", file->name); - - img_multiframe_context_set(img); - - return !err; -} -#endif /* HAVE_LIBGIF */ - -#if HAVE_LIBWEBP && !HAVE_IMLIB2_MULTI_FRAME -static bool img_load_webp(img_t *img, const fileinfo_t *file) -{ - FILE *webp_file; - WebPData data; - Imlib_Image im = NULL; - struct WebPAnimDecoderOptions opts; - WebPAnimDecoder *dec = NULL; - struct WebPAnimInfo info; - unsigned char *buf = NULL, *bytes = NULL; - int ts; - const WebPDemuxer *demux; - WebPIterator iter; - unsigned long flags; - unsigned int delay; - bool err = false; - multi_img_t *m = &img->multi; - - img_multiframe_deprecation_notice(); - - if ((webp_file = fopen(file->path, "rb")) == NULL) { - error(0, errno, "%s: Error opening webp image", file->name); - return false; - } - fseek(webp_file, 0L, SEEK_END); - data.size = ftell(webp_file); - rewind(webp_file); - bytes = emalloc(data.size); - if ((err = fread(bytes, 1, data.size, webp_file) != data.size)) { - error(0, 0, "%s: Error reading webp image", file->name); - goto fail; - } - data.bytes = bytes; - - /* Setup the WebP Animation Decoder */ - if ((err = !WebPAnimDecoderOptionsInit(&opts))) { - error(0, 0, "%s: WebP library version mismatch", file->name); - goto fail; - } - opts.color_mode = MODE_BGRA; - /* NOTE: Multi-threaded decoding may cause problems on some system */ - opts.use_threads = true; - dec = WebPAnimDecoderNew(&data, &opts); - if ((err = (dec == NULL) || !WebPAnimDecoderGetInfo(dec, &info))) { - error(0, 0, "%s: WebP parsing or memory error (file is corrupt?)", file->name); - goto fail; - } - demux = WebPAnimDecoderGetDemuxer(dec); - - /* Get global information for the image */ - flags = WebPDemuxGetI(demux, WEBP_FF_FORMAT_FLAGS); - img->w = WebPDemuxGetI(demux, WEBP_FF_CANVAS_WIDTH); - img->h = WebPDemuxGetI(demux, WEBP_FF_CANVAS_HEIGHT); - - if (info.frame_count > m->cap) { - m->cap = info.frame_count; - m->frames = erealloc(m->frames, m->cap * sizeof(*m->frames)); - } - - /* Load and decode frames (also works on images with only 1 frame) */ - m->length = m->cnt = m->sel = 0; - while (WebPAnimDecoderGetNext(dec, &buf, &ts)) { - im = imlib_create_image_using_copied_data(info.canvas_width, info.canvas_height, - (uint32_t *)buf); - imlib_context_set_image(im); - imlib_image_set_format("webp"); - /* Get an iterator of this frame - used for frame info (duration, etc.) */ - WebPDemuxGetFrame(demux, m->cnt + 1, &iter); - imlib_image_set_has_alpha((flags & ALPHA_FLAG) == ALPHA_FLAG); - /* Store info for this frame */ - m->frames[m->cnt].im = im; - delay = iter.duration > 0 ? iter.duration : DEF_WEBP_DELAY; - m->frames[m->cnt].delay = delay; - m->length += m->frames[m->cnt].delay; - m->cnt++; - } - WebPDemuxReleaseIterator(&iter); - - img_multiframe_context_set(img); -fail: - if (dec != NULL) - WebPAnimDecoderDelete(dec); - free(bytes); - fclose(webp_file); - return !err; -} -#endif /* HAVE_LIBWEBP */ - -#if HAVE_IMLIB2_MULTI_FRAME static void img_area_clear(int x, int y, int w, int h) { assert(x >= 0 && y >= 0); @@ -596,14 +308,6 @@ bool img_load(img_t *img, const fileinfo_t *file) #endif if ((fmt = imlib_image_format()) != NULL) { /* NOLINT: fmt might be unused, not worth fixing */ -#if HAVE_LIBGIF && !HAVE_IMLIB2_MULTI_FRAME - if (STREQ(fmt, "gif")) - img_load_gif(img, file); -#endif -#if HAVE_LIBWEBP && !HAVE_IMLIB2_MULTI_FRAME - if (STREQ(fmt, "webp")) - img_load_webp(img, file); -#endif #if HAVE_LIBEXIF && defined(IMLIB2_VERSION) if (!STREQ(fmt, "jpeg") && !STREQ(fmt, "jpg")) exif_auto_orientate(file); diff --git a/options.c b/options.c index 2835967..4ae2ea5 100644 --- a/options.c +++ b/options.c @@ -62,13 +62,6 @@ static void print_version(void) #endif #if HAVE_IMLIB2_MULTI_FRAME "+multiframe " -#else - #if HAVE_LIBGIF - "+giflib " - #endif - #if HAVE_LIBWEBP - "+libwebp " - #endif #endif "\n", stdout); } From 48d1afd50a4611270ca11215690d5e241bc01fdc Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 21 Sep 2023 05:06:07 +0600 Subject: [PATCH 15/27] inline img_multiframe_context_set() no longer needs to be a separate function now that the legacy loaders are removed. also remove a NOLINT comment and use `(void)var` to silence warning. --- image.c | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/image.c b/image.c index 2a01954..a138f95 100644 --- a/image.c +++ b/image.c @@ -133,19 +133,6 @@ void exif_auto_orientate(const fileinfo_t *file) #endif #if HAVE_IMLIB2_MULTI_FRAME -static void img_multiframe_context_set(img_t *img) -{ - if (img->multi.cnt > 1) { - img_free(img->im, false); - img->im = img->multi.frames[0].im; - } else if (img->multi.cnt == 1) { - img_free(img->multi.frames[0].im, false); - img->multi.cnt = 0; - } - - imlib_context_set_image(img->im); -} - static void img_area_clear(int x, int y, int w, int h) { assert(x >= 0 && y >= 0); @@ -253,8 +240,16 @@ static bool img_load_multiframe(img_t *img, const fileinfo_t *file) img_free(frame, false); } img_free(blank, false); - img_multiframe_context_set(img); imlib_context_set_color_modifier(img->cmod); /* restore cmod */ + + if (m->cnt > 1) { + img_free(img->im, false); + img->im = m->frames[0].im; + } else if (m->cnt == 1) { + img_free(m->frames[0].im, false); + m->cnt = 0; + } + imlib_context_set_image(img->im); return m->cnt > 0; } #endif /* HAVE_IMLIB2_MULTI_FRAME */ @@ -307,12 +302,13 @@ bool img_load(img_t *img, const fileinfo_t *file) animated = img_load_multiframe(img, file); #endif - if ((fmt = imlib_image_format()) != NULL) { /* NOLINT: fmt might be unused, not worth fixing */ + (void)fmt; /* maybe unused */ #if HAVE_LIBEXIF && defined(IMLIB2_VERSION) + if ((fmt = imlib_image_format()) != NULL) { if (!STREQ(fmt, "jpeg") && !STREQ(fmt, "jpg")) exif_auto_orientate(file); -#endif } +#endif /* for animated images, we want the _canvas_ width/height, which * img_load_multiframe() sets already. */ From a9e13042d4e3398123cf9c3335d03fa462a45764 Mon Sep 17 00:00:00 2001 From: NRK Date: Tue, 4 Jul 2023 10:41:27 +0600 Subject: [PATCH 16/27] Release version 32 --- config.mk | 2 +- etc/CHANGELOG.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/config.mk b/config.mk index b1faec6..49a1652 100644 --- a/config.mk +++ b/config.mk @@ -1,5 +1,5 @@ # nsxiv version -VERSION = 31 +VERSION = 32 # PREFIX for install PREFIX = /usr/local diff --git a/etc/CHANGELOG.md b/etc/CHANGELOG.md index f89cc95..5af260f 100644 --- a/etc/CHANGELOG.md +++ b/etc/CHANGELOG.md @@ -8,6 +8,60 @@ there may be more changes. Please use `git log` to view them. - - - +**[v32](https://codeberg.org/nsxiv/nsxiv/archive/v32.tar.gz)** +*(October 01, 2023)* + +* Changes: + + * Removed legacy multi-frame loaders. Animated image support now requires + Imlib2 v1.8.0 or above. + * Move loading/caching messages to right side bar [#446] + * Set a default delay if delay is 0 in a multi-frame image [#445] + * `config.mk`: default to `-O2` [#435] + * `config.mk` no longer explicitly sets `CC` to `c99` [#455] + * Assertions are now opt-in and requires explicitly defining `DEBUG` [#447] + +* Added: + + * Added a pick-quit key-binding [#432] + * Ability to configure Xresources class name in `config.h` [#427] + * `--version` output now also includes compiled-in feature list [#462] + * Document handling of empty X resources values [#428] + * Experimental flag `--bg-cache` to generate thumbnail cache in a background + process [#438] + +* Fixes: + + * Changing brightness/contrast on multi-frame images [#440] + * Brightness keybindings on manpage [#467] + * Various autoreload bugs [#437], [#459], [#460] + * `*-info` scripts not updating when selecting thumbnail with mouse [#477] + * Updated openbsd configuration in `config.mk` [#453] + * Memory leak in `win_draw_bar` [#444] + * Thumbnail leak when removing the last file [#423] + +[#423]: https://codeberg.org/nsxiv/nsxiv/pulls/423 +[#427]: https://codeberg.org/nsxiv/nsxiv/pulls/427 +[#428]: https://codeberg.org/nsxiv/nsxiv/pulls/428 +[#432]: https://codeberg.org/nsxiv/nsxiv/pulls/432 +[#435]: https://codeberg.org/nsxiv/nsxiv/pulls/435 +[#437]: https://codeberg.org/nsxiv/nsxiv/pulls/437 +[#438]: https://codeberg.org/nsxiv/nsxiv/pulls/438 +[#440]: https://codeberg.org/nsxiv/nsxiv/pulls/440 +[#444]: https://codeberg.org/nsxiv/nsxiv/pulls/444 +[#445]: https://codeberg.org/nsxiv/nsxiv/pulls/445 +[#446]: https://codeberg.org/nsxiv/nsxiv/pulls/446 +[#447]: https://codeberg.org/nsxiv/nsxiv/pulls/447 +[#453]: https://codeberg.org/nsxiv/nsxiv/pulls/453 +[#455]: https://codeberg.org/nsxiv/nsxiv/pulls/455 +[#459]: https://codeberg.org/nsxiv/nsxiv/pulls/459 +[#460]: https://codeberg.org/nsxiv/nsxiv/pulls/460 +[#462]: https://codeberg.org/nsxiv/nsxiv/pulls/462 +[#467]: https://codeberg.org/nsxiv/nsxiv/pulls/467 +[#477]: https://codeberg.org/nsxiv/nsxiv/pulls/477 + +- - - + **[v31](https://codeberg.org/nsxiv/nsxiv/archive/v31.tar.gz)** *(January 28, 2023)* From 70cbe59daa319ff71afeb84444dff7d3fdfadbdb Mon Sep 17 00:00:00 2001 From: NRK Date: Mon, 2 Oct 2023 09:43:46 +0000 Subject: [PATCH 17/27] ci: various fixes (#478) - "branches" option got removed, change to "when -> branches" - "pipeline" option got removed, change to "steps" - install codespell in a virtual-env since alpine complains otherwise --- etc/woodpecker/analysis.yml | 5 +++-- etc/woodpecker/build.yml | 5 +++-- etc/woodpecker/spell.yml | 9 ++++++--- 3 files changed, 12 insertions(+), 7 deletions(-) diff --git a/etc/woodpecker/analysis.yml b/etc/woodpecker/analysis.yml index b08dc36..4edcd6b 100644 --- a/etc/woodpecker/analysis.yml +++ b/etc/woodpecker/analysis.yml @@ -1,6 +1,7 @@ -branches: master +when: + branch: master -pipeline: +steps: analysis: image: alpine commands: | diff --git a/etc/woodpecker/build.yml b/etc/woodpecker/build.yml index 1f447a3..431f27f 100644 --- a/etc/woodpecker/build.yml +++ b/etc/woodpecker/build.yml @@ -1,7 +1,8 @@ -branches: master +when: + branch: master # NOTE: "stable" tcc is too old and fails at linking. instead fetching a recent known working commit. -pipeline: +steps: build: image: alpine environment: diff --git a/etc/woodpecker/spell.yml b/etc/woodpecker/spell.yml index a6b71d8..78ce57f 100644 --- a/etc/woodpecker/spell.yml +++ b/etc/woodpecker/spell.yml @@ -1,10 +1,13 @@ -branches: master +when: + branch: master -# NOTE: codespell not available on stable alpine, use edge -pipeline: +# NOTE: codespell not available on stable alpine, grab it from pip +steps: spell-check: image: alpine:edge commands: | apk add --no-cache python3 py3-pip git + python3 -m venv ~/py3-venv + . ~/py3-venv/bin/activate pip install codespell git ls-files | sed '/\.png$/d' | xargs codespell From bed596bf47a1571639aa1427dbdd57f00ed0503e Mon Sep 17 00:00:00 2001 From: NRK Date: Mon, 13 Nov 2023 10:45:49 +0000 Subject: [PATCH 18/27] move key-handler prompts to the right bar (#481) this makes it more consistent with e4fceab by moving the key-handler related messages to the right side as well. additionally this fixes a regression introduced by 3659361 where the left statusbar would remain at "Running key-handler..." if the image didn't change. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/481 Reviewed-by: eylles --- main.c | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/main.c b/main.c index e0ff5b3..0e1e15c 100644 --- a/main.c +++ b/main.c @@ -601,12 +601,10 @@ void handle_key_handler(bool init) if (win.bar.h == 0) return; if (init) { - close_info(); - snprintf(win.bar.l.buf, win.bar.l.size, + snprintf(win.bar.r.buf, win.bar.r.size, "Getting key handler input (%s to abort)...", XKeysymToString(KEYHANDLER_ABORT)); } else { /* abort */ - open_info(); update_info(); } win_draw(&win); @@ -635,8 +633,7 @@ static bool run_key_handler(const char *key, unsigned int mask) if (key == NULL) return false; - close_info(); - strncpy(win.bar.l.buf, "Running key handler...", win.bar.l.size); + strncpy(win.bar.r.buf, "Running key handler...", win.bar.r.size); win_draw(&win); win_set_cursor(&win, CURSOR_WATCH); setenv("NSXIV_USING_NULL", options->using_null ? "1" : "0", 1); @@ -687,6 +684,8 @@ static bool run_key_handler(const char *key, unsigned int mask) if (mode == MODE_IMAGE && changed) { img_close(&img, true); load_image(fileidx); + } else { + update_info(); } free(oldst); reset_cursor(); From add12ecdfd0f5afea56c76532ba563599a620368 Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 8 Feb 2024 17:57:33 +0000 Subject: [PATCH 19/27] README: fix typo --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 9a9c1d4..e8ce9e9 100644 --- a/README.md +++ b/README.md @@ -81,8 +81,8 @@ you want to build nsxiv on a distribution with separate runtime and development packages (e.g. \*-dev on Debian). \* [inotify][] is a Linux-specific API for monitoring filesystem changes. - It's not natively available on `*BSD` systems but can be enabed via installing - and linking against [libinotify-kqueue][]. + It's not natively available on `*BSD` systems but can be enabled via + installing and linking against [libinotify-kqueue][]. [inotify]: https://www.man7.org/linux/man-pages/man7/inotify.7.html [libinotify-kqueue]: https://github.com/libinotify-kqueue/libinotify-kqueue From 0faff1866d304c9ea7ef9da542dd2a04ecf35da8 Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 8 Feb 2024 17:57:55 +0000 Subject: [PATCH 20/27] ci: adjust clang-tidy checks disable misc-include-cleaner, it nags about "directly" including things like "stdbool.h" instead of relying on "nsxiv.h" for it. also disable bugprone-switch-missing-default-case which nags about missing `default` switch cases. --- etc/woodpecker/clang-tidy-checks | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etc/woodpecker/clang-tidy-checks b/etc/woodpecker/clang-tidy-checks index edf0ba6..c326eef 100644 --- a/etc/woodpecker/clang-tidy-checks +++ b/etc/woodpecker/clang-tidy-checks @@ -4,13 +4,13 @@ misc-*,android-cloexec-*,llvm-include-order -readability-*,readability-duplicate-include,readability-misleading-indentation # silence --misc-unused-parameters +-misc-unused-parameters,-misc-include-cleaner,-misc-no-recursion -bugprone-easily-swappable-parameters,-bugprone-narrowing-conversions,-bugprone-incorrect-roundings -bugprone-implicit-widening-of-multiplication-result,-bugprone-integer-division -android-cloexec-fopen,-android-cloexec-pipe,-cert-err33-c -bugprone-assignment-in-if-condition -bugprone-suspicious-realloc-usage +-bugprone-switch-missing-default-case # false positive warnings -clang-analyzer-valist.Uninitialized --misc-no-recursion From 6cc1225fef0eb74ecb651f72ea22921f3daecece Mon Sep 17 00:00:00 2001 From: NRK Date: Mon, 12 Feb 2024 22:00:31 +0000 Subject: [PATCH 21/27] free cache_tmpfile in tns_free (#485) doesn't really matter since the process is about to exit anyways, but makes it consistent with everything else. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/485 --- thumbs.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/thumbs.c b/thumbs.c index bd1e314..c7b9dc6 100644 --- a/thumbs.c +++ b/thumbs.c @@ -198,6 +198,8 @@ CLEANUP void tns_free(tns_t *tns) free(cache_dir); cache_dir = NULL; + free(cache_tmpfile); + cache_tmpfile = cache_tmpfile_base = NULL; } static Imlib_Image tns_scale_down(Imlib_Image im, int dim) From 437e060021ccabf9d5c95068c7c52218b92359e1 Mon Sep 17 00:00:00 2001 From: fxdn Date: Sat, 17 Feb 2024 08:14:05 +0000 Subject: [PATCH 22/27] document Xresources application namespace explicitly (#488) A decent amount of users have been confused by this. So make it more explicit to avoid confusion. Co-authored-by: NRK Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/488 Reviewed-by: NRK Co-authored-by: fxdn Co-committed-by: fxdn --- etc/nsxiv.1 | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/etc/nsxiv.1 b/etc/nsxiv.1 index 9b5c243..5edfece 100644 --- a/etc/nsxiv.1 +++ b/etc/nsxiv.1 @@ -440,7 +440,9 @@ Zoom in. .B Button5 Zoom out. .SH CONFIGURATION -The following X resources are supported: +The following X resources are supported under "Nsxiv" (e.g. +.B Nsxiv.bar.font +): .TP .B window.background Color of the window background From a581cd6344717551c9bacae58809d83ae6979220 Mon Sep 17 00:00:00 2001 From: e5150 Date: Tue, 23 Apr 2024 06:47:09 +0000 Subject: [PATCH 23/27] fix: image placement when rotating (#493) When rotating a partially shown image (i.e. image size * zoom > window size) the image is panned to top or left (if `win->w` or `win->h` is greatest, respectively). Seems to be due to unsignedness of `win->[wh]`, when taking the difference between them in `img_rotate` either `img->[xy]` ends up being close to `UINT_MAX` and the image is panned to top or left edge. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/493 Reviewed-by: NRK Co-authored-by: e5150 Co-committed-by: e5150 --- image.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/image.c b/image.c index a138f95..dd91994 100644 --- a/image.c +++ b/image.c @@ -676,8 +676,8 @@ void img_rotate(img_t *img, degree_t d) ox = d == DEGREE_90 ? img->x : img->win->w - img->x - img->w * img->zoom; oy = d == DEGREE_270 ? img->y : img->win->h - img->y - img->h * img->zoom; - img->x = oy + (img->win->w - img->win->h) / 2; - img->y = ox + (img->win->h - img->win->w) / 2; + img->x = oy + (int)(img->win->w - img->win->h) / 2; + img->y = ox + (int)(img->win->h - img->win->w) / 2; tmp = img->w; img->w = img->h; From 65acb98396b7eb8e7b22140061c799dbd0de8a3e Mon Sep 17 00:00:00 2001 From: NRK Date: Wed, 5 Jun 2024 19:37:46 +0000 Subject: [PATCH 24/27] fix: unresponsive UI when animation is too fast (#489) the previous check_timeouts() logic tried to greedily handle as many timeouts as possible until there are no more active timeouts left. this caused a number of issues such as: https://codeberg.org/nsxiv/nsxiv/issues/439 https://codeberg.org/nsxiv/nsxiv-record/issues/281 the new logic relaxes this and only does a single iteration. any remaining active timeout will be picked up by the next iteration instead. Fixes: https://codeberg.org/nsxiv/nsxiv/issues/439 --- main.c | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/main.c b/main.c index 0e1e15c..5cd8b09 100644 --- a/main.c +++ b/main.c @@ -249,10 +249,10 @@ void reset_timeout(timeout_f handler) static bool check_timeouts(int *t) { - int i = 0, tdiff, tmin = -1; + int i = 0, tdiff, tmin; struct timeval now; - while (i < (int)ARRLEN(timeouts)) { + for (i = 0; i < (int)ARRLEN(timeouts); ++i) { if (timeouts[i].active) { gettimeofday(&now, 0); tdiff = TV_DIFF(&timeouts[i].when, &now); @@ -260,16 +260,22 @@ static bool check_timeouts(int *t) timeouts[i].active = false; if (timeouts[i].handler != NULL) timeouts[i].handler(); - i = tmin = -1; - } else if (tmin < 0 || tdiff < tmin) { - tmin = tdiff; } } - i++; } - if (tmin > 0 && t != NULL) - *t = tmin; - return tmin > 0; + + tmin = INT_MAX; + gettimeofday(&now, 0); + for (i = 0; i < (int)ARRLEN(timeouts); ++i) { + if (timeouts[i].active) { + tdiff = TV_DIFF(&timeouts[i].when, &now); + tmin = MIN(tmin, tdiff); + } + } + + if (tmin != INT_MAX && t != NULL) + *t = MAX(tmin, 0); + return tmin != INT_MAX; } static void autoreload(void) From 931912dcf6bb8377d82c8de9c68c8e34138d18cb Mon Sep 17 00:00:00 2001 From: NRK Date: Wed, 5 Jun 2024 19:40:58 +0000 Subject: [PATCH 25/27] make pipes non-blocking and read output on POLLHUP (#490) this makes it so that script outputs are read when the script actually finishes. one objection to reading on POLLHUP was that the script might fill up the pipe and get stuck. we already mark the read-end as non-blocking on our end so might as well make the write-end non-blocking as well which avoids the script getting blocked after (and if) it fills up the pipe. ref: https://codeberg.org/nsxiv/nsxiv/pulls/334 Closes: https://codeberg.org/nsxiv/nsxiv/issues/377 Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/490 Reviewed-by: eylles --- main.c | 16 ++++++++-------- nsxiv.h | 2 +- util.c | 17 ++++++++++------- 3 files changed, 19 insertions(+), 16 deletions(-) diff --git a/main.c b/main.c index 5cd8b09..85bf8f6 100644 --- a/main.c +++ b/main.c @@ -334,8 +334,7 @@ static void open_title(void) snprintf(fcnt, ARRLEN(fcnt), "%d", filecnt); construct_argv(argv, ARRLEN(argv), wintitle.f.cmd, files[fileidx].path, fidx, fcnt, w, h, z, NULL); - if ((wintitle.pid = spawn(&wintitle.fd, NULL, argv)) > 0) - fcntl(wintitle.fd, F_SETFL, O_NONBLOCK); + wintitle.pid = spawn(&wintitle.fd, NULL, O_NONBLOCK, argv); } void close_info(void) @@ -358,8 +357,7 @@ void open_info(void) } construct_argv(argv, ARRLEN(argv), cmd, files[fileidx].name, w, h, files[fileidx].path, NULL); - if ((info.pid = spawn(&info.fd, NULL, argv)) > 0) - fcntl(info.fd, F_SETFL, O_NONBLOCK); + info.pid = spawn(&info.fd, NULL, O_NONBLOCK, argv); } static void read_info(void) @@ -649,7 +647,7 @@ static bool run_key_handler(const char *key, unsigned int mask) mask & Mod1Mask ? "M-" : "", mask & ShiftMask ? "S-" : "", key); construct_argv(argv, ARRLEN(argv), keyhandler.f.cmd, kstr, NULL); - if ((pid = spawn(NULL, &writefd, argv)) < 0) + if ((pid = spawn(NULL, &writefd, 0x0, argv)) < 0) return false; if ((pfs = fdopen(writefd, "w")) == NULL) { error(0, errno, "open pipe"); @@ -806,13 +804,15 @@ static void run(void) pfd[FD_INFO].fd = info.fd; pfd[FD_TITLE].fd = wintitle.fd; pfd[FD_ARL].fd = arl.fd; - pfd[FD_X].events = pfd[FD_INFO].events = pfd[FD_TITLE].events = pfd[FD_ARL].events = POLLIN; + + pfd[FD_X].events = pfd[FD_ARL].events = POLLIN; + pfd[FD_INFO].events = pfd[FD_TITLE].events = 0; if (poll(pfd, ARRLEN(pfd), to_set ? timeout : -1) < 0) continue; - if (pfd[FD_INFO].revents & POLLIN) + if (pfd[FD_INFO].revents & POLLHUP) read_info(); - if (pfd[FD_TITLE].revents & POLLIN) + if (pfd[FD_TITLE].revents & POLLHUP) read_title(); if ((pfd[FD_ARL].revents & POLLIN) && arl_handle(&arl)) { img.autoreload_pending = true; diff --git a/nsxiv.h b/nsxiv.h index 3ffc113..fc0720c 100644 --- a/nsxiv.h +++ b/nsxiv.h @@ -356,7 +356,7 @@ int r_closedir(r_dir_t*); char* r_readdir(r_dir_t*, bool); int r_mkdir(char*); void construct_argv(char**, unsigned int, ...); -pid_t spawn(int*, int*, char *const []); +pid_t spawn(int*, int*, int, char *const []); /* window.c */ diff --git a/util.c b/util.c index c7fb210..e2fe8af 100644 --- a/util.c +++ b/util.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -230,25 +231,27 @@ void construct_argv(char **argv, unsigned int len, ...) assert(argv[len - 1] == NULL && "argv should be NULL terminated"); } -static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx) +static int mkspawn_pipe(posix_spawn_file_actions_t *fa, const char *cmd, int *pfd, int dupidx, int pipeflags) { - int err; + int err = 0; if (pipe(pfd) < 0) { error(0, errno, "pipe: %s", cmd); return -1; } - err = posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx); + if (pipeflags && (fcntl(pfd[0], F_SETFL, pipeflags) < 0 || fcntl(pfd[1], F_SETFL, pipeflags) < 0)) + err = errno; + err = err ? err : posix_spawn_file_actions_adddup2(fa, pfd[dupidx], dupidx); err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[0]); err = err ? err : posix_spawn_file_actions_addclose(fa, pfd[1]); if (err) { - error(0, err, "posix_spawn_file_actions: %s", cmd); + error(0, err, "mkspawn_pipe: %s", cmd); close(pfd[0]); close(pfd[1]); } return err ? -1 : 0; } -pid_t spawn(int *readfd, int *writefd, char *const argv[]) +pid_t spawn(int *readfd, int *writefd, int pipeflags, char *const argv[]) { pid_t pid = -1; const char *cmd; @@ -263,9 +266,9 @@ pid_t spawn(int *readfd, int *writefd, char *const argv[]) return pid; } - if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1) < 0) + if (readfd != NULL && mkspawn_pipe(&fa, cmd, pfd_read, 1, pipeflags) < 0) goto err_destroy_fa; - if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0) < 0) + if (writefd != NULL && mkspawn_pipe(&fa, cmd, pfd_write, 0, pipeflags) < 0) goto err_close_readfd; if ((err = posix_spawnp(&pid, cmd, &fa, NULL, argv, environ)) != 0) { From 2a62683e6084126b1bb70dd9a1608d04a6547207 Mon Sep 17 00:00:00 2001 From: NRK Date: Wed, 5 Jun 2024 21:02:02 +0000 Subject: [PATCH 26/27] fix: missing include in 65acb98 caught by the github CI. the woodpecker CI somehow missed this. Reviewed-on: https://codeberg.org/nsxiv/nsxiv/pulls/497 --- main.c | 1 + 1 file changed, 1 insertion(+) diff --git a/main.c b/main.c index 85bf8f6..d73988e 100644 --- a/main.c +++ b/main.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include From 420a0a245591ea73f86ebfb90de207a8e67e6cca Mon Sep 17 00:00:00 2001 From: NRK Date: Thu, 6 Jun 2024 10:46:04 +0000 Subject: [PATCH 27/27] ci: fix macos build for whatever reason, the path where homebrew installs headers and libraries are no longer being searched by default. work around it by manually specifying them with -I and -L. --- .github/workflows/build.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 6b6af3f..054dd94 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -47,7 +47,11 @@ jobs: - name: build run: | # libinotify-kqueue isn't available on homebrew - make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 + make clean && make -s OPT_DEP_DEFAULT=1 HAVE_INOTIFY=0 \ + CPPFLAGS="-I/opt/homebrew/include -I/opt/homebrew/include/freetype2" \ + LDLIBS="-L/opt/homebrew/lib" # force uninstallation with --ignore-dependencies brew uninstall --ignore-dependencies libxft libexif - make clean && make -s OPT_DEP_DEFAULT=0 + make clean && make -s OPT_DEP_DEFAULT=0 \ + CPPFLAGS="-I/opt/homebrew/include" \ + LDLIBS="-L/opt/homebrew/lib"