Flutter Engine
 
Loading...
Searching...
No Matches
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
6
7#include <gtk/gtk.h>
8#include <cstring>
9
12
13static constexpr char kInProgressError[] = "In Progress";
14static constexpr char kUnknownClipboardFormatError[] =
15 "Unknown Clipboard Format";
16
17static constexpr char kTextPlainFormat[] = "text/plain";
18
19static constexpr char kSoundTypeAlert[] = "SystemSoundType.alert";
20static constexpr char kSoundTypeClick[] = "SystemSoundType.click";
21static constexpr char kSoundTypeTick[] = "SystemSoundType.tick";
22
25
26 FlPlatformChannel* channel;
27
29
31
32 GCancellable* cancellable;
33};
34
35G_DEFINE_TYPE(FlPlatformHandler, fl_platform_handler, G_TYPE_OBJECT)
36
37// Called when clipboard text received.
38static 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);
43}
44
45// Called when clipboard text received during has_strings.
46static 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);
51 method_call, text != nullptr && strlen(text) > 0);
52}
53
54// Called when Flutter wants to copy to the clipboard.
55static FlMethodResponse* clipboard_set_data(FlMethodCall* method_call,
56 const gchar* text,
57 gpointer user_data) {
58 GtkClipboard* clipboard =
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.
66static 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 =
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.
86static FlMethodResponse* clipboard_has_strings(FlMethodCall* method_call,
87 gpointer user_data) {
88 GtkClipboard* clipboard =
91 g_object_ref(method_call));
92
93 // Will respond later.
94 return nullptr;
95}
96
97// Quit this application
98static 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.
123static 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;
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);
137 return;
138 }
139
140 if (exit_response == FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT) {
142 }
143
144 // If request was due to a request from Flutter, pass result back.
145 if (self->exit_application_method_call != nullptr) {
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.
153static void request_app_exit(FlPlatformHandler* self,
155 if (!self->app_initialization_complete ||
158 return;
159 }
160
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.
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.
175static FlMethodResponse* system_exit_application(FlMethodCall* method_call,
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 ||
194 }
195
196 // Send the request back to Flutter to follow the standard process.
198
199 // Will respond later.
200 return nullptr;
201}
202
203// Called when Flutter wants to play a sound.
204static 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.
220static void system_navigator_pop(gpointer user_data) {
222}
223
224static 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
236static void fl_platform_handler_class_init(FlPlatformHandlerClass* klass) {
237 G_OBJECT_CLASS(klass)->dispose = fl_platform_handler_dispose;
238}
239
240static void fl_platform_handler_init(FlPlatformHandler* self) {
241 self->cancellable = g_cancellable_new();
242}
243
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
254FlPlatformHandler* 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 =
262 self->app_initialization_complete = FALSE;
263
264 return self;
265}
266
268 g_return_if_fail(FL_IS_PLATFORM_HANDLER(self));
269 // Request a cancellable exit.
271}
GLenum type
GLFWwindow * window
Definition main.cc:60
G_DEFINE_TYPE(FlBasicMessageChannelResponseHandle, fl_basic_message_channel_response_handle, G_TYPE_OBJECT) static void fl_basic_message_channel_response_handle_dispose(GObject *object)
g_autoptr(GMutexLocker) locker
return TRUE
G_BEGIN_DECLS G_MODULE_EXPORT FlMethodCall * method_call
G_MODULE_EXPORT FlMethodErrorResponse * fl_method_error_response_new(const gchar *code, const gchar *message, FlValue *details)
G_MODULE_EXPORT FlMethodSuccessResponse * fl_method_success_response_new(FlValue *result)
const uint8_t uint32_t uint32_t GError ** error
FlMethodResponse * fl_platform_channel_make_system_request_app_exit_response(FlPlatformChannelExitResponse exit_response)
gboolean fl_platform_channel_system_request_app_exit_finish(GObject *object, GAsyncResult *result, FlPlatformChannelExitResponse *exit_response, GError **error)
void fl_platform_channel_respond_clipboard_get_data(FlMethodCall *method_call, const gchar *text)
void fl_platform_channel_system_request_app_exit(FlPlatformChannel *self, FlPlatformChannelExitType type, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data)
void fl_platform_channel_respond_system_exit_application(FlMethodCall *method_call, FlPlatformChannelExitResponse exit_response)
void fl_platform_channel_respond_clipboard_has_strings(FlMethodCall *method_call, gboolean has_strings)
FlPlatformChannel * fl_platform_channel_new(FlBinaryMessenger *messenger, FlPlatformChannelVTable *vtable, gpointer user_data)
FlPlatformChannelExitResponse
@ FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT
FlPlatformChannelExitType
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED
static constexpr char kSoundTypeTick[]
static void system_initialization_complete(gpointer user_data)
static FlMethodResponse * system_exit_application(FlMethodCall *method_call, FlPlatformChannelExitType type, gpointer user_data)
static void clipboard_text_has_strings_cb(GtkClipboard *clipboard, const gchar *text, gpointer user_data)
static void fl_platform_handler_dispose(GObject *object)
FlPlatformHandler * fl_platform_handler_new(FlBinaryMessenger *messenger)
static void quit_application()
static FlMethodResponse * clipboard_set_data(FlMethodCall *method_call, const gchar *text, gpointer user_data)
static constexpr char kSoundTypeAlert[]
static FlMethodResponse * clipboard_has_strings(FlMethodCall *method_call, gpointer user_data)
static void fl_platform_handler_init(FlPlatformHandler *self)
static void system_navigator_pop(gpointer user_data)
static constexpr char kUnknownClipboardFormatError[]
static FlPlatformChannelVTable platform_channel_vtable
static void request_app_exit(FlPlatformHandler *self, FlPlatformChannelExitType type)
static FlMethodResponse * clipboard_get_data(FlMethodCall *method_call, const gchar *format, gpointer user_data)
static void fl_platform_handler_class_init(FlPlatformHandlerClass *klass)
static constexpr char kSoundTypeClick[]
static void clipboard_text_cb(GtkClipboard *clipboard, const gchar *text, gpointer user_data)
static constexpr char kTextPlainFormat[]
static void request_app_exit_response_cb(GObject *object, GAsyncResult *result, gpointer user_data)
void fl_platform_handler_request_app_exit(FlPlatformHandler *self)
static void system_sound_play(const gchar *type, gpointer user_data)
static constexpr char kInProgressError[]
uint32_t uint32_t * format
std::u16string text
GdkDisplay * gdk_display_get_default()
Definition mock_gtk.cc:65
GtkClipboard * gtk_clipboard_get_default(GdkDisplay *display)
Definition mock_gtk.cc:363
void gtk_clipboard_request_text(GtkClipboard *clipboard, GtkClipboardTextReceivedFunc callback, gpointer user_data)
Definition mock_gtk.cc:374
void gdk_display_beep(GdkDisplay *display)
Definition mock_gtk.cc:70
void gtk_clipboard_set_text(GtkClipboard *clipboard, const gchar *text, gint len)
Definition mock_gtk.cc:368
FlPlatformChannel * channel
FlMethodCall * exit_application_method_call
FlMethodResponse *(* clipboard_set_data)(FlMethodCall *method_call, const gchar *text, gpointer user_data)