Convert to strictly non-unique GtkApplication

It's not pretty, but it works.
This commit is contained in:
Přemysl Eric Janouch 2023-06-02 13:01:17 +02:00
parent 6a7c86a41b
commit abf4f1a792
Signed by: p
GPG Key ID: A0420B94F92B9493

214
fiv.c
View File

@ -2161,89 +2161,12 @@ output_thumbnail(gchar **uris, gboolean extract, const char *size_arg)
cairo_surface_destroy(surface);
}
int
main(int argc, char *argv[])
static void
on_app_startup(GApplication *app, G_GNUC_UNUSED gpointer user_data)
{
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.
@ -2346,9 +2269,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",
@ -2399,24 +2322,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);
@ -2424,6 +2357,103 @@ main(int argc, char *argv[])
}
gtk_widget_show(g.window);
gtk_main();
}
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;
}
// 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;
}