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 
6 
7 #include <gtk/gtk.h>
8 #include <cstring>
9 
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 
24  GObject parent_instance;
25 
26  FlPlatformChannel* channel;
27 
29 
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);
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);
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;
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.
153 static 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.
175 static 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.
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) {
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 
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 =
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.
271 }
return window
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_BEGIN_DECLS G_MODULE_EXPORT FlValue gpointer user_data
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)
FlPlatformChannel * fl_platform_channel_new(FlBinaryMessenger *messenger, FlPlatformChannelVTable *vtable, gpointer user_data)
void fl_platform_channel_respond_clipboard_has_strings(FlMethodCall *method_call, gboolean has_strings)
FlPlatformChannelExitResponse
@ FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT
FlPlatformChannelExitType
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE
@ FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED
static constexpr char kSoundTypeTick[]
FlPlatformHandler * fl_platform_handler_new(FlBinaryMessenger *messenger)
static void system_initialization_complete(gpointer user_data)
static FlMethodResponse * clipboard_set_data(FlMethodCall *method_call, const gchar *text, gpointer user_data)
static void clipboard_text_has_strings_cb(GtkClipboard *clipboard, const gchar *text, gpointer user_data)
static FlMethodResponse * system_exit_application(FlMethodCall *method_call, FlPlatformChannelExitType type, gpointer user_data)
static void fl_platform_handler_dispose(GObject *object)
static FlMethodResponse * clipboard_has_strings(FlMethodCall *method_call, gpointer user_data)
static void quit_application()
static constexpr char kSoundTypeAlert[]
static void fl_platform_handler_init(FlPlatformHandler *self)
static void system_navigator_pop(gpointer user_data)
static constexpr char kUnknownClipboardFormatError[]
static FlMethodResponse * clipboard_get_data(FlMethodCall *method_call, const gchar *format, gpointer user_data)
static FlPlatformChannelVTable platform_channel_vtable
static void request_app_exit(FlPlatformHandler *self, FlPlatformChannelExitType type)
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
FlPlatformChannel * channel
FlMethodCall * exit_application_method_call
GCancellable * cancellable
FlMethodResponse *(* clipboard_set_data)(FlMethodCall *method_call, const gchar *text, gpointer user_data)