33// found in the LICENSE file.
44
55#include " flutter/shell/platform/linux/fl_key_event_plugin.h"
6+
7+ #include < gtk/gtk.h>
8+
9+ #include " flutter/shell/platform/linux/fl_text_input_plugin.h"
610#include " flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
711#include " flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
812
@@ -20,33 +24,272 @@ static constexpr char kUnicodeScalarValuesKey[] = "unicodeScalarValues";
2024static constexpr char kGtkToolkit [] = " gtk" ;
2125static constexpr char kLinuxKeymap [] = " linux" ;
2226
27+ static constexpr uint64_t kMaxPendingEvents = 1000 ;
28+
29+ // Definition of the FlKeyEventPlugin GObject class.
30+
2331struct _FlKeyEventPlugin {
2432 GObject parent_instance;
2533
2634 FlBasicMessageChannel* channel = nullptr ;
27- GAsyncReadyCallback response_callback = nullptr ;
35+ FlTextInputPlugin* text_input_plugin = nullptr ;
36+ FlKeyEventPluginCallback response_callback = nullptr ;
37+ GPtrArray* pending_events;
2838};
2939
3040G_DEFINE_TYPE (FlKeyEventPlugin, fl_key_event_plugin, G_TYPE_OBJECT)
3141
42+ // Declare and define a private pair object to bind the id and the event
43+ // together.
44+
45+ G_DECLARE_FINAL_TYPE(FlKeyEventPair,
46+ fl_key_event_pair,
47+ FL,
48+ KEY_EVENT_PAIR,
49+ GObject);
50+
51+ struct _FlKeyEventPair {
52+ GObject parent_instance;
53+
54+ uint64_t id;
55+ GdkEventKey* event;
56+ };
57+
58+ G_DEFINE_TYPE (FlKeyEventPair, fl_key_event_pair, G_TYPE_OBJECT)
59+
60+ // Dispose method for FlKeyEventPair.
61+ static void fl_key_event_pair_dispose(GObject* object) {
62+ // Redundant, but added so that we don't get a warning about unused function
63+ // for FL_IS_KEY_EVENT_PAIR.
64+ g_return_if_fail (FL_IS_KEY_EVENT_PAIR (object));
65+
66+ FlKeyEventPair* self = FL_KEY_EVENT_PAIR (object);
67+ g_clear_pointer (&self->event , gdk_event_free);
68+ G_OBJECT_CLASS (fl_key_event_pair_parent_class)->dispose (object);
69+ }
70+
71+ // Class Initialization method for FlKeyEventPair class.
72+ static void fl_key_event_pair_class_init (FlKeyEventPairClass* klass) {
73+ G_OBJECT_CLASS (klass)->dispose = fl_key_event_pair_dispose;
74+ }
75+
76+ // Initialization for FlKeyEventPair instances.
77+ static void fl_key_event_pair_init (FlKeyEventPair* self) {}
78+
79+ // Creates a new FlKeyEventPair instance, given a unique ID, and an event struct
80+ // to keep.
81+ FlKeyEventPair* fl_key_event_pair_new (uint64_t id, GdkEventKey* event) {
82+ FlKeyEventPair* self =
83+ FL_KEY_EVENT_PAIR (g_object_new (fl_key_event_pair_get_type (), nullptr ));
84+
85+ // Copy the event to preserve refcounts for referenced values (mainly the
86+ // window).
87+ GdkEventKey* event_copy = reinterpret_cast <GdkEventKey*>(
88+ gdk_event_copy (reinterpret_cast <GdkEvent*>(event)));
89+ self->id = id;
90+ self->event = event_copy;
91+ return self;
92+ }
93+
94+ // Declare and define a private class to hold response data from the framework.
95+ G_DECLARE_FINAL_TYPE (FlKeyEventResponseData,
96+ fl_key_event_response_data,
97+ FL,
98+ KEY_EVENT_RESPONSE_DATA,
99+ GObject);
100+
101+ struct _FlKeyEventResponseData {
102+ GObject parent_instance;
103+
104+ FlKeyEventPlugin* plugin;
105+ uint64_t id;
106+ gpointer user_data;
107+ };
108+
109+ // Definition for FlKeyEventResponseData private class.
110+ G_DEFINE_TYPE (FlKeyEventResponseData, fl_key_event_response_data, G_TYPE_OBJECT)
111+
112+ // Dispose method for FlKeyEventResponseData private class.
113+ static void fl_key_event_response_data_dispose(GObject* object) {
114+ g_return_if_fail (FL_IS_KEY_EVENT_RESPONSE_DATA (object));
115+ FlKeyEventResponseData* self = FL_KEY_EVENT_RESPONSE_DATA (object);
116+ // Don't need to weak pointer anymore.
117+ g_object_remove_weak_pointer (G_OBJECT (self->plugin ),
118+ reinterpret_cast <gpointer*>(&(self->plugin )));
119+ }
120+
121+ // Class initialization method for FlKeyEventResponseData private class.
122+ static void fl_key_event_response_data_class_init (
123+ FlKeyEventResponseDataClass* klass) {
124+ G_OBJECT_CLASS (klass)->dispose = fl_key_event_response_data_dispose;
125+ }
126+
127+ // Instance initialization method for FlKeyEventResponseData private class.
128+ static void fl_key_event_response_data_init (FlKeyEventResponseData* self) {}
129+
130+ // Creates a new FlKeyEventResponseData private class with a plugin that created
131+ // the request, a unique ID for tracking, and optional user data.
132+ // Will keep a weak pointer to the plugin.
133+ FlKeyEventResponseData* fl_key_event_response_data_new (FlKeyEventPlugin* plugin,
134+ uint64_t id,
135+ gpointer user_data) {
136+ FlKeyEventResponseData* self = FL_KEY_EVENT_RESPONSE_DATA (
137+ g_object_new (fl_key_event_response_data_get_type (), nullptr ));
138+
139+ self->plugin = plugin;
140+ // Add a weak pointer so we can know if the key event plugin disappeared
141+ // while the framework was responding.
142+ g_object_add_weak_pointer (G_OBJECT (plugin),
143+ reinterpret_cast <gpointer*>(&(self->plugin )));
144+ self->id = id;
145+ self->user_data = user_data;
146+ return self;
147+ }
148+
149+ // Calculates a unique ID for a given GdkEventKey object to use for
150+ // identification of responses from the framework.
151+ static uint64_t get_event_id (GdkEventKey* event) {
152+ // Combine the event timestamp, the type of event, and the hardware keycode
153+ // (scan code) of the event to come up with a unique id for this event that
154+ // can be derived solely from the event data itself, so that we can identify
155+ // whether or not we have seen this event already.
156+ return (event->time & 0xffffffff ) |
157+ (static_cast <uint64_t >(event->type ) & 0xffff ) << 32 |
158+ (static_cast <uint64_t >(event->hardware_keycode ) & 0xffff ) << 48 ;
159+ }
160+
161+ // Finds an event in the event queue that was sent to the framework by its ID.
162+ static GdkEventKey* find_pending_event (FlKeyEventPlugin* self, uint64_t id) {
163+ if (self->pending_events ->len == 0 ||
164+ FL_KEY_EVENT_PAIR (g_ptr_array_index (self->pending_events , 0 ))->id != id) {
165+ return nullptr ;
166+ }
167+
168+ return FL_KEY_EVENT_PAIR (g_ptr_array_index (self->pending_events , 0 ))->event ;
169+ }
170+
171+ // Removes an event from the pending event queue.
172+ static void remove_pending_event (FlKeyEventPlugin* self, uint64_t id) {
173+ if (self->pending_events ->len == 0 ||
174+ FL_KEY_EVENT_PAIR (g_ptr_array_index (self->pending_events , 0 ))->id != id) {
175+ g_warning (
176+ " Tried to remove pending event with id %ld, but the event was out of "
177+ " order, or is unknown." ,
178+ id);
179+ return ;
180+ }
181+ g_ptr_array_remove_index (self->pending_events , 0 );
182+ }
183+
184+ // Adds an GdkEventKey to the pending event queue, with a unique ID, and the
185+ // plugin that added it.
186+ static void add_pending_event (FlKeyEventPlugin* self,
187+ uint64_t id,
188+ GdkEventKey* event) {
189+ if (self->pending_events ->len > kMaxPendingEvents ) {
190+ g_warning (
191+ " There are %d keyboard events that have not yet received a "
192+ " response from the framework. Are responses being sent?" ,
193+ self->pending_events ->len );
194+ }
195+ g_ptr_array_add (self->pending_events , fl_key_event_pair_new (id, event));
196+ }
197+
198+ // Handles a response from the framework to a key event sent to the framework
199+ // earlier.
200+ static void handle_response (GObject* object,
201+ GAsyncResult* result,
202+ gpointer user_data) {
203+ g_autoptr (FlKeyEventResponseData) data =
204+ FL_KEY_EVENT_RESPONSE_DATA (user_data);
205+
206+ // Will also return if the weak pointer has been destroyed.
207+ if (data->plugin == nullptr ) {
208+ return ;
209+ }
210+
211+ FlKeyEventPlugin* self = data->plugin ;
212+
213+ g_autoptr (GError) error = nullptr ;
214+ FlBasicMessageChannel* messageChannel = FL_BASIC_MESSAGE_CHANNEL (object);
215+ FlValue* message =
216+ fl_basic_message_channel_send_finish (messageChannel, result, &error);
217+ if (error != nullptr ) {
218+ g_warning (" Unable to retrieve framework response: %s" , error->message );
219+ return ;
220+ }
221+ g_autoptr (FlValue) handled_value = fl_value_lookup_string (message, " handled" );
222+ bool handled = FALSE ;
223+ if (handled_value != nullptr ) {
224+ GdkEventKey* event = find_pending_event (self, data->id );
225+ if (event == nullptr ) {
226+ g_warning (
227+ " Event response for event id %ld received, but event was received "
228+ " out of order, or is unknown." ,
229+ data->id );
230+ } else {
231+ handled = fl_value_get_bool (handled_value);
232+ if (!handled) {
233+ if (self->text_input_plugin != nullptr ) {
234+ // Propagate the event to the text input plugin.
235+ handled = fl_text_input_plugin_filter_keypress (
236+ self->text_input_plugin , event);
237+ }
238+ // Dispatch the event to other GTK windows if the text input plugin
239+ // didn't handle it. We keep track of the event id so we can recognize
240+ // the event when our window receives it again and not respond to it. If
241+ // the response callback is set, then use that instead.
242+ if (!handled && self->response_callback == nullptr ) {
243+ gdk_event_put (reinterpret_cast <GdkEvent*>(event));
244+ }
245+ }
246+ }
247+ }
248+
249+ if (handled) {
250+ // Because the event was handled, we no longer need to track it. Unhandled
251+ // events will be removed when the event is re-dispatched to the window.
252+ remove_pending_event (self, data->id );
253+ }
254+
255+ if (self->response_callback != nullptr ) {
256+ self->response_callback (object, message, handled, data->user_data );
257+ }
258+ }
259+
260+ // Disposes of an FlKeyEventPlugin instance.
32261static void fl_key_event_plugin_dispose (GObject* object) {
33262 FlKeyEventPlugin* self = FL_KEY_EVENT_PLUGIN (object);
34263
35264 g_clear_object (&self->channel );
265+ g_object_remove_weak_pointer (
266+ G_OBJECT (self->text_input_plugin ),
267+ reinterpret_cast <gpointer*>(&(self->text_input_plugin )));
268+ g_ptr_array_free (self->pending_events , TRUE );
36269
37270 G_OBJECT_CLASS (fl_key_event_plugin_parent_class)->dispose (object);
38271}
39272
273+ // Initializes the FlKeyEventPlugin class methods.
40274static void fl_key_event_plugin_class_init (FlKeyEventPluginClass* klass) {
41275 G_OBJECT_CLASS (klass)->dispose = fl_key_event_plugin_dispose;
42276}
43277
278+ // Initializes an FlKeyEventPlugin instance.
44279static void fl_key_event_plugin_init (FlKeyEventPlugin* self) {}
45280
46- FlKeyEventPlugin* fl_key_event_plugin_new (FlBinaryMessenger* messenger,
47- GAsyncReadyCallback response_callback,
48- const char * channel_name) {
281+ // Creates a new FlKeyEventPlugin instance, with a messenger used to send
282+ // messages to the framework, an FlTextInputPlugin used to handle key events
283+ // that the framework doesn't handle. Mainly for testing purposes, it also takes
284+ // an optional callback to call when a response is received, and an optional
285+ // channel name to use when sending messages.
286+ FlKeyEventPlugin* fl_key_event_plugin_new (
287+ FlBinaryMessenger* messenger,
288+ FlTextInputPlugin* text_input_plugin,
289+ FlKeyEventPluginCallback response_callback,
290+ const char * channel_name) {
49291 g_return_val_if_fail (FL_IS_BINARY_MESSENGER (messenger), nullptr );
292+ g_return_val_if_fail (FL_IS_TEXT_INPUT_PLUGIN (text_input_plugin), nullptr );
50293
51294 FlKeyEventPlugin* self = FL_KEY_EVENT_PLUGIN (
52295 g_object_new (fl_key_event_plugin_get_type (), nullptr ));
@@ -56,15 +299,37 @@ FlKeyEventPlugin* fl_key_event_plugin_new(FlBinaryMessenger* messenger,
56299 messenger, channel_name == nullptr ? kChannelName : channel_name,
57300 FL_MESSAGE_CODEC (codec));
58301 self->response_callback = response_callback;
302+ // Add a weak pointer so we know if the text input plugin goes away.
303+ g_object_add_weak_pointer (
304+ G_OBJECT (text_input_plugin),
305+ reinterpret_cast <gpointer*>(&(self->text_input_plugin )));
306+ self->text_input_plugin = text_input_plugin;
59307
308+ self->pending_events = g_ptr_array_new_with_free_func (g_object_unref);
60309 return self;
61310}
62311
63- void fl_key_event_plugin_send_key_event (FlKeyEventPlugin* self,
312+ // Sends a key event to the framework.
313+ bool fl_key_event_plugin_send_key_event (FlKeyEventPlugin* self,
64314 GdkEventKey* event,
65315 gpointer user_data) {
66- g_return_if_fail (FL_IS_KEY_EVENT_PLUGIN (self));
67- g_return_if_fail (event != nullptr );
316+ g_return_val_if_fail (FL_IS_KEY_EVENT_PLUGIN (self), FALSE );
317+ g_return_val_if_fail (event != nullptr , FALSE );
318+
319+ // Get an ID for the event, so we can match them up when we get a response
320+ // from the framework. Use the event time, type, and hardware keycode as a
321+ // unique ID, since they are part of the event structure that we can look up
322+ // when we receive a random event that may or may not have been
323+ // tracked/produced by this code.
324+ uint64_t id = get_event_id (event);
325+ if (self->pending_events ->len != 0 &&
326+ FL_KEY_EVENT_PAIR (g_ptr_array_index (self->pending_events , 0 ))->id == id) {
327+ // If the event is at the head of the queue of pending events we've seen,
328+ // and has the same id, then we know that this is a re-dispatched event, and
329+ // we shouldn't respond to it, but we should remove it from tracking.
330+ remove_pending_event (self, id);
331+ return FALSE ;
332+ }
68333
69334 const gchar* type;
70335 switch (event->type ) {
@@ -75,7 +340,7 @@ void fl_key_event_plugin_send_key_event(FlKeyEventPlugin* self,
75340 type = kTypeValueUp ;
76341 break ;
77342 default :
78- return ;
343+ return FALSE ;
79344 }
80345
81346 int64_t scan_code = event->hardware_keycode ;
@@ -109,9 +374,9 @@ void fl_key_event_plugin_send_key_event(FlKeyEventPlugin* self,
109374 // Remove lock states from state mask.
110375 guint state = event->state & ~(GDK_LOCK_MASK | GDK_MOD2_MASK);
111376
112- static bool shift_lock_pressed = false ;
113- static bool caps_lock_pressed = false ;
114- static bool num_lock_pressed = false ;
377+ static bool shift_lock_pressed = FALSE ;
378+ static bool caps_lock_pressed = FALSE ;
379+ static bool num_lock_pressed = FALSE ;
115380 switch (event->keyval ) {
116381 case GDK_KEY_Num_Lock:
117382 num_lock_pressed = event->type == GDK_KEY_PRESS;
@@ -144,6 +409,14 @@ void fl_key_event_plugin_send_key_event(FlKeyEventPlugin* self,
144409 fl_value_new_int (unicodeScalarValues));
145410 }
146411
412+ // Track the event as pending a response from the framework.
413+ add_pending_event (self, id, event);
414+ FlKeyEventResponseData* data =
415+ fl_key_event_response_data_new (self, id, user_data);
416+ // Send the message off to the framework for handling (or not).
147417 fl_basic_message_channel_send (self->channel , message, nullptr ,
148- self->response_callback , user_data);
418+ handle_response, data);
419+ // Return true before we know what the framework will do, because if it
420+ // doesn't handle the key, we'll re-dispatch it later.
421+ return TRUE ;
149422}
0 commit comments