Skip to content

Commit 9b8c4bd

Browse files
committed
Support headless mode
1 parent 5d22779 commit 9b8c4bd

File tree

8 files changed

+134
-5
lines changed

8 files changed

+134
-5
lines changed

plugins/color_panel/gtk/color_panel_plugin.cc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
// See color_panel.dart for documentation.
2020
const char kChannelName[] = "flutter/colorpanel";
21+
const char kNoScreenError[] = "No Screen";
2122
const char kShowColorPanelMethod[] = "ColorPanel.Show";
2223
const char kColorPanelShowAlpha[] = "ColorPanel.ShowAlpha";
2324
const char kHideColorPanelMethod[] = "ColorPanel.Hide";
@@ -92,9 +93,14 @@ static FlMethodResponse* show_color_panel(FlColorPanelPlugin* self,
9293
gboolean use_alpha =
9394
use_alpha_value != nullptr ? fl_value_get_bool(use_alpha_value) : FALSE;
9495

96+
FlView* view = fl_plugin_registrar_get_view(self->registrar);
97+
if (view == nullptr) {
98+
return FL_METHOD_RESPONSE(
99+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
100+
}
101+
95102
self->color_chooser_dialog = GTK_COLOR_CHOOSER_DIALOG(
96103
gtk_color_chooser_dialog_new(kWindowTitle, nullptr));
97-
FlView* view = fl_plugin_registrar_get_view(self->registrar);
98104
GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
99105
gtk_window_set_transient_for(GTK_WINDOW(self->color_chooser_dialog), window);
100106
gtk_color_chooser_set_use_alpha(GTK_COLOR_CHOOSER(self->color_chooser_dialog),

plugins/file_chooser/gtk/file_chooser_plugin.cc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// See channel_controller.dart for documentation.
2020
const char kChannelName[] = "flutter/filechooser";
2121
const char kBadArgumentsError[] = "Bad Arguments";
22+
const char kNoScreenError[] = "No Screen";
2223
const char kShowOpenPanelMethod[] = "FileChooser.Show.Open";
2324
const char kShowSavePanelMethod[] = "FileChooser.Show.Save";
2425
const char kInitialDirectoryKey[] = "initialDirectory";
@@ -84,6 +85,11 @@ static FlMethodResponse* show_dialog(FlFileChooserPlugin* self,
8485
confirm_button_text = fl_value_get_string(value);
8586

8687
FlView* view = fl_plugin_registrar_get_view(self->registrar);
88+
if (view == nullptr) {
89+
return FL_METHOD_RESPONSE(
90+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
91+
}
92+
8793
GtkWindow* window = GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
8894
g_autoptr(GtkFileChooserNative) dialog =
8995
GTK_FILE_CHOOSER_NATIVE(gtk_file_chooser_native_new(

plugins/menubar/gtk/menubar_plugin.cc

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
// See menu_channel.dart for documentation.
2121
const char kChannelName[] = "flutter/menubar";
2222
const char kBadArgumentsError[] = "Bad Arguments";
23+
const char kNoScreenError[] = "No Screen";
2324
const char kFailureError[] = "Failure";
2425
const char kMenuSetMethod[] = "Menubar.SetMenu";
2526
const char kMenuItemSelectedCallbackMethod[] = "Menubar.SelectedCallback";
@@ -145,6 +146,11 @@ static FlMethodResponse* menu_set(FlMenubarPlugin* self, FlValue* args) {
145146
}
146147

147148
FlView* view = fl_plugin_registrar_get_view(self->registrar);
149+
if (view == nullptr) {
150+
return FL_METHOD_RESPONSE(
151+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
152+
}
153+
148154
GtkApplication* app = gtk_window_get_application(
149155
GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
150156
if (app == nullptr) {
@@ -213,8 +219,10 @@ FlMenubarPlugin* fl_menubar_plugin_new(FlPluginRegistrar* registrar) {
213219

214220
// Add a GAction for the menubar to trigger.
215221
FlView* view = fl_plugin_registrar_get_view(self->registrar);
216-
GtkApplication* app = gtk_window_get_application(
217-
GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
222+
GtkApplication* app = nullptr;
223+
if (view != nullptr)
224+
app = gtk_window_get_application(
225+
GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view))));
218226
if (app != nullptr) {
219227
g_autoptr(GSimpleAction) inactive_action =
220228
g_simple_action_new("flutter-menu-inactive", nullptr);

plugins/window_size/gtk/window_size_plugin.cc

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// See window_size_channel.dart for documentation.
2020
const char kChannelName[] = "flutter/windowsize";
2121
const char kBadArgumentsError[] = "Bad Arguments";
22+
const char kNoScrenError[] = "No Screen";
2223
const char kGetScreenListMethod[] = "getScreenList";
2324
const char kGetWindowInfoMethod[] = "getWindowInfo";
2425
const char kSetWindowFrameMethod[] = "setWindowFrame";
@@ -49,12 +50,16 @@ G_DEFINE_TYPE(FlWindowSizePlugin, fl_window_size_plugin, g_object_get_type())
4950
// Gets the window being controlled.
5051
GtkWindow* get_window(FlWindowSizePlugin* self) {
5152
FlView* view = fl_plugin_registrar_get_view(self->registrar);
53+
if (view == nullptr) return nullptr;
54+
5255
return GTK_WINDOW(gtk_widget_get_toplevel(GTK_WIDGET(view)));
5356
}
5457

5558
// Gets the display connection.
5659
GdkDisplay* get_display(FlWindowSizePlugin* self) {
5760
FlView* view = fl_plugin_registrar_get_view(self->registrar);
61+
if (view == nullptr) return nullptr;
62+
5863
return gtk_widget_get_display(GTK_WIDGET(view));
5964
}
6065

@@ -97,6 +102,11 @@ static FlMethodResponse* get_screen_list(FlWindowSizePlugin* self) {
97102
g_autoptr(FlValue) screens = fl_value_new_list();
98103

99104
GdkDisplay* display = get_display(self);
105+
if (display == nullptr) {
106+
return FL_METHOD_RESPONSE(
107+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
108+
}
109+
100110
gint n_monitors = gdk_display_get_n_monitors(display);
101111
for (gint i = 0; i < n_monitors; i++) {
102112
GdkMonitor* monitor = gdk_display_get_monitor(display, i);
@@ -109,6 +119,10 @@ static FlMethodResponse* get_screen_list(FlWindowSizePlugin* self) {
109119
// Gets information about the Flutter window.
110120
static FlMethodResponse* get_window_info(FlWindowSizePlugin* self) {
111121
GtkWindow* window = get_window(self);
122+
if (window == nullptr) {
123+
return FL_METHOD_RESPONSE(
124+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
125+
}
112126

113127
g_autoptr(FlValue) window_info = fl_value_new_map();
114128

@@ -158,6 +172,11 @@ static FlMethodResponse* set_window_frame(FlWindowSizePlugin* self,
158172
double height = fl_value_get_float(fl_value_get_list_value(args, 3));
159173

160174
GtkWindow* window = get_window(self);
175+
if (window == nullptr) {
176+
return FL_METHOD_RESPONSE(
177+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
178+
}
179+
161180
gtk_window_move(window, static_cast<gint>(x), static_cast<gint>(y));
162181
gtk_window_resize(window, static_cast<gint>(width),
163182
static_cast<gint>(height));
@@ -183,6 +202,11 @@ static FlMethodResponse* set_window_minimum_size(FlWindowSizePlugin* self,
183202
double width = fl_value_get_float(fl_value_get_list_value(args, 0));
184203
double height = fl_value_get_float(fl_value_get_list_value(args, 1));
185204

205+
if (get_window() == nullptr) {
206+
return FL_METHOD_RESPONSE(
207+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
208+
}
209+
186210
if (width >= 0 && height >= 0) {
187211
self->window_geometry.min_width = static_cast<gint>(width);
188212
self->window_geometry.min_height = static_cast<gint>(height);
@@ -204,6 +228,11 @@ static FlMethodResponse* set_window_maximum_size(FlWindowSizePlugin* self,
204228
double width = fl_value_get_float(fl_value_get_list_value(args, 0));
205229
double height = fl_value_get_float(fl_value_get_list_value(args, 1));
206230

231+
if (get_window() == nullptr) {
232+
return FL_METHOD_RESPONSE(
233+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
234+
}
235+
207236
self->window_geometry.max_width = static_cast<gint>(width);
208237
self->window_geometry.max_height = static_cast<gint>(height);
209238

@@ -221,6 +250,10 @@ static FlMethodResponse* set_window_title(FlWindowSizePlugin* self,
221250
}
222251

223252
GtkWindow* window = get_window(self);
253+
if (window == nullptr) {
254+
return FL_METHOD_RESPONSE(
255+
fl_method_error_response_new(kNoScreenError, nullptr, nullptr));
256+
}
224257
gtk_window_set_title(window, fl_value_get_string(args));
225258

226259
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));

testbed/gtk/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
3434
add_executable(${BINARY_NAME}
3535
"main.cc"
3636
"fl_application.cc"
37+
"fl_headless_application.cc"
3738
"window_configuration.cc"
3839
"${FLUTTER_MANAGED_DIR}/gtk_plugin_registrant.cc"
3940
)
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
#include "fl_headless_application.h"
2+
3+
#include <flutter_linux/flutter_linux.h>
4+
#include <flutter_linux/fl_engine_extra.h>
5+
6+
#include "flutter/gtk_plugin_registrant.h"
7+
8+
struct _FlHeadlessApplication {
9+
GApplication parent_instance;
10+
11+
FlEngine* engine;
12+
};
13+
14+
G_DEFINE_TYPE(FlHeadlessApplication, fl_headless_application,
15+
G_TYPE_APPLICATION)
16+
17+
// Implements GApplication::activate.
18+
static void fl_headless_application_activate(GApplication* application) {
19+
FlHeadlessApplication* self = FL_HEADLESS_APPLICATION(application);
20+
21+
g_autoptr(FlDartProject) project = fl_dart_project_new();
22+
self->engine = fl_engine_new_headless(project);
23+
24+
g_application_hold(application);
25+
26+
fl_register_plugins(FL_PLUGIN_REGISTRY(self->engine));
27+
}
28+
29+
// Implements GObject::dispose.
30+
static void fl_headless_application_dispose(GObject* object) {
31+
FlHeadlessApplication* self = FL_HEADLESS_APPLICATION(object);
32+
33+
g_clear_object(&self->engine);
34+
35+
G_OBJECT_CLASS(fl_headless_application_parent_class)->dispose(object);
36+
}
37+
38+
static void fl_headless_application_class_init(
39+
FlHeadlessApplicationClass* klass) {
40+
G_APPLICATION_CLASS(klass)->activate = fl_headless_application_activate;
41+
G_OBJECT_CLASS(klass)->dispose = fl_headless_application_dispose;
42+
}
43+
44+
static void fl_headless_application_init(FlHeadlessApplication* self) {}
45+
46+
FlHeadlessApplication* fl_headless_application_new() {
47+
return FL_HEADLESS_APPLICATION(
48+
g_object_new(fl_headless_application_get_type(), nullptr));
49+
}

testbed/gtk/fl_headless_application.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
#ifndef FLUTTER_FL_HEADLESS_APPLICATION_H_
2+
#define FLUTTER_FL_HEADLESS_APPLICATION_H_
3+
4+
#include <gio/gio.h>
5+
6+
G_DECLARE_FINAL_TYPE(FlHeadlessApplication, fl_headless_application, FL,
7+
HEADLESS_APPLICATION, GApplication)
8+
9+
/**
10+
* fl_headless_application_new:
11+
*
12+
* Creates a new Flutter headless application.
13+
*
14+
* Returns: a new #FlHeadlessApplication.
15+
*/
16+
FlHeadlessApplication* fl_headless_application_new();
17+
18+
#endif // FLUTTER_FL_HEADLESS_APPLICATION_H_
19+

testbed/gtk/main.cc

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
#include "fl_application.h"
2+
#include "fl_headless_application.h"
23

34
int main(int argc, char** argv) {
4-
g_autoptr(FlApplication) app = fl_application_new();
5-
return g_application_run(G_APPLICATION(app), argc, argv);
5+
g_autoptr(GApplication) app = nullptr;
6+
7+
if (gdk_init_check(&argc, &argv))
8+
app = G_APPLICATION(fl_application_new());
9+
else
10+
app = G_APPLICATION(fl_headless_application_new());
11+
12+
return g_application_run(app, argc, argv);
613
}

0 commit comments

Comments
 (0)