| Flutter Linux Embedder |

fl_platform_handler.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "flutter/shell/platform/linux/fl_platform_handler.h"
6
7 #include <gtk/gtk.h>
8 #include <cstring>
9
10 #include "flutter/shell/platform/linux/fl_platform_channel.h"
11 #include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
12
13 static constexpr char kInProgressError[] = "In Progress";
14 static constexpr char kUnknownClipboardFormatError[] =
15 "Unknown Clipboard Format";
16
17 static constexpr char kTextPlainFormat[] = "text/plain";
18
19 static constexpr char kSoundTypeAlert[] = "SystemSoundType.alert";
20 static constexpr char kSoundTypeClick[] = "SystemSoundType.click";
21 static constexpr char kSoundTypeTick[] = "SystemSoundType.tick";
22
23 struct _FlPlatformHandler {
24 GObject parent_instance;
25
26 FlPlatformChannel* channel;
27
28 FlMethodCall* exit_application_method_call;
29
30 bool app_initialization_complete;
31
32 GCancellable* cancellable;
33 };
34
35 G_DEFINE_TYPE(FlPlatformHandler, fl_platform_handler, G_TYPE_OBJECT)
36
37 // Called when clipboard text received.
38 static void clipboard_text_cb(GtkClipboard* clipboard,
39 const gchar* text,
40 gpointer user_data) {
41 g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
42 fl_platform_channel_respond_clipboard_get_data(method_call, text);
43 }
44
45 // Called when clipboard text received during has_strings.
46 static void clipboard_text_has_strings_cb(GtkClipboard* clipboard,
47 const gchar* text,
48 gpointer user_data) {
49 g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
50 fl_platform_channel_respond_clipboard_has_strings(
51 method_call, text != nullptr && strlen(text) > 0);
52 }
53
54 // Called when Flutter wants to copy to the clipboard.
55 static FlMethodResponse* clipboard_set_data(FlMethodCall* method_call,
56 const gchar* text,
57 gpointer user_data) {
58 GtkClipboard* clipboard =
59 gtk_clipboard_get_default(gdk_display_get_default());
60 gtk_clipboard_set_text(clipboard, text, -1);
61
62 return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
63 }
64
65 // Called when Flutter wants to paste from the clipboard.
66 static FlMethodResponse* clipboard_get_data(FlMethodCall* method_call,
67 const gchar* format,
68 gpointer user_data) {
69 if (strcmp(format, kTextPlainFormat) != 0) {
70 return FL_METHOD_RESPONSE(fl_method_error_response_new(
71 kUnknownClipboardFormatError, "GTK clipboard API only supports text",
72 nullptr));
73 }
74
75 GtkClipboard* clipboard =
76 gtk_clipboard_get_default(gdk_display_get_default());
77 gtk_clipboard_request_text(clipboard, clipboard_text_cb,
78 g_object_ref(method_call));
79
80 // Will respond later.
81 return nullptr;
82 }
83
84 // Called when Flutter wants to know if the content of the clipboard is able to
85 // be pasted, without actually accessing the clipboard content itself.
86 static FlMethodResponse* clipboard_has_strings(FlMethodCall* method_call,
87 gpointer user_data) {
88 GtkClipboard* clipboard =
89 gtk_clipboard_get_default(gdk_display_get_default());
90 gtk_clipboard_request_text(clipboard, clipboard_text_has_strings_cb,
91 g_object_ref(method_call));
92
93 // Will respond later.
94 return nullptr;
95 }
96
97 // Quit this application
98 static void quit_application() {
99 GApplication* app = g_application_get_default();
100 if (app == nullptr) {
101 // Unable to gracefully quit, so just exit the process.
102 exit(0);
103 }
104
105 // GtkApplication windows contain a reference back to the application.
106 // Break them so the application object can cleanup.
107 // See https://gitlab.gnome.org/GNOME/gtk/-/issues/6190
108 if (GTK_IS_APPLICATION(app)) {
109 // List is copied as it will be modified as windows are disconnected from
110 // the application.
111 g_autoptr(GList) windows =
112 g_list_copy(gtk_application_get_windows(GTK_APPLICATION(app)));
113 for (GList* link = windows; link != NULL; link = link->next) {
114 GtkWidget* window = GTK_WIDGET(link->data);
115 gtk_window_set_application(GTK_WINDOW(window), NULL);
116 }
117 }
118
119 g_application_quit(app);
120 }
121
122 // Handle response of System.requestAppExit.
123 static void request_app_exit_response_cb(GObject* object,
124 GAsyncResult* result,
125 gpointer user_data) {
126 FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
127
128 g_autoptr(GError) error = nullptr;
129 FlPlatformChannelExitResponse exit_response;
130 if (!fl_platform_channel_system_request_app_exit_finish(
131 object, result, &exit_response, &error)) {
132 if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
133 return;
134 }
135 g_warning("Failed to complete System.requestAppExit: %s", error->message);
136 quit_application();
137 return;
138 }
139
140 if (exit_response == FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT) {
141 quit_application();
142 }
143
144 // If request was due to a request from Flutter, pass result back.
145 if (self->exit_application_method_call != nullptr) {
146 fl_platform_channel_respond_system_exit_application(
147 self->exit_application_method_call, exit_response);
148 }
149 }
150
151 // Send a request to Flutter to exit the application, but only if it's ready for
152 // a request.
153 static void request_app_exit(FlPlatformHandler* self,
154 FlPlatformChannelExitType type) {
155 if (!self->app_initialization_complete ||
156 type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
157 quit_application();
158 return;
159 }
160
161 fl_platform_channel_system_request_app_exit(
162 self->channel, type, self->cancellable, request_app_exit_response_cb,
163 self);
164 }
165
166 // Called when the Dart app has finished initialization and is ready to handle
167 // requests. For the Flutter framework, this means after the ServicesBinding has
168 // been initialized and it sends a System.initializationComplete message.
169 static void system_initialization_complete(gpointer user_data) {
170 FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
171 self->app_initialization_complete = TRUE;
172 }
173
174 // Called when Flutter wants to exit the application.
175 static FlMethodResponse* system_exit_application(FlMethodCall* method_call,
176 FlPlatformChannelExitType type,
177 gpointer user_data) {
178 FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
179 // Save method call to respond to when our request to Flutter completes.
180 if (self->exit_application_method_call != nullptr) {
181 return FL_METHOD_RESPONSE(fl_method_error_response_new(
182 kInProgressError, "Request already in progress", nullptr));
183 }
184 self->exit_application_method_call =
185 FL_METHOD_CALL(g_object_ref(method_call));
186
187 // Requested to immediately quit if the app hasn't yet signaled that it is
188 // ready to handle requests, or if the type of exit requested is "required".
189 if (!self->app_initialization_complete ||
190 type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
191 quit_application();
192 return fl_platform_channel_make_system_request_app_exit_response(
193 FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT);
194 }
195
196 // Send the request back to Flutter to follow the standard process.
197 request_app_exit(self, type);
198
199 // Will respond later.
200 return nullptr;
201 }
202
203 // Called when Flutter wants to play a sound.
204 static void system_sound_play(const gchar* type, gpointer user_data) {
205 if (strcmp(type, kSoundTypeAlert) == 0) {
206 GdkDisplay* display = gdk_display_get_default();
207 if (display != nullptr) {
208 gdk_display_beep(display);
209 }
210 } else if (strcmp(type, kSoundTypeClick) == 0) {
211 // We don't make sounds for keyboard on desktops.
212 } else if (strcmp(type, kSoundTypeTick) == 0) {
213 // We don't make ticking sounds on desktops.
214 } else {
215 g_warning("Ignoring unknown sound type %s in SystemSound.play.\n", type);
216 }
217 }
218
219 // Called when Flutter wants to quit the application.
220 static void system_navigator_pop(gpointer user_data) {
221 quit_application();
222 }
223
224 static void fl_platform_handler_dispose(GObject* object) {
225 FlPlatformHandler* self = FL_PLATFORM_HANDLER(object);
226
227 g_cancellable_cancel(self->cancellable);
228
229 g_clear_object(&self->channel);
230 g_clear_object(&self->exit_application_method_call);
231 g_clear_object(&self->cancellable);
232
233 G_OBJECT_CLASS(fl_platform_handler_parent_class)->dispose(object);
234 }
235
236 static void fl_platform_handler_class_init(FlPlatformHandlerClass* klass) {
237 G_OBJECT_CLASS(klass)->dispose = fl_platform_handler_dispose;
238 }
239
240 static void fl_platform_handler_init(FlPlatformHandler* self) {
241 self->cancellable = g_cancellable_new();
242 }
243
244 static FlPlatformChannelVTable platform_channel_vtable = {
245 .clipboard_set_data = clipboard_set_data,
246 .clipboard_get_data = clipboard_get_data,
247 .clipboard_has_strings = clipboard_has_strings,
248 .system_exit_application = system_exit_application,
249 .system_initialization_complete = system_initialization_complete,
250 .system_sound_play = system_sound_play,
251 .system_navigator_pop = system_navigator_pop,
252 };
253
254 FlPlatformHandler* fl_platform_handler_new(FlBinaryMessenger* messenger) {
255 g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
256
257 FlPlatformHandler* self = FL_PLATFORM_HANDLER(
258 g_object_new(fl_platform_handler_get_type(), nullptr));
259
260 self->channel =
261 fl_platform_channel_new(messenger, &platform_channel_vtable, self);
262 self->app_initialization_complete = FALSE;
263
264 return self;
265 }
266
267 void fl_platform_handler_request_app_exit(FlPlatformHandler* self) {
268 g_return_if_fail(FL_IS_PLATFORM_HANDLER(self));
269 // Request a cancellable exit.
270 request_app_exit(self, FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE);
271 }
window
return window
Definition: fl_application.cc:41
G_DEFINE_TYPE
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
Definition: fl_basic_message_channel.cc:37
user_data
G_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
Definition: fl_event_channel.h:90
fl_method_channel.h
method_call
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
Definition: fl_method_channel.h:120
fl_method_error_response_new
G_MODULE_EXPORT FlMethodErrorResponse * fl_method_error_response_new(const gchar *code, const gchar *message, FlValue *details)
Definition: fl_method_response.cc:144
fl_method_success_response_new
G_MODULE_EXPORT FlMethodSuccessResponse * fl_method_success_response_new(FlValue *result)
Definition: fl_method_response.cc:126
TRUE
return TRUE
Definition: fl_pixel_buffer_texture_test.cc:53
error
const uint8_t uint32_t uint32_t GError ** error
Definition: fl_pixel_buffer_texture_test.cc:40
fl_platform_channel_make_system_request_app_exit_response
FlMethodResponse * fl_platform_channel_make_system_request_app_exit_response(FlPlatformChannelExitResponse exit_response)
Definition: fl_platform_channel.cc:330
fl_platform_channel_system_request_app_exit_finish
gboolean fl_platform_channel_system_request_app_exit_finish(GObject *object, GAsyncResult *result, FlPlatformChannelExitResponse *exit_response, GError **error)
Definition: fl_platform_channel.cc:268
fl_platform_channel_respond_clipboard_get_data
void fl_platform_channel_respond_clipboard_get_data(FlMethodCall *method_call, const gchar *text)
Definition: fl_platform_channel.cc:284
fl_platform_channel_system_request_app_exit
void fl_platform_channel_system_request_app_exit(FlPlatformChannel *self, FlPlatformChannelExitType type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
Definition: fl_platform_channel.cc:243
fl_platform_channel_respond_system_exit_application
void fl_platform_channel_respond_system_exit_application(FlMethodCall *method_call, FlPlatformChannelExitResponse exit_response)
Definition: fl_platform_channel.cc:318
fl_platform_channel_new
FlPlatformChannel * fl_platform_channel_new(FlBinaryMessenger *messenger, FlPlatformChannelVTable *vtable, gpointer user_data)
Definition: fl_platform_channel.cc:222
fl_platform_channel_respond_clipboard_has_strings
void fl_platform_channel_respond_clipboard_has_strings(FlMethodCall *method_call, gboolean has_strings)
Definition: fl_platform_channel.cc:302
fl_platform_channel.h
FlPlatformChannelExitResponse
FlPlatformChannelExitResponse
Definition: fl_platform_channel.h:18
FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT
@ FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT
Definition: fl_platform_channel.h:20
FlPlatformChannelExitType
FlPlatformChannelExitType
Definition: fl_platform_channel.h:13
FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE
Definition: fl_platform_channel.h:14
FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED
Definition: fl_platform_channel.h:15
kSoundTypeTick
static constexpr char kSoundTypeTick[]
Definition: fl_platform_handler.cc:21
fl_platform_handler_new
FlPlatformHandler * fl_platform_handler_new(FlBinaryMessenger *messenger)
Definition: fl_platform_handler.cc:254
system_initialization_complete
static void system_initialization_complete(gpointer user_data)
Definition: fl_platform_handler.cc:169
clipboard_set_data
static FlMethodResponse * clipboard_set_data(FlMethodCall *method_call, const gchar *text, gpointer user_data)
Definition: fl_platform_handler.cc:55
clipboard_text_has_strings_cb
static void clipboard_text_has_strings_cb(GtkClipboard *clipboard, const gchar *text, gpointer user_data)
Definition: fl_platform_handler.cc:46
system_exit_application
static FlMethodResponse * system_exit_application(FlMethodCall *method_call, FlPlatformChannelExitType type, gpointer user_data)
Definition: fl_platform_handler.cc:175
fl_platform_handler_dispose
static void fl_platform_handler_dispose(GObject *object)
Definition: fl_platform_handler.cc:224
clipboard_has_strings
static FlMethodResponse * clipboard_has_strings(FlMethodCall *method_call, gpointer user_data)
Definition: fl_platform_handler.cc:86
quit_application
static void quit_application()
Definition: fl_platform_handler.cc:98
kSoundTypeAlert
static constexpr char kSoundTypeAlert[]
Definition: fl_platform_handler.cc:19
fl_platform_handler_init
static void fl_platform_handler_init(FlPlatformHandler *self)
Definition: fl_platform_handler.cc:240
system_navigator_pop
static void system_navigator_pop(gpointer user_data)
Definition: fl_platform_handler.cc:220
kUnknownClipboardFormatError
static constexpr char kUnknownClipboardFormatError[]
Definition: fl_platform_handler.cc:14
clipboard_get_data
static FlMethodResponse * clipboard_get_data(FlMethodCall *method_call, const gchar *format, gpointer user_data)
Definition: fl_platform_handler.cc:66
platform_channel_vtable
static FlPlatformChannelVTable platform_channel_vtable
Definition: fl_platform_handler.cc:244
request_app_exit
static void request_app_exit(FlPlatformHandler *self, FlPlatformChannelExitType type)
Definition: fl_platform_handler.cc:153
fl_platform_handler_class_init
static void fl_platform_handler_class_init(FlPlatformHandlerClass *klass)
Definition: fl_platform_handler.cc:236
kSoundTypeClick
static constexpr char kSoundTypeClick[]
Definition: fl_platform_handler.cc:20
clipboard_text_cb
static void clipboard_text_cb(GtkClipboard *clipboard, const gchar *text, gpointer user_data)
Definition: fl_platform_handler.cc:38
kTextPlainFormat
static constexpr char kTextPlainFormat[]
Definition: fl_platform_handler.cc:17
request_app_exit_response_cb
static void request_app_exit_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
Definition: fl_platform_handler.cc:123
fl_platform_handler_request_app_exit
void fl_platform_handler_request_app_exit(FlPlatformHandler *self)
Definition: fl_platform_handler.cc:267
system_sound_play
static void system_sound_play(const gchar *type, gpointer user_data)
Definition: fl_platform_handler.cc:204
kInProgressError
static constexpr char kInProgressError[]
Definition: fl_platform_handler.cc:13
fl_platform_handler.h
type
uint8_t type
Definition: fl_standard_message_codec_test.cc:1117
format
uint32_t uint32_t * format
Definition: fl_texture_registrar_test.cc:42
_FlPlatformHandler
Definition: fl_platform_handler.cc:23
_FlPlatformHandler::channel
FlPlatformChannel * channel
Definition: fl_platform_handler.cc:26
_FlPlatformHandler::exit_application_method_call
FlMethodCall * exit_application_method_call
Definition: fl_platform_handler.cc:28
_FlPlatformHandler::cancellable
GCancellable * cancellable
Definition: fl_platform_handler.cc:32
_FlPlatformHandler::app_initialization_complete
bool app_initialization_complete
Definition: fl_platform_handler.cc:30
_FlPlatformHandler::parent_instance
GObject parent_instance
Definition: fl_platform_handler.cc:24
FlPlatformChannelVTable
Definition: fl_platform_channel.h:36
FlPlatformChannelVTable::clipboard_set_data
FlMethodResponse *(* clipboard_set_data)(FlMethodCall *method_call, const gchar *text, gpointer user_data)
Definition: fl_platform_channel.h:37