Begin implementing thumbnailer for Linux

This commit is contained in:
ISSOtm
2024-06-25 18:53:37 +02:00
parent 08178c9f3a
commit e4ceb3d93b
3 changed files with 205 additions and 3 deletions

View File

@@ -217,6 +217,14 @@ SDL_LDFLAGS += $(shell $(PKG_CONFIG) --libs openal)
SDL_AUDIO_DRIVERS += openal SDL_AUDIO_DRIVERS += openal
endif endif
endif endif
GIO_CFLAGS := $(shell $(PKG_CONFIG) --cflags gio-2.0) -DG_LOG_USE_STRUCTURED
GIO_LDFLAGS := $(shell $(PKG_CONFIG) --libs gio-2.0)
ifeq ($(CONF),debug)
GIO_CFLAGS += -DG_ENABLE_DEBUG
else
GIO_CFLAGS += -DG_DISABLE_ASSERT
endif
endif endif
ifeq (,$(PKG_CONFIG)) ifeq (,$(PKG_CONFIG))
@@ -330,6 +338,7 @@ endif
cocoa: $(BIN)/SameBoy.app cocoa: $(BIN)/SameBoy.app
quicklook: $(BIN)/SameBoy.qlgenerator quicklook: $(BIN)/SameBoy.qlgenerator
xdg-thumbnailer: $(BIN)/XdgThumbnailer/sameboy-thumbnailer
sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/mgb_boot.bin $(BIN)/SDL/cgb0_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders $(BIN)/SDL/Palettes sdl: $(SDL_TARGET) $(BIN)/SDL/dmg_boot.bin $(BIN)/SDL/mgb_boot.bin $(BIN)/SDL/cgb0_boot.bin $(BIN)/SDL/cgb_boot.bin $(BIN)/SDL/agb_boot.bin $(BIN)/SDL/sgb_boot.bin $(BIN)/SDL/sgb2_boot.bin $(BIN)/SDL/LICENSE $(BIN)/SDL/registers.sym $(BIN)/SDL/background.bmp $(BIN)/SDL/Shaders $(BIN)/SDL/Palettes
bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/cgb0_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/mgb_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin bootroms: $(BIN)/BootROMs/agb_boot.bin $(BIN)/BootROMs/cgb_boot.bin $(BIN)/BootROMs/cgb0_boot.bin $(BIN)/BootROMs/dmg_boot.bin $(BIN)/BootROMs/mgb_boot.bin $(BIN)/BootROMs/sgb_boot.bin $(BIN)/BootROMs/sgb2_boot.bin
tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin tester: $(TESTER_TARGET) $(BIN)/tester/dmg_boot.bin $(BIN)/tester/cgb_boot.bin $(BIN)/tester/agb_boot.bin $(BIN)/tester/sgb_boot.bin $(BIN)/tester/sgb2_boot.bin
@@ -355,6 +364,7 @@ TESTER_SOURCES := $(shell ls Tester/*.c)
IOS_SOURCES := $(filter-out iOS/installer.m, $(shell ls iOS/*.m)) $(shell ls AppleCommon/*.m) IOS_SOURCES := $(filter-out iOS/installer.m, $(shell ls iOS/*.m)) $(shell ls AppleCommon/*.m)
COCOA_SOURCES := $(shell ls Cocoa/*.m) $(shell ls HexFiend/*.m) $(shell ls JoyKit/*.m) $(shell ls AppleCommon/*.m) COCOA_SOURCES := $(shell ls Cocoa/*.m) $(shell ls HexFiend/*.m) $(shell ls JoyKit/*.m) $(shell ls AppleCommon/*.m)
QUICKLOOK_SOURCES := $(shell ls QuickLook/*.m) $(shell ls QuickLook/*.c) QUICKLOOK_SOURCES := $(shell ls QuickLook/*.m) $(shell ls QuickLook/*.c)
XDG_THUMBNAILER_SOURCES := $(shell ls XdgThumbnailer/*.c)
ifeq ($(PLATFORM),windows32) ifeq ($(PLATFORM),windows32)
CORE_SOURCES += $(shell ls Windows/*.c) CORE_SOURCES += $(shell ls Windows/*.c)
@@ -367,6 +377,7 @@ IOS_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(IOS_SOURCES))
QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES)) QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES))
SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES)) SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_SOURCES))
TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES)) TESTER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(TESTER_SOURCES))
XDG_THUMBNAILER_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(XDG_THUMBNAILER_SOURCES)) $(OBJ)/XdgThumbnailer/interface.c.o
lib: $(PUBLIC_HEADERS) lib: $(PUBLIC_HEADERS)
@@ -410,6 +421,20 @@ $(OBJ)/SDL/%.c.o: SDL/%.c
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
$(OBJ)/XdgThumbnailer/%.c.o: XdgThumbnailer/%.c
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(GIO_CFLAGS) -c $< -o $@
# Make sure not to attempt compiling this before generating the interface code.
$(OBJ)/XdgThumbnailer/main.c.o: $(OBJ)/XdgThumbnailer/interface.h
# Silence warnings for this. It is code generated not by us, so we do not want `-Werror` to break
# compilation with some version of the generator and/or compiler.
$(OBJ)/XdgThumbnailer/interface.c.o: $(OBJ)/XdgThumbnailer/interface.c
-@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(GIO_CFLAGS) -w -c $< -o $@
$(OBJ)/XdgThumbnailer/interface.c $(OBJ)/XdgThumbnailer/interface.h: XdgThumbnailer/interface.xml
-@$(MKDIR) -p $(dir $@)
gdbus-codegen --c-generate-autocleanup none --c-namespace Thumbnailer --interface-prefix org.freedesktop.thumbnails. --generate-c-code $(OBJ)/XdgThumbnailer/interface $<
$(OBJ)/OpenDialog/%.c.o: OpenDialog/%.c $(OBJ)/OpenDialog/%.c.o: OpenDialog/%.c
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@ $(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
@@ -531,6 +556,12 @@ $(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs
-@$(MKDIR) -p $(dir $@) -@$(MKDIR) -p $(dir $@)
cp -f $^ $@ cp -f $^ $@
# XDG thumbnailer
$(BIN)/XdgThumbnailer/sameboy-thumbnailer: $(CORE_OBJECTS) $(XDG_THUMBNAILER_OBJECTS)
-@$(MKDIR) -p $(dir $@)
$(CC) $^ -o $@ $(LDFLAGS) $(FAT_FLAGS) $(GIO_LDFLAGS)
# SDL Port # SDL Port
# Unix versions build only one binary # Unix versions build only one binary

View File

@@ -0,0 +1,32 @@
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN"
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
<node name="/com/github/liji32/sameboy/XdgThumbnailer">
<interface name="org.freedesktop.thumbnails.SpecializedThumbnailer1">
<method name="Queue">
<arg type="s" name="uri" direction="in" />
<arg type="s" name="mime_type" direction="in" />
<arg type="s" name="flavor" direction="in" />
<arg type="b" name="urgent" direction="in" />
<arg type="u" name="handle" direction="out" />
</method>
<method name="Dequeue">
<arg type="u" name="handle" direction="in" />
</method>
<signal name="Ready">
<arg type="u" name="handle" />
<arg type="s" name="uri" />
</signal>
<signal name="Started">
<arg type="u" name="handle" />
</signal>
<signal name="Finished">
<arg type="u" name="handle" />
</signal>
<signal name="Error">
<arg type="u" name="handle" />
<arg type="s" name="failed_uri" />
<arg type="i" name="error_code" />
<arg type="s" name="message" />
</signal>
</interface>
</node>

139
XdgThumbnailer/main.c Normal file
View File

@@ -0,0 +1,139 @@
#define G_LOG_DOMAIN "sameboy-thumbnailer"
#include <gio/gio.h>
#include <glib-object.h>
#include <glib-unix.h>
#include <glib.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
// Auto-generated via `gdbus-codegen` from `interface.xml`.
#include "build/obj/XdgThumbnailer/interface.h"
static char const *const name_on_bus = "com.github.liji32.sameboy.XdgThumbnailer";
static char const *const object_path = "/com/github/liji32/sameboy/XdgThumbnailer";
/* --- The main work being performed here --- */
static GThreadPool *thread_pool;
// The function called by the threads in `thread_pool`.
static void generate_thumbnail(void *data, void *user_data)
{
// TODO
}
static gboolean handle_queue(ThumbnailerSpecializedThumbnailer1 *object,
GDBusMethodInvocation *invocation, char const *uri, char const *mime_type,
char const *flavor, gboolean urgent)
{
g_info("Received Queue(uri=\"%s\", mime_type=\"%s\", flavor=\"%s\", urgent=%s) request", uri, mime_type, flavor, urgent ? "true" : "false");
// TODO
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
static gboolean handle_dequeue(ThumbnailerSpecializedThumbnailer1 *object,
GDBusMethodInvocation *invocation, unsigned handle)
{
g_info("Received Dequeue(handle=%u) request", handle);
// TODO
return G_DBUS_METHOD_INVOCATION_HANDLED;
}
/* --- "Glue"; or, how the above is orchestrated / wired up --- */
static GMainLoop *main_loop;
static void on_bus_acquired(GDBusConnection *connection, const char *name, void *user_data)
{
g_assert_cmpstr(name, ==, name_on_bus);
(void)user_data;
g_info("Acquired bus");
GError *error;
// Create the interface, and hook up callbacks for when its methods are called.
ThumbnailerSpecializedThumbnailer1 *thumbnailer_interface =
thumbnailer_specialized_thumbnailer1_skeleton_new();
g_signal_connect(thumbnailer_interface, "handle-queue", G_CALLBACK(handle_queue), NULL);
g_signal_connect(thumbnailer_interface, "handle-dequeue", G_CALLBACK(handle_dequeue), NULL);
// Export the interface on the bus.
error = NULL;
GDBusInterfaceSkeleton *interface = G_DBUS_INTERFACE_SKELETON(thumbnailer_interface);
gboolean res = g_dbus_interface_skeleton_export(interface, connection, object_path, &error);
g_assert(res);
g_assert_no_error(error);
if (error) {
g_error("Error exporting interface \"%s\" at \"%s\": %s",
g_dbus_interface_skeleton_get_info(interface)->name, object_path, error->message);
// NOTREACHED
}
}
static void on_name_acquired(GDBusConnection *connection, const char *name, void *user_data)
{
g_assert_cmpstr(name, ==, name_on_bus);
(void)user_data;
g_info("Acquired the name \"%s\" on the session bus", name);
}
static void on_name_lost(GDBusConnection *connection, const char *name, void *user_data)
{
g_assert_cmpstr(name, ==, name_on_bus);
(void)user_data;
if (connection != NULL) {
g_info("Lost the name \"%s\" on the session bus", name);
}
else {
g_error("Failed to connect to session bus");
}
g_main_loop_quit(main_loop);
}
static gboolean handle_sigterm(void *user_data)
{
g_info("SIGTERM received! Quitting...");
g_main_loop_quit(main_loop);
return G_SOURCE_CONTINUE; // Do not remove this source ourselves, let the post-main loop do so.
}
int main(int argc, char const *argv[])
{
GError *error;
// Create the thread pool *before* starting to accept tasks from D-Bus.
// Make it non-exclusive so that the number of spawned threads grows dynamically, to consume
// fewer system resources when no thumbnails are being generated.
thread_pool =
g_thread_pool_new(generate_thumbnail, NULL, g_get_num_processors(), FALSE, &error);
g_assert_no_error(error); // Creating a non-exclusive thread pool cannot generate errors.
// Likewise, create the main loop before then, so it can be aborted even before entering it.
main_loop = g_main_loop_new(NULL, FALSE);
// Refuse to replace the name or be replaced; there should only be one instance of the
// thumbnailer on the bus at all times. To replace this program, kill it.
unsigned owner_id =
g_bus_own_name(G_BUS_TYPE_SESSION, name_on_bus, G_BUS_NAME_OWNER_FLAGS_DO_NOT_QUEUE,
on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL);
unsigned sigterm_source_id = g_unix_signal_add(SIGTERM, handle_sigterm, NULL);
g_main_loop_run(main_loop);
gboolean removed =
g_source_remove(sigterm_source_id); // This must be done before destroying the main loop.
g_assert(removed);
g_info("Waiting for outstanding tasks...");
g_thread_pool_free(thread_pool, FALSE, TRUE);
g_main_loop_unref(main_loop);
g_bus_unown_name(owner_id);
return 0;
}