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