Compare commits
8 Commits
f56c40cf00
...
cc59e537da
Author | SHA1 | Date | |
---|---|---|---|
cc59e537da | |||
338ae69121 | |||
1c61fcc5bc | |||
dd1d6647dc | |||
abf4f1a792 | |||
6a7c86a41b | |||
6277a32fe6 | |||
8f0576d6bc |
4
.gitmodules
vendored
4
.gitmodules
vendored
@ -1,6 +1,6 @@
|
||||
[submodule "wuffs-mirror-release-c"]
|
||||
path = wuffs-mirror-release-c
|
||||
path = submodules/wuffs-mirror-release-c
|
||||
url = https://github.com/google/wuffs-mirror-release-c
|
||||
[submodule "liberty"]
|
||||
path = liberty
|
||||
path = submodules/liberty
|
||||
url = https://git.janouch.name/p/liberty.git
|
||||
|
13
README.adoc
13
README.adoc
@ -2,7 +2,7 @@ fiv
|
||||
===
|
||||
|
||||
'fiv' is a slightly unconventional, general-purpose image browser and viewer
|
||||
for Linux (that said, macOS and Windows ports are possible).
|
||||
for Linux (as well as macOS and Windows, though these have known issues).
|
||||
|
||||
image::docs/fiv.webp["Screenshot of both the browser and the viewer"]
|
||||
|
||||
@ -40,15 +40,16 @@ Building and Running
|
||||
--------------------
|
||||
Build-only dependencies:
|
||||
Meson, pkg-config, asciidoctor or asciidoc (recommended but optional) +
|
||||
Runtime dependencies:
|
||||
gtk+-3.0, glib>=2.64, pixman-1, shared-mime-info, libturbojpeg, libwebp +
|
||||
Optional dependencies: lcms2, LibRaw, librsvg-2.0, xcursor, libheif, libtiff,
|
||||
ExifTool, resvg (unstable API, needs to be requested explicitly) +
|
||||
Runtime dependencies: gtk+-3.0, glib>=2.64, pixman-1, shared-mime-info,
|
||||
libturbojpeg, libwebp, librsvg-2.0 (for icons) +
|
||||
Optional dependencies: lcms2, Little CMS fast float plugin,
|
||||
LibRaw, librsvg-2.0, xcursor, libheif, libtiff, ExifTool,
|
||||
resvg (unstable API, needs to be requested explicitly) +
|
||||
Runtime dependencies for reverse image search:
|
||||
xdg-utils, cURL, jq
|
||||
|
||||
$ git clone --recursive https://git.janouch.name/p/fiv.git
|
||||
$ meson builddir
|
||||
$ meson setup builddir
|
||||
$ cd builddir
|
||||
$ meson compile
|
||||
|
||||
|
@ -640,11 +640,15 @@ materialize_icon(FivBrowser *self, Entry *entry)
|
||||
// of using GLib to look up icons for us, derive a list from a guessed
|
||||
// MIME type, with "-symbolic" prefixes and fallbacks,
|
||||
// and use gtk_icon_theme_choose_icon() instead.
|
||||
// TODO(p): Make sure we have /some/ icon for every entry.
|
||||
// TODO(p): We might want to populate these on an as-needed basis.
|
||||
GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(
|
||||
gtk_icon_theme_get_default(), entry->icon, self->item_height / 2,
|
||||
GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
|
||||
GtkIconTheme *theme = gtk_icon_theme_get_default();
|
||||
GtkIconInfo *icon_info = gtk_icon_theme_lookup_by_gicon(theme, entry->icon,
|
||||
self->item_height / 2, GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
|
||||
if (!icon_info) {
|
||||
// This icon is included within GTK+.
|
||||
icon_info = gtk_icon_theme_lookup_icon(theme, "text-x-generic",
|
||||
self->item_height / 2, GTK_ICON_LOOKUP_FORCE_SYMBOLIC);
|
||||
}
|
||||
if (!icon_info)
|
||||
return;
|
||||
|
||||
|
2
fiv-io.c
2
fiv-io.c
@ -84,7 +84,7 @@
|
||||
#define WUFFS_CONFIG__MODULE__PNG
|
||||
#define WUFFS_CONFIG__MODULE__TGA
|
||||
#define WUFFS_CONFIG__MODULE__ZLIB
|
||||
#include "wuffs-mirror-release-c/release/c/wuffs-v0.3.c"
|
||||
#include "submodules/wuffs-mirror-release-c/release/c/wuffs-v0.3.c"
|
||||
|
||||
#include "fiv-io.h"
|
||||
|
||||
|
385
fiv.c
385
fiv.c
@ -43,6 +43,11 @@
|
||||
#include "fiv-thumbnail.h"
|
||||
#include "fiv-view.h"
|
||||
|
||||
#ifdef HAVE_LCMS2_FAST_FLOAT
|
||||
#include <lcms2.h>
|
||||
#include <lcms2_fast_float.h>
|
||||
#endif // HAVE_LCMS2_FAST_FLOAT
|
||||
|
||||
// --- Utilities ---------------------------------------------------------------
|
||||
|
||||
static void exit_fatal(const char *format, ...) G_GNUC_PRINTF(1, 2);
|
||||
@ -388,12 +393,6 @@ on_about_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
|
||||
GtkStyleContext *style = gtk_widget_get_style_context(widget);
|
||||
gtk_render_background(style, cr, 0, 0, allocation.width, allocation.height);
|
||||
|
||||
// The transformation matrix turns out/is applied wrongly on Quartz.
|
||||
gboolean broken_backend = cairo_surface_get_type(cairo_get_target(cr)) ==
|
||||
CAIRO_SURFACE_TYPE_QUARTZ;
|
||||
if (broken_backend)
|
||||
cairo_push_group(cr);
|
||||
|
||||
cairo_translate(cr, (allocation.width - ABOUT_SIZE * ABOUT_SCALE) / 2,
|
||||
ABOUT_SIZE * ABOUT_SCALE / 4);
|
||||
cairo_scale(cr, ABOUT_SCALE, ABOUT_SCALE);
|
||||
@ -419,11 +418,6 @@ on_about_draw(GtkWidget *widget, cairo_t *cr, gpointer user_data)
|
||||
|
||||
cairo_restore(cr);
|
||||
draw_ligature(cr);
|
||||
|
||||
if (broken_backend) {
|
||||
cairo_pop_group_to_source(cr);
|
||||
cairo_paint(cr);
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
@ -2098,163 +2092,12 @@ static const char stylesheet[] = "@define-color fiv-tile @content_view_bg; \
|
||||
} \
|
||||
.fiv-information label { padding: 0 4px; }";
|
||||
|
||||
static FivThumbnailSize
|
||||
output_thumbnail_prologue(gchar **uris, const char *size_arg)
|
||||
{
|
||||
if (!uris)
|
||||
exit_fatal("No path given");
|
||||
if (uris[1])
|
||||
exit_fatal("Only one thumbnail at a time may be produced");
|
||||
|
||||
FivThumbnailSize size = FIV_THUMBNAIL_SIZE_COUNT;
|
||||
if (size_arg) {
|
||||
for (size = 0; size < FIV_THUMBNAIL_SIZE_COUNT; size++) {
|
||||
if (!strcmp(
|
||||
fiv_thumbnail_sizes[size].thumbnail_spec_name, size_arg))
|
||||
break;
|
||||
}
|
||||
if (size >= FIV_THUMBNAIL_SIZE_COUNT)
|
||||
exit_fatal("unknown thumbnail size: %s", size_arg);
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
_setmode(fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
output_thumbnail_for_search(gchar **uris, const char *size_arg)
|
||||
on_app_startup(GApplication *app, G_GNUC_UNUSED gpointer user_data)
|
||||
{
|
||||
FivThumbnailSize size = output_thumbnail_prologue(uris, size_arg);
|
||||
|
||||
GError *error = NULL;
|
||||
GFile *file = g_file_new_for_uri(uris[0]);
|
||||
cairo_surface_t *surface = NULL;
|
||||
GBytes *bytes = NULL;
|
||||
if ((surface = fiv_thumbnail_produce(file, size, &error)) &&
|
||||
(bytes = fiv_io_serialize_for_search(surface, &error))) {
|
||||
fwrite(
|
||||
g_bytes_get_data(bytes, NULL), 1, g_bytes_get_size(bytes), stdout);
|
||||
g_bytes_unref(bytes);
|
||||
} else {
|
||||
g_assert(error != NULL);
|
||||
}
|
||||
|
||||
g_object_unref(file);
|
||||
if (error)
|
||||
exit_fatal("%s", error->message);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
output_thumbnail(gchar **uris, gboolean extract, const char *size_arg)
|
||||
{
|
||||
FivThumbnailSize size = output_thumbnail_prologue(uris, size_arg);
|
||||
|
||||
GError *error = NULL;
|
||||
GFile *file = g_file_new_for_uri(uris[0]);
|
||||
cairo_surface_t *surface = NULL;
|
||||
if (extract && (surface = fiv_thumbnail_extract(file, size, &error)))
|
||||
fiv_io_serialize_to_stdout(surface, FIV_IO_SERIALIZE_LOW_QUALITY);
|
||||
else if (size_arg &&
|
||||
(g_clear_error(&error),
|
||||
(surface = fiv_thumbnail_produce(file, size, &error))))
|
||||
fiv_io_serialize_to_stdout(surface, 0);
|
||||
else
|
||||
g_assert(error != NULL);
|
||||
|
||||
g_object_unref(file);
|
||||
if (error)
|
||||
exit_fatal("%s", error->message);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
gboolean show_version = FALSE, show_supported_media_types = FALSE,
|
||||
invalidate_cache = FALSE, browse = FALSE, extract_thumbnail = FALSE;
|
||||
gchar **args = NULL, *thumbnail_size = NULL, *thumbnail_size_search = NULL;
|
||||
const GOptionEntry options[] = {
|
||||
{G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &args,
|
||||
NULL, "[PATH | URI]..."},
|
||||
{"browse", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, &browse,
|
||||
"Start in filesystem browsing mode", NULL},
|
||||
{"invalidate-cache", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, &invalidate_cache,
|
||||
"Invalidate the wide thumbnail cache", NULL},
|
||||
{"list-supported-media-types", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, &show_supported_media_types,
|
||||
"Output supported media types and exit", NULL},
|
||||
{"version", 'V', G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_NONE,
|
||||
&show_version, "Output version information and exit", NULL},
|
||||
{},
|
||||
};
|
||||
const GOptionEntry options_internal[] = {
|
||||
{"extract-thumbnail", 0, 0,
|
||||
G_OPTION_ARG_NONE, &extract_thumbnail,
|
||||
"Output any embedded thumbnail (superseding --thumbnail)", NULL},
|
||||
{"thumbnail", 0, 0,
|
||||
G_OPTION_ARG_STRING, &thumbnail_size,
|
||||
"Generate thumbnails, up to SIZE, and output that size", "SIZE"},
|
||||
{"thumbnail-for-search", 0, 0,
|
||||
G_OPTION_ARG_STRING, &thumbnail_size_search,
|
||||
"Output an image file suitable for searching by content", "SIZE"},
|
||||
{},
|
||||
};
|
||||
|
||||
GOptionContext *context =
|
||||
g_option_context_new(" - Image browser and viewer");
|
||||
g_option_context_add_group(context, gtk_get_option_group(TRUE));
|
||||
g_option_context_add_main_entries(context, options, NULL);
|
||||
|
||||
GOptionGroup *internals = g_option_group_new(
|
||||
"internal", "Internal Options:", "Show internal options", NULL, NULL);
|
||||
g_option_group_add_entries(internals, options_internal);
|
||||
g_option_context_add_group(context, internals);
|
||||
|
||||
GError *error = NULL;
|
||||
gboolean initialized =
|
||||
g_option_context_parse(context, &argc, &argv, &error);
|
||||
g_option_context_free(context);
|
||||
if (show_version) {
|
||||
const char *version = PROJECT_VERSION;
|
||||
printf("%s %s\n", PROJECT_NAME, &version[*version == 'v']);
|
||||
return 0;
|
||||
}
|
||||
if (show_supported_media_types) {
|
||||
char **types = fiv_io_all_supported_media_types();
|
||||
for (char **p = types; *p; p++)
|
||||
g_print("%s\n", *p);
|
||||
g_strfreev(types);
|
||||
return 0;
|
||||
}
|
||||
if (invalidate_cache) {
|
||||
fiv_thumbnail_invalidate();
|
||||
return 0;
|
||||
}
|
||||
if (!initialized)
|
||||
exit_fatal("%s", error->message);
|
||||
|
||||
// Normalize all arguments to URIs.
|
||||
for (gsize i = 0; args && args[i]; i++) {
|
||||
GFile *resolved = g_file_new_for_commandline_arg(args[i]);
|
||||
g_free(args[i]);
|
||||
args[i] = g_file_get_uri(resolved);
|
||||
g_object_unref(resolved);
|
||||
}
|
||||
if (thumbnail_size_search) {
|
||||
output_thumbnail_for_search(args, thumbnail_size_search);
|
||||
return 0;
|
||||
}
|
||||
if (extract_thumbnail || thumbnail_size) {
|
||||
output_thumbnail(args, extract_thumbnail, thumbnail_size);
|
||||
return 0;
|
||||
}
|
||||
// We can't prevent GApplication from adding --gapplication-service.
|
||||
if (g_application_get_flags(app) & G_APPLICATION_IS_SERVICE)
|
||||
exit(EXIT_FAILURE);
|
||||
|
||||
// It doesn't make much sense to have command line arguments able to
|
||||
// resolve to the VFS they may end up being contained within.
|
||||
@ -2357,9 +2200,9 @@ main(int argc, char *argv[])
|
||||
gtk_container_add(GTK_CONTAINER(g.stack), g.view_box);
|
||||
gtk_container_add(GTK_CONTAINER(g.stack), g.browser_paned);
|
||||
|
||||
g.window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
|
||||
g_signal_connect(g.window, "destroy",
|
||||
G_CALLBACK(gtk_main_quit), NULL);
|
||||
g.window = gtk_application_window_new(GTK_APPLICATION(app));
|
||||
g_signal_connect_swapped(g.window, "destroy",
|
||||
G_CALLBACK(g_application_quit), app);
|
||||
g_signal_connect(g.window, "key-press-event",
|
||||
G_CALLBACK(on_key_press), NULL);
|
||||
g_signal_connect(g.window, "window-state-event",
|
||||
@ -2410,24 +2253,34 @@ main(int argc, char *argv[])
|
||||
|
||||
// XXX: The widget wants to read the display's profile. The realize is ugly.
|
||||
gtk_widget_realize(g.view);
|
||||
}
|
||||
|
||||
static struct {
|
||||
gboolean browse, extract_thumbnail;
|
||||
gchar **args, *thumbnail_size, *thumbnail_size_search;
|
||||
} o;
|
||||
|
||||
static void
|
||||
on_app_activate(
|
||||
G_GNUC_UNUSED GApplication *app, G_GNUC_UNUSED gpointer user_data)
|
||||
{
|
||||
// XXX: We follow the behaviour of Firefox and Eye of GNOME, which both
|
||||
// interpret multiple command line arguments differently, as a collection.
|
||||
// However, single-element collections are unrepresentable this way.
|
||||
// Should we allow multiple targets only in a special new mode?
|
||||
g.files_index = -1;
|
||||
if (args) {
|
||||
const gchar *target = *args;
|
||||
if (args[1]) {
|
||||
fiv_collection_reload(args);
|
||||
if (o.args) {
|
||||
const gchar *target = *o.args;
|
||||
if (o.args[1]) {
|
||||
fiv_collection_reload(o.args);
|
||||
target = FIV_COLLECTION_SCHEME ":/";
|
||||
}
|
||||
|
||||
GFile *file = g_file_new_for_uri(target);
|
||||
open_any_file(file, browse);
|
||||
open_any_file(file, o.browse);
|
||||
g_object_unref(file);
|
||||
g_strfreev(args);
|
||||
}
|
||||
|
||||
if (!g.directory) {
|
||||
GFile *file = g_file_new_for_path(".");
|
||||
open_any_file(file, FALSE);
|
||||
@ -2435,6 +2288,184 @@ main(int argc, char *argv[])
|
||||
}
|
||||
|
||||
gtk_widget_show(g.window);
|
||||
gtk_main();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// --- Plumbing ----------------------------------------------------------------
|
||||
|
||||
static FivThumbnailSize
|
||||
output_thumbnail_prologue(gchar **uris, const char *size_arg)
|
||||
{
|
||||
if (!uris)
|
||||
exit_fatal("No path given");
|
||||
if (uris[1])
|
||||
exit_fatal("Only one thumbnail at a time may be produced");
|
||||
|
||||
FivThumbnailSize size = FIV_THUMBNAIL_SIZE_COUNT;
|
||||
if (size_arg) {
|
||||
for (size = 0; size < FIV_THUMBNAIL_SIZE_COUNT; size++) {
|
||||
if (!strcmp(
|
||||
fiv_thumbnail_sizes[size].thumbnail_spec_name, size_arg))
|
||||
break;
|
||||
}
|
||||
if (size >= FIV_THUMBNAIL_SIZE_COUNT)
|
||||
exit_fatal("unknown thumbnail size: %s", size_arg);
|
||||
}
|
||||
|
||||
#ifdef G_OS_WIN32
|
||||
_setmode(fileno(stdout), _O_BINARY);
|
||||
#endif
|
||||
return size;
|
||||
}
|
||||
|
||||
static void
|
||||
output_thumbnail_for_search(gchar **uris, const char *size_arg)
|
||||
{
|
||||
FivThumbnailSize size = output_thumbnail_prologue(uris, size_arg);
|
||||
|
||||
GError *error = NULL;
|
||||
GFile *file = g_file_new_for_uri(uris[0]);
|
||||
cairo_surface_t *surface = NULL;
|
||||
GBytes *bytes = NULL;
|
||||
if ((surface = fiv_thumbnail_produce(file, size, &error)) &&
|
||||
(bytes = fiv_io_serialize_for_search(surface, &error))) {
|
||||
fwrite(
|
||||
g_bytes_get_data(bytes, NULL), 1, g_bytes_get_size(bytes), stdout);
|
||||
g_bytes_unref(bytes);
|
||||
} else {
|
||||
g_assert(error != NULL);
|
||||
}
|
||||
|
||||
g_object_unref(file);
|
||||
if (error)
|
||||
exit_fatal("%s", error->message);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static void
|
||||
output_thumbnail(gchar **uris, gboolean extract, const char *size_arg)
|
||||
{
|
||||
FivThumbnailSize size = output_thumbnail_prologue(uris, size_arg);
|
||||
|
||||
GError *error = NULL;
|
||||
GFile *file = g_file_new_for_uri(uris[0]);
|
||||
cairo_surface_t *surface = NULL;
|
||||
if (extract && (surface = fiv_thumbnail_extract(file, size, &error)))
|
||||
fiv_io_serialize_to_stdout(surface, FIV_IO_SERIALIZE_LOW_QUALITY);
|
||||
else if (size_arg &&
|
||||
(g_clear_error(&error),
|
||||
(surface = fiv_thumbnail_produce(file, size, &error))))
|
||||
fiv_io_serialize_to_stdout(surface, 0);
|
||||
else
|
||||
g_assert(error != NULL);
|
||||
|
||||
g_object_unref(file);
|
||||
if (error)
|
||||
exit_fatal("%s", error->message);
|
||||
|
||||
cairo_surface_destroy(surface);
|
||||
}
|
||||
|
||||
static gint
|
||||
on_app_handle_local_options(G_GNUC_UNUSED GApplication *app,
|
||||
GVariantDict *options, G_GNUC_UNUSED gpointer user_data)
|
||||
{
|
||||
if (g_variant_dict_contains(options, "version")) {
|
||||
const char *version = PROJECT_VERSION;
|
||||
printf("%s %s\n", PROJECT_NAME, &version[*version == 'v']);
|
||||
return 0;
|
||||
}
|
||||
if (g_variant_dict_contains(options, "list-supported-media-types")) {
|
||||
char **types = fiv_io_all_supported_media_types();
|
||||
for (char **p = types; *p; p++)
|
||||
g_print("%s\n", *p);
|
||||
g_strfreev(types);
|
||||
return 0;
|
||||
}
|
||||
if (g_variant_dict_contains(options, "invalidate-cache")) {
|
||||
fiv_thumbnail_invalidate();
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(p): Use Little CMS with contexts instead.
|
||||
#ifdef HAVE_LCMS2_FAST_FLOAT
|
||||
cmsPlugin(cmsFastFloatExtensions());
|
||||
#endif // HAVE_LCMS2_FAST_FLOAT
|
||||
|
||||
// Normalize all arguments to URIs, and run thumbnailing modes first.
|
||||
for (gsize i = 0; o.args && o.args[i]; i++) {
|
||||
GFile *resolved = g_file_new_for_commandline_arg(o.args[i]);
|
||||
g_free(o.args[i]);
|
||||
o.args[i] = g_file_get_uri(resolved);
|
||||
g_object_unref(resolved);
|
||||
}
|
||||
|
||||
// These come from an option group that doesn't get copied to "options".
|
||||
if (o.thumbnail_size_search) {
|
||||
output_thumbnail_for_search(o.args, o.thumbnail_size_search);
|
||||
return 0;
|
||||
}
|
||||
if (o.extract_thumbnail || o.thumbnail_size) {
|
||||
output_thumbnail(o.args, o.extract_thumbnail, o.thumbnail_size);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char *argv[])
|
||||
{
|
||||
const GOptionEntry options[] = {
|
||||
{G_OPTION_REMAINING, 0, 0,
|
||||
G_OPTION_ARG_FILENAME_ARRAY, &o.args,
|
||||
NULL, "[PATH | URI]..."},
|
||||
{"browse", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, &o.browse,
|
||||
"Start in filesystem browsing mode", NULL},
|
||||
{"invalidate-cache", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, NULL,
|
||||
"Invalidate the wide thumbnail cache", NULL},
|
||||
{"list-supported-media-types", 0, G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, NULL,
|
||||
"Output supported media types and exit", NULL},
|
||||
{"version", 'V', G_OPTION_FLAG_IN_MAIN,
|
||||
G_OPTION_ARG_NONE, NULL,
|
||||
"Output version information and exit", NULL},
|
||||
{},
|
||||
};
|
||||
const GOptionEntry options_internal[] = {
|
||||
{"extract-thumbnail", 0, 0,
|
||||
G_OPTION_ARG_NONE, &o.extract_thumbnail,
|
||||
"Output any embedded thumbnail (superseding --thumbnail)", NULL},
|
||||
{"thumbnail", 0, 0,
|
||||
G_OPTION_ARG_STRING, &o.thumbnail_size,
|
||||
"Generate thumbnails, up to SIZE, and output that size", "SIZE"},
|
||||
{"thumbnail-for-search", 0, 0,
|
||||
G_OPTION_ARG_STRING, &o.thumbnail_size_search,
|
||||
"Output an image file suitable for searching by content", "SIZE"},
|
||||
{},
|
||||
};
|
||||
|
||||
// We never get the ::open signal, thanks to G_OPTION_ARG_FILENAME_ARRAY.
|
||||
GtkApplication *app = gtk_application_new(NULL, G_APPLICATION_NON_UNIQUE);
|
||||
g_application_set_option_context_parameter_string(
|
||||
G_APPLICATION(app), " - Image browser and viewer");
|
||||
g_application_add_main_option_entries(G_APPLICATION(app), options);
|
||||
|
||||
GOptionGroup *internals = g_option_group_new(
|
||||
"internal", "Internal Options:", "Show internal options", NULL, NULL);
|
||||
g_option_group_add_entries(internals, options_internal);
|
||||
g_application_add_option_group(G_APPLICATION(app), internals);
|
||||
|
||||
g_signal_connect(app, "handle-local-options",
|
||||
G_CALLBACK(on_app_handle_local_options), NULL);
|
||||
g_signal_connect(app, "startup",
|
||||
G_CALLBACK(on_app_startup), NULL);
|
||||
g_signal_connect(app, "activate",
|
||||
G_CALLBACK(on_app_activate), NULL);
|
||||
|
||||
int status = g_application_run(G_APPLICATION(app), argc, argv);
|
||||
g_object_unref(app);
|
||||
g_strfreev(o.args);
|
||||
return status;
|
||||
}
|
||||
|
25
meson.build
25
meson.build
@ -25,11 +25,12 @@ libjpegqs = dependency('libjpegqs', required : get_option('libjpegqs'),
|
||||
lcms2 = dependency('lcms2', required : get_option('lcms2'))
|
||||
# Note that only libraw_r is thread-safe, but we'll just run it out-of process.
|
||||
libraw = dependency('libraw', required : get_option('libraw'))
|
||||
# This is a direct runtime dependency, but its usage may be disabled for images.
|
||||
librsvg = dependency('librsvg-2.0', required : get_option('librsvg'))
|
||||
xcursor = dependency('xcursor', required : get_option('xcursor'))
|
||||
libheif = dependency('libheif', required : get_option('libheif'))
|
||||
libtiff = dependency('libtiff-4', required : get_option('libtiff'))
|
||||
# This is a direct dependency of GTK+, but its usage may be disabled.
|
||||
# This is a direct dependency of GTK+, but its usage may be disabled for images.
|
||||
gdkpixbuf = dependency('gdk-pixbuf-2.0', required : get_option('gdk-pixbuf'))
|
||||
dependencies = [
|
||||
dependency('gtk+-3.0'),
|
||||
@ -53,6 +54,24 @@ dependencies = [
|
||||
cc.find_library('m', required : false),
|
||||
]
|
||||
|
||||
# As of writing, no pkg-config file is produced, and the plugin is not installed
|
||||
# by default. The library can be built statically, but it's a bit of a hassle.
|
||||
have_lcms2_fast_float = false
|
||||
if not get_option('lcms2fastfloat').disabled()
|
||||
lcms2ff = dependency('lcms2_fast_float', required : false)
|
||||
if not lcms2ff.found()
|
||||
lcms2ff = cc.find_library(
|
||||
'lcms2_fast_float', required : get_option('lcms2fastfloat'))
|
||||
if lcms2ff.found() and not cc.has_header('lcms2_fast_float.h')
|
||||
error('lcms2_fast_float.h not found')
|
||||
endif
|
||||
endif
|
||||
if lcms2ff.found()
|
||||
dependencies += lcms2ff
|
||||
have_lcms2_fast_float = true
|
||||
endif
|
||||
endif
|
||||
|
||||
# As of writing, the API is unstable, and no pkg-config file is produced.
|
||||
# Trying to wrap Cargo in Meson is a recipe for pain, so no version pinning.
|
||||
have_resvg = false
|
||||
@ -85,6 +104,7 @@ endif
|
||||
|
||||
conf.set('HAVE_JPEG_QS', libjpegqs.found())
|
||||
conf.set('HAVE_LCMS2', lcms2.found())
|
||||
conf.set('HAVE_LCMS2_FAST_FLOAT', have_lcms2_fast_float)
|
||||
conf.set('HAVE_LIBRAW', libraw.found())
|
||||
conf.set('HAVE_RESVG', have_resvg)
|
||||
conf.set('HAVE_LIBRSVG', librsvg.found())
|
||||
@ -236,7 +256,8 @@ if not win32
|
||||
else
|
||||
command = ['env', 'LC_ALL=C',
|
||||
'asciidoc-release-version=' + meson.project_version(),
|
||||
'awk', '-f', files('liberty/tools/asciiman.awk'), '@INPUT@']
|
||||
'awk', '-f', files('submodules/liberty/tools/asciiman.awk'),
|
||||
'@INPUT@']
|
||||
man_capture = true
|
||||
endif
|
||||
custom_target('manpage for ' + page,
|
||||
|
@ -3,6 +3,8 @@ option('tools', type : 'feature', value : 'disabled',
|
||||
|
||||
option('lcms2', type : 'feature', value : 'auto',
|
||||
description : 'Build with Little CMS colour management')
|
||||
option('lcms2fastfloat', type : 'feature', value : 'auto',
|
||||
description : 'Build with Little CMS fast float plugin support')
|
||||
option('libjpegqs', type : 'feature', value : 'auto',
|
||||
description : 'Build with JPEG Quant Smooth integration')
|
||||
option('libraw', type : 'feature', value : 'auto',
|
||||
|
@ -97,7 +97,7 @@ setup() {
|
||||
endian = 'little'
|
||||
EOF
|
||||
|
||||
meson --buildtype=debugoptimized --prefix="$packagedir" \
|
||||
meson setup --buildtype=debugoptimized --prefix="$packagedir" \
|
||||
--bindir . --libdir . --cross-file="$toolchain" "$builddir" "$sourcedir"
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user