aboutsummaryrefslogtreecommitdiff
path: root/fiv-browser.c
diff options
context:
space:
mode:
authorPřemysl Eric Janouch <p@janouch.name>2021-12-26 19:41:42 +0100
committerPřemysl Eric Janouch <p@janouch.name>2021-12-27 21:51:01 +0100
commit336053f24d40bc5c350cad285a8d5e146c09d321 (patch)
treec6128561d31f351ffe311ff5dbbe987efd5e0e13 /fiv-browser.c
parent2f993502fc6584a5877b300a1353cabf58c4e0e9 (diff)
downloadfiv-336053f24d40bc5c350cad285a8d5e146c09d321.tar.gz
fiv-336053f24d40bc5c350cad285a8d5e146c09d321.tar.xz
fiv-336053f24d40bc5c350cad285a8d5e146c09d321.zip
Implement trivial wide thumbnail production
Also make libwebp a required dependency.
Diffstat (limited to 'fiv-browser.c')
-rw-r--r--fiv-browser.c123
1 files changed, 123 insertions, 0 deletions
diff --git a/fiv-browser.c b/fiv-browser.c
index 2dfd70e..788197c 100644
--- a/fiv-browser.c
+++ b/fiv-browser.c
@@ -18,6 +18,8 @@
#include <math.h>
#include <pixman.h>
+#include "config.h"
+
#include "fiv-browser.h"
#include "fiv-io.h"
#include "fiv-view.h"
@@ -50,6 +52,10 @@ struct _FivBrowser {
GArray *layouted_rows; ///< [Row]
int selected;
+ GList *thumbnail_queue; ///< URIs to thumbnail
+ GSubprocess *thumbnailer; ///< A slave for the current queue head
+ GCancellable *thumbnail_cancel; ///< Cancellable handle
+
GdkCursor *pointer; ///< Cached pointer cursor
cairo_surface_t *glow; ///< CAIRO_FORMAT_A8 mask
int item_border_x; ///< L/R .item margin + border
@@ -440,6 +446,115 @@ reload_thumbnails(FivBrowser *self)
gtk_widget_queue_resize(GTK_WIDGET(self));
}
+// --- Slave management --------------------------------------------------------
+
+static void thumbnailer_step(FivBrowser *self);
+
+static void
+thumbnailer_process(FivBrowser *self, const gchar *uri)
+{
+ // TODO(p): Consider using Entry pointers directly.
+ Entry *entry = NULL;
+ for (guint i = 0; i < self->entries->len; i++) {
+ Entry *e = &g_array_index(self->entries, Entry, i);
+ if (!g_strcmp0(e->uri, uri)) {
+ entry = e;
+ break;
+ }
+ }
+ if (!entry) {
+ g_warning("finished thumbnailing an unknown URI");
+ return;
+ }
+
+ entry_add_thumbnail(entry, self);
+ materialize_icon(self, entry);
+ gtk_widget_queue_resize(GTK_WIDGET(self));
+}
+
+static void
+on_thumbnailer_ready(GObject *object, GAsyncResult *res, gpointer user_data)
+{
+ GSubprocess *subprocess = G_SUBPROCESS(object);
+ FivBrowser *self = FIV_BROWSER(user_data);
+ GError *error = NULL;
+ if (!g_subprocess_wait_check_finish(subprocess, res, &error)) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ }
+
+ gboolean succeeded = g_subprocess_get_if_exited(self->thumbnailer) &&
+ g_subprocess_get_exit_status(self->thumbnailer) == EXIT_SUCCESS;
+ g_clear_object(&self->thumbnailer);
+ if (!self->thumbnail_queue) {
+ g_warning("finished thumbnailing an unknown image");
+ return;
+ }
+
+ gchar *uri = self->thumbnail_queue->data;
+ self->thumbnail_queue =
+ g_list_delete_link(self->thumbnail_queue, self->thumbnail_queue);
+ if (succeeded)
+ thumbnailer_process(self, uri);
+ g_free(uri);
+
+ // TODO(p): Eliminate high recursion depth with non-paths.
+ thumbnailer_step(self);
+}
+
+static void
+thumbnailer_step(FivBrowser *self)
+{
+ if (!self->thumbnail_queue)
+ return;
+
+ GFile *file = g_file_new_for_uri(self->thumbnail_queue->data);
+ gchar *path = g_file_get_path(file);
+ g_object_unref(file);
+
+ GError *error = NULL;
+ self->thumbnailer = g_subprocess_new(G_SUBPROCESS_FLAGS_NONE, &error,
+ PROJECT_NAME, "--thumbnail",
+ fiv_io_thumbnail_sizes[self->item_size].thumbnail_spec_name, "--", path,
+ NULL);
+ g_free(path);
+
+ if (error) {
+ g_warning("%s", error->message);
+ g_error_free(error);
+ return;
+ }
+
+ self->thumbnail_cancel = g_cancellable_new();
+ g_subprocess_wait_check_async(
+ self->thumbnailer, self->thumbnail_cancel, on_thumbnailer_ready, self);
+}
+
+static void
+thumbnailer_launch(FivBrowser *self)
+{
+ if (self->thumbnailer) {
+ g_cancellable_cancel(self->thumbnail_cancel);
+ g_clear_object(&self->thumbnail_cancel);
+
+ // Just let it exit on its own.
+ g_clear_object(&self->thumbnailer);
+ g_list_free_full(self->thumbnail_queue, g_free);
+ self->thumbnail_queue = NULL;
+ }
+
+ // TODO(p): Also collect rescaled images.
+ GList *missing = NULL, *rescaled = NULL;
+ for (guint i = self->entries->len; i--; ) {
+ Entry *e = &g_array_index(self->entries, Entry, i);
+ if (e->icon)
+ missing = g_list_prepend(missing, g_strdup(e->uri));
+ }
+
+ self->thumbnail_queue = g_list_concat(missing, rescaled);
+ thumbnailer_step(self);
+}
+
// --- Context menu-------------------------------------------------------------
typedef struct _OpenContext {
@@ -633,6 +748,13 @@ fiv_browser_finalize(GObject *gobject)
cairo_surface_destroy(self->glow);
g_clear_object(&self->pointer);
+ g_list_free_full(self->thumbnail_queue, g_free);
+ g_clear_object(&self->thumbnailer);
+ if (self->thumbnail_cancel) {
+ g_cancellable_cancel(self->thumbnail_cancel);
+ g_clear_object(&self->thumbnail_cancel);
+ }
+
G_OBJECT_CLASS(fiv_browser_parent_class)->finalize(gobject);
}
@@ -1076,4 +1198,5 @@ fiv_browser_load(
g_array_sort(self->entries, entry_compare);
reload_thumbnails(self);
+ thumbnailer_launch(self);
}