my_application.cc
1 #include "my_application.h" 2 3 #include <flutter_linux/flutter_linux.h> 4 #ifdef GDK_WINDOWING_X11 5 #include <gdk/gdkx.h> 6 #endif 7 8 #include "flutter/generated_plugin_registrant.h" 9 10 struct _MyApplication { 11 GtkApplication parent_instance; 12 char** dart_entrypoint_arguments; 13 }; 14 15 G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 17 // Called when first Flutter frame received. 18 static void first_frame_cb(MyApplication* self, FlView* view) { 19 gtk_widget_show(gtk_widget_get_toplevel(GTK_WIDGET(view))); 20 } 21 22 // Implements GApplication::activate. 23 static void my_application_activate(GApplication* application) { 24 MyApplication* self = MY_APPLICATION(application); 25 GtkWindow* window = 26 GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 27 28 // Use a header bar when running in GNOME as this is the common style used 29 // by applications and is the setup most users will be using (e.g. Ubuntu 30 // desktop). 31 // If running on X and not using GNOME then just use a traditional title bar 32 // in case the window manager does more exotic layout, e.g. tiling. 33 // If running on Wayland assume the header bar will work (may need changing 34 // if future cases occur). 35 gboolean use_header_bar = TRUE; 36 #ifdef GDK_WINDOWING_X11 37 GdkScreen* screen = gtk_window_get_screen(window); 38 if (GDK_IS_X11_SCREEN(screen)) { 39 const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 40 if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 41 use_header_bar = FALSE; 42 } 43 } 44 #endif 45 if (use_header_bar) { 46 GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 47 gtk_widget_show(GTK_WIDGET(header_bar)); 48 gtk_header_bar_set_title(header_bar, "funit_converter"); 49 gtk_header_bar_set_show_close_button(header_bar, TRUE); 50 gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 51 } else { 52 gtk_window_set_title(window, "funit_converter"); 53 } 54 55 gtk_window_set_default_size(window, 1280, 720); 56 57 g_autoptr(FlDartProject) project = fl_dart_project_new(); 58 fl_dart_project_set_dart_entrypoint_arguments( 59 project, self->dart_entrypoint_arguments); 60 61 FlView* view = fl_view_new(project); 62 GdkRGBA background_color; 63 // Background defaults to black, override it here if necessary, e.g. #00000000 64 // for transparent. 65 gdk_rgba_parse(&background_color, "#000000"); 66 fl_view_set_background_color(view, &background_color); 67 gtk_widget_show(GTK_WIDGET(view)); 68 gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 69 70 // Show the window when Flutter renders. 71 // Requires the view to be realized so we can start rendering. 72 g_signal_connect_swapped(view, "first-frame", G_CALLBACK(first_frame_cb), 73 self); 74 gtk_widget_realize(GTK_WIDGET(view)); 75 76 fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 77 78 gtk_widget_grab_focus(GTK_WIDGET(view)); 79 } 80 81 // Implements GApplication::local_command_line. 82 static gboolean my_application_local_command_line(GApplication* application, 83 gchar*** arguments, 84 int* exit_status) { 85 MyApplication* self = MY_APPLICATION(application); 86 // Strip out the first argument as it is the binary name. 87 self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 88 89 g_autoptr(GError) error = nullptr; 90 if (!g_application_register(application, nullptr, &error)) { 91 g_warning("Failed to register: %s", error->message); 92 *exit_status = 1; 93 return TRUE; 94 } 95 96 g_application_activate(application); 97 *exit_status = 0; 98 99 return TRUE; 100 } 101 102 // Implements GApplication::startup. 103 static void my_application_startup(GApplication* application) { 104 // MyApplication* self = MY_APPLICATION(object); 105 106 // Perform any actions required at application startup. 107 108 G_APPLICATION_CLASS(my_application_parent_class)->startup(application); 109 } 110 111 // Implements GApplication::shutdown. 112 static void my_application_shutdown(GApplication* application) { 113 // MyApplication* self = MY_APPLICATION(object); 114 115 // Perform any actions required at application shutdown. 116 117 G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application); 118 } 119 120 // Implements GObject::dispose. 121 static void my_application_dispose(GObject* object) { 122 MyApplication* self = MY_APPLICATION(object); 123 g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 124 G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 125 } 126 127 static void my_application_class_init(MyApplicationClass* klass) { 128 G_APPLICATION_CLASS(klass)->activate = my_application_activate; 129 G_APPLICATION_CLASS(klass)->local_command_line = 130 my_application_local_command_line; 131 G_APPLICATION_CLASS(klass)->startup = my_application_startup; 132 G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown; 133 G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 134 } 135 136 static void my_application_init(MyApplication* self) {} 137 138 MyApplication* my_application_new() { 139 // Set the program name to the application ID, which helps various systems 140 // like GTK and desktop environments map this running application to its 141 // corresponding .desktop file. This ensures better integration by allowing 142 // the application to be recognized beyond its binary name. 143 g_set_prgname(APPLICATION_ID); 144 145 return MY_APPLICATION(g_object_new(my_application_get_type(), 146 "application-id", APPLICATION_ID, "flags", 147 G_APPLICATION_NON_UNIQUE, nullptr)); 148 }