mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-09-02 04:52:38 +02:00
Begin implementing thumbnailer for Linux
This commit is contained in:
37
Makefile
37
Makefile
@@ -217,6 +217,14 @@ SDL_LDFLAGS += $(shell $(PKG_CONFIG) --libs openal)
|
||||
SDL_AUDIO_DRIVERS += openal
|
||||
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
|
||||
|
||||
ifeq (,$(PKG_CONFIG))
|
||||
@@ -330,6 +338,7 @@ endif
|
||||
|
||||
cocoa: $(BIN)/SameBoy.app
|
||||
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
|
||||
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
|
||||
@@ -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)
|
||||
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)
|
||||
XDG_THUMBNAILER_SOURCES := $(shell ls XdgThumbnailer/*.c)
|
||||
|
||||
ifeq ($(PLATFORM),windows32)
|
||||
CORE_SOURCES += $(shell ls Windows/*.c)
|
||||
@@ -367,6 +377,7 @@ IOS_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(IOS_SOURCES))
|
||||
QUICKLOOK_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(QUICKLOOK_SOURCES))
|
||||
SDL_OBJECTS := $(patsubst %,$(OBJ)/%.o,$(SDL_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)
|
||||
|
||||
@@ -410,6 +421,20 @@ $(OBJ)/SDL/%.c.o: SDL/%.c
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(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
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(SDL_CFLAGS) $(GL_CFLAGS) -c $< -o $@
|
||||
@@ -427,7 +452,7 @@ $(OBJ)/HexFiend/%.m.o: HexFiend/%.m
|
||||
$(OBJ)/%.m.o: %.m
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
$(CC) $(CFLAGS) $(FRONTEND_CFLAGS) $(FAT_FLAGS) $(OCFLAGS) -c $< -o $@
|
||||
|
||||
|
||||
# iOS Port
|
||||
|
||||
$(BIN)/SameBoy-iOS.app: $(BIN)/SameBoy-iOS.app/SameBoy \
|
||||
@@ -530,7 +555,13 @@ endif
|
||||
$(BIN)/SameBoy.qlgenerator/Contents/Resources/cgb_boot_fast.bin: $(BIN)/BootROMs/cgb_boot_fast.bin
|
||||
-@$(MKDIR) -p $(dir $@)
|
||||
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
|
||||
|
||||
# Unix versions build only one binary
|
||||
@@ -615,7 +646,7 @@ $(BIN)/SDL/background.bmp: SDL/background.bmp
|
||||
$(BIN)/SDL/Shaders: Shaders
|
||||
-@$(MKDIR) -p $@
|
||||
cp -rf Shaders/*.fsh $@
|
||||
|
||||
|
||||
$(BIN)/SDL/Palettes: Misc/Palettes
|
||||
-@$(MKDIR) -p $@
|
||||
cp -rf Misc/Palettes/*.sbp $@
|
||||
|
32
XdgThumbnailer/interface.xml
Normal file
32
XdgThumbnailer/interface.xml
Normal 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
139
XdgThumbnailer/main.c
Normal 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;
|
||||
}
|
Reference in New Issue
Block a user