#include "my_application.h"
#include <flutter_linux/flutter_linux.h>
#ifdef GDK_WINDOWING_X11
#include <gdk/gdkx.h>
#endif
#include <sys/stat.h>
#include <libgen.h>
#include <locale.h>
#include <libintl.h>
#include "flutter/generated_plugin_registrant.h"
#define LOCALE_DIR "/locale/"
#define PACKAGE "app"
#define _(String) gettext(String)
struct _MyApplication
{
GtkApplication parent_instance;
char **dart_entrypoint_arguments;
};
G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
// 118n
static void setup_app_locale()
{
struct stat sb;
const char *proc_name = "/proc/self/exe";
if (lstat(proc_name, &sb) >= 0)
{
/* Add one to the link size, so that we can determine whether
the buffer returned by readlink() was truncated. */
ssize_t bufsiz = sb.st_size + 1;
/* Some magic symlinks under (for example) /proc and /sys
report 'st_size' as zero. In that case, take PATH_MAX as
a "good enough" estimate. */
if (0 == sb.st_size)
{
bufsiz = PATH_MAX;
}
ssize_t loc_dir_len = bufsiz + strlen(LOCALE_DIR) + 1;
char *buf = (char *)malloc(loc_dir_len);
if (NULL != buf)
{
ssize_t nbytes = readlink(proc_name, buf, bufsiz);
if (nbytes >= 0)
{
/* If the return value was equal to the buffer size, then the
the link target was larger than expected (perhaps because the
target was changed between the call to lstat() and the call to
readlink()). Warn the user that the returned target may have
been truncated. */
if (nbytes == bufsiz)
{
g_message("(Returned buffer may have been truncated)\n");
}
if (nbytes <= bufsiz)
{
buf[nbytes] = '\0';
}
char *dir = dirname(buf);
strncat(dir, LOCALE_DIR, strlen(LOCALE_DIR));
char *loc = setlocale(LC_ALL, NULL);
g_message("current locale is:%s", loc);
g_message("locale files dir is:%s", buf);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, buf);
textdomain(PACKAGE);
}
free(buf);
}
}
}
// 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;
setup_app_locale();
#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));
gtk_header_bar_set_title(header_bar, _("app_name"));
gtk_header_bar_set_show_close_button(header_bar, TRUE);
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
}
else
{
gtk_window_set_title(window, _("app_name"));
}
gtk_window_set_default_size(window, 1280, 720);
gtk_widget_show(GTK_WIDGET(window));
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);
gtk_widget_show(GTK_WIDGET(view));
gtk_container_add(GTK_CONTAINER(window), 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 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_OBJECT_CLASS(klass)->dispose = my_application_dispose;
}
static void my_application_init(MyApplication *self) {}
MyApplication *my_application_new()
{
return MY_APPLICATION(g_object_new(my_application_get_type(),
"application-id", APPLICATION_ID,
"flags", G_APPLICATION_NON_UNIQUE,
nullptr));
}