2026-09-16 19:24:59 +02:00
|
|
|
#include "my_application.h"
|
|
|
|
|
|
|
|
|
|
#include <flutter_linux/flutter_linux.h>
|
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
|
#include <gdk/gdkx.h>
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "flutter/generated_plugin_registrant.h"
|
|
|
|
|
|
|
|
|
|
struct _MyApplication {
|
|
|
|
|
GtkApplication parent_instance;
|
|
|
|
|
char** dart_entrypoint_arguments;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
|
|
|
|
|
|
|
|
|
|
// Called when first Flutter frame received.
|
|
|
|
|
static void first_frame_cb(MyApplication* self, FlView* view) {
|
|
|
|
|
gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view)));
|
|
|
|
|
}
|
|
|
|
|
|
fix(app): the app has its own icon instead of Flutter's
The launcher showed Flutter's logo: the project template's icons were
never replaced. tool/app_icons.py now makes every icon from the
kingfisher (assets/icon/halkyon_icon.png), through flutter_launcher_icons
v0.14.4 configured in the app's pubspec: Android's adaptive icon (the
kingfisher inset 16 % on white, for each launcher to cut to its shape)
and the square one before Android 8; iOS's, without transparency;
macOS's, the rounded tile on Apple's grid (824 of 1024 pixels); and
Windows', the rounded tile, its .ico with every size from 16 to 256
pixels (the tool writes only 256). It keeps the iOS project file as it
was, which the tool would break (flutter_launcher_icons issue 634), and
writes the snap's icon as tool/snap_icon.py did, which it replaces.
The Linux runner gives its window the bundled icon too, for desktops
that don't take it from the desktop file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-06 22:49:25 +02:00
|
|
|
// Gives the window the app's icon.
|
|
|
|
|
//
|
|
|
|
|
// Desktops take an application's icon from the icon theme, by its application
|
|
|
|
|
// ID, once it's installed with a desktop file and an icon, as the snap is. A
|
|
|
|
|
// bundle run as it is has neither, so the window falls back to the PNG the app
|
|
|
|
|
// bundles anyway (pubspec.yaml declares it an asset): data/flutter_assets/
|
|
|
|
|
// beside the executable. On Wayland the compositor takes the icon of the
|
|
|
|
|
// window's desktop file and ignores this.
|
|
|
|
|
static void set_window_icon(GtkWindow* window) {
|
|
|
|
|
gtk_window_set_icon_name(window, APPLICATION_ID);
|
|
|
|
|
|
|
|
|
|
g_autofree gchar* executable = g_file_read_link("/proc/self/exe", nullptr);
|
|
|
|
|
if (executable == nullptr) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
g_autofree gchar* bundle = g_path_get_dirname(executable);
|
|
|
|
|
g_autofree gchar* icon_path =
|
|
|
|
|
g_build_filename(bundle, "data", "flutter_assets", "assets", "icon",
|
2026-10-28 10:42:47 +01:00
|
|
|
"halkyon_learn_icon.png", nullptr);
|
fix(app): the app has its own icon instead of Flutter's
The launcher showed Flutter's logo: the project template's icons were
never replaced. tool/app_icons.py now makes every icon from the
kingfisher (assets/icon/halkyon_icon.png), through flutter_launcher_icons
v0.14.4 configured in the app's pubspec: Android's adaptive icon (the
kingfisher inset 16 % on white, for each launcher to cut to its shape)
and the square one before Android 8; iOS's, without transparency;
macOS's, the rounded tile on Apple's grid (824 of 1024 pixels); and
Windows', the rounded tile, its .ico with every size from 16 to 256
pixels (the tool writes only 256). It keeps the iOS project file as it
was, which the tool would break (flutter_launcher_icons issue 634), and
writes the snap's icon as tool/snap_icon.py did, which it replaces.
The Linux runner gives its window the bundled icon too, for desktops
that don't take it from the desktop file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-06 22:49:25 +02:00
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
if (!gtk_window_set_icon_from_file(window, icon_path, &error)) {
|
|
|
|
|
g_warning("Couldn't load the window icon %s: %s", icon_path,
|
|
|
|
|
error->message);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-09-16 19:24:59 +02:00
|
|
|
// Implements GApplication::activate.
|
|
|
|
|
static void my_application_activate(GApplication* application) {
|
|
|
|
|
MyApplication* self = MY_APPLICATION(application);
|
|
|
|
|
GtkWindow* window =
|
|
|
|
|
GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
|
|
|
|
|
|
|
|
|
|
// Use a header bar when running in GNOME as this is the common style used
|
|
|
|
|
// by applications and is the setup most users will be using (e.g. Ubuntu
|
|
|
|
|
// desktop).
|
|
|
|
|
// If running on X and not using GNOME then just use a traditional title bar
|
|
|
|
|
// in case the window manager does more exotic layout, e.g. tiling.
|
|
|
|
|
// If running on Wayland assume the header bar will work (may need changing
|
|
|
|
|
// if future cases occur).
|
|
|
|
|
gboolean use_header_bar = TRUE;
|
|
|
|
|
#ifdef GDK_WINDOWING_X11
|
|
|
|
|
GdkScreen* screen = gtk_window_get_screen(window);
|
|
|
|
|
if (GDK_IS_X11_SCREEN(screen)) {
|
|
|
|
|
const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
|
|
|
|
|
if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
|
|
|
|
|
use_header_bar = FALSE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
if (use_header_bar) {
|
|
|
|
|
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
|
|
|
|
gtk_widget_show(GTK_WIDGET(header_bar));
|
2026-09-24 13:21:37 +02:00
|
|
|
gtk_header_bar_set_title(header_bar, "Halkyon Learn");
|
2026-09-16 19:24:59 +02:00
|
|
|
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
|
|
|
|
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
|
|
|
|
} else {
|
2026-09-24 13:21:37 +02:00
|
|
|
gtk_window_set_title(window, "Halkyon Learn");
|
2026-09-16 19:24:59 +02:00
|
|
|
}
|
|
|
|
|
|
fix(app): the app has its own icon instead of Flutter's
The launcher showed Flutter's logo: the project template's icons were
never replaced. tool/app_icons.py now makes every icon from the
kingfisher (assets/icon/halkyon_icon.png), through flutter_launcher_icons
v0.14.4 configured in the app's pubspec: Android's adaptive icon (the
kingfisher inset 16 % on white, for each launcher to cut to its shape)
and the square one before Android 8; iOS's, without transparency;
macOS's, the rounded tile on Apple's grid (824 of 1024 pixels); and
Windows', the rounded tile, its .ico with every size from 16 to 256
pixels (the tool writes only 256). It keeps the iOS project file as it
was, which the tool would break (flutter_launcher_icons issue 634), and
writes the snap's icon as tool/snap_icon.py did, which it replaces.
The Linux runner gives its window the bundled icon too, for desktops
that don't take it from the desktop file.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
2026-10-06 22:49:25 +02:00
|
|
|
set_window_icon(window);
|
2026-09-16 19:24:59 +02:00
|
|
|
gtk_window_set_default_size(window, 1280, 720);
|
|
|
|
|
|
|
|
|
|
g_autoptr(FlDartProject) project = fl_dart_project_new();
|
|
|
|
|
fl_dart_project_set_dart_entrypoint_arguments(
|
|
|
|
|
project, self->dart_entrypoint_arguments);
|
|
|
|
|
|
|
|
|
|
FlView* view = fl_view_new(project);
|
|
|
|
|
GdkRGBA background_color;
|
|
|
|
|
// Background defaults to black, override it here if necessary, e.g. #00000000
|
|
|
|
|
// for transparent.
|
|
|
|
|
gdk_rgba_parse(&background_color, "#000000");
|
|
|
|
|
fl_view_set_background_color(view, &background_color);
|
|
|
|
|
gtk_widget_show(GTK_WIDGET(view));
|
|
|
|
|
gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
|
|
|
|
|
|
|
|
|
|
// Show the window when Flutter renders.
|
|
|
|
|
// Requires the view to be realized so we can start rendering.
|
|
|
|
|
g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb),
|
|
|
|
|
self);
|
|
|
|
|
gtk_widget_realize(GTK_WIDGET(view));
|
|
|
|
|
|
|
|
|
|
fl_register_plugins(FL_PLUGIN_REGISTRY(view));
|
|
|
|
|
|
|
|
|
|
gtk_widget_grab_focus(GTK_WIDGET(view));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements GApplication::local_command_line.
|
|
|
|
|
static gboolean my_application_local_command_line(GApplication* application,
|
|
|
|
|
gchar*** arguments,
|
|
|
|
|
int* exit_status) {
|
|
|
|
|
MyApplication* self = MY_APPLICATION(application);
|
|
|
|
|
// Strip out the first argument as it is the binary name.
|
|
|
|
|
self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
|
|
|
|
|
|
|
|
|
|
g_autoptr(GError) error = nullptr;
|
|
|
|
|
if (!g_application_register(application, nullptr, &error)) {
|
|
|
|
|
g_warning("Failed to register: %s", error->message);
|
|
|
|
|
*exit_status = 1;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
g_application_activate(application);
|
|
|
|
|
*exit_status = 0;
|
|
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements GApplication::startup.
|
|
|
|
|
static void my_application_startup(GApplication* application) {
|
|
|
|
|
// MyApplication* self = MY_APPLICATION(object);
|
|
|
|
|
|
|
|
|
|
// Perform any actions required at application startup.
|
|
|
|
|
|
|
|
|
|
G_APPLICATION_CLASS(my_application_parent_class)->startup(application);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements GApplication::shutdown.
|
|
|
|
|
static void my_application_shutdown(GApplication* application) {
|
|
|
|
|
// MyApplication* self = MY_APPLICATION(object);
|
|
|
|
|
|
|
|
|
|
// Perform any actions required at application shutdown.
|
|
|
|
|
|
|
|
|
|
G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Implements GObject::dispose.
|
|
|
|
|
static void my_application_dispose(GObject* object) {
|
|
|
|
|
MyApplication* self = MY_APPLICATION(object);
|
|
|
|
|
g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
|
|
|
|
|
G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void my_application_class_init(MyApplicationClass* klass) {
|
|
|
|
|
G_APPLICATION_CLASS(klass)->activate = my_application_activate;
|
|
|
|
|
G_APPLICATION_CLASS(klass)->local_command_line =
|
|
|
|
|
my_application_local_command_line;
|
|
|
|
|
G_APPLICATION_CLASS(klass)->startup = my_application_startup;
|
|
|
|
|
G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;
|
|
|
|
|
G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void my_application_init(MyApplication* self) {}
|
|
|
|
|
|
|
|
|
|
MyApplication* my_application_new() {
|
|
|
|
|
// Set the program name to the application ID, which helps various systems
|
|
|
|
|
// like GTK and desktop environments map this running application to its
|
|
|
|
|
// corresponding .desktop file. This ensures better integration by allowing
|
|
|
|
|
// the application to be recognized beyond its binary name.
|
|
|
|
|
g_set_prgname(APPLICATION_ID);
|
|
|
|
|
|
|
|
|
|
return MY_APPLICATION(g_object_new(my_application_get_type(),
|
|
|
|
|
"application-id", APPLICATION_ID, "flags",
|
|
|
|
|
G_APPLICATION_NON_UNIQUE, nullptr));
|
|
|
|
|
}
|