Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 1a4d906

Browse files
authored
[Linux] Reset keyboard states on engine restart (#28877)
With this PR, the state of the keyboard system will be reset when the engine is reset (typically during a hot restart.)
1 parent f30362b commit 1a4d906

File tree

3 files changed

+79
-39
lines changed

3 files changed

+79
-39
lines changed

shell/platform/linux/fl_keyboard_manager.cc

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,16 @@ struct _FlKeyboardManager {
184184
// automatically released on dispose.
185185
GPtrArray* responder_list;
186186

187-
// An array of #FlKeyboardPendingEvent. FlKeyboardManager must manually
188-
// release the elements unless it is transferring them to
189-
// pending_redispatches.
187+
// An array of #FlKeyboardPendingEvent.
188+
//
189+
// Its elements are *not* unreferenced when removed. When FlKeyboardManager is
190+
// disposed, this array will be set with a free_func so that the elements are
191+
// unreferenced when removed.
190192
GPtrArray* pending_responds;
191193

192-
// An array of #FlKeyboardPendingEvent. FlKeyboardManager must manually
193-
// release the elements.
194+
// An array of #FlKeyboardPendingEvent.
195+
//
196+
// Its elements are unreferenced when removed.
194197
GPtrArray* pending_redispatches;
195198

196199
// The last sequence ID used. Increased by 1 by every use.
@@ -207,18 +210,14 @@ static void fl_keyboard_manager_class_init(FlKeyboardManagerClass* klass) {
207210

208211
static void fl_keyboard_manager_init(FlKeyboardManager* self) {}
209212

210-
static void fl_key_event_destroy_notify(gpointer event);
211213
static void fl_keyboard_manager_dispose(GObject* object) {
212214
FlKeyboardManager* self = FL_KEYBOARD_MANAGER(object);
213215

214216
if (self->text_input_plugin != nullptr)
215217
g_clear_object(&self->text_input_plugin);
216218
g_ptr_array_free(self->responder_list, TRUE);
217-
g_ptr_array_set_free_func(self->pending_responds,
218-
fl_key_event_destroy_notify);
219+
g_ptr_array_set_free_func(self->pending_responds, g_object_unref);
219220
g_ptr_array_free(self->pending_responds, TRUE);
220-
g_ptr_array_set_free_func(self->pending_redispatches,
221-
fl_key_event_destroy_notify);
222221
g_ptr_array_free(self->pending_redispatches, TRUE);
223222

224223
G_OBJECT_CLASS(fl_keyboard_manager_parent_class)->dispose(object);
@@ -278,11 +277,8 @@ static bool fl_keyboard_manager_remove_redispatched(FlKeyboardManager* self,
278277
self->pending_redispatches, static_cast<const uint64_t*>(&hash),
279278
compare_pending_by_hash, &result_index);
280279
if (found) {
281-
FlKeyboardPendingEvent* removed =
282-
FL_KEYBOARD_PENDING_EVENT(g_ptr_array_remove_index_fast(
283-
self->pending_redispatches, result_index));
284-
g_return_val_if_fail(removed != nullptr, TRUE);
285-
g_object_unref(removed);
280+
// The removed object is freed due to `pending_redispatches`'s free_func.
281+
g_ptr_array_remove_index_fast(self->pending_redispatches, result_index);
286282
return TRUE;
287283
} else {
288284
return FALSE;
@@ -343,7 +339,7 @@ FlKeyboardManager* fl_keyboard_manager_new(
343339
self->responder_list = g_ptr_array_new_with_free_func(g_object_unref);
344340

345341
self->pending_responds = g_ptr_array_new();
346-
self->pending_redispatches = g_ptr_array_new();
342+
self->pending_redispatches = g_ptr_array_new_with_free_func(g_object_unref);
347343

348344
self->last_sequence_id = 1;
349345

@@ -399,7 +395,3 @@ gboolean fl_keyboard_manager_is_state_clear(FlKeyboardManager* self) {
399395
return self->pending_responds->len == 0 &&
400396
self->pending_redispatches->len == 0;
401397
}
402-
403-
static void fl_key_event_destroy_notify(gpointer event) {
404-
fl_key_event_dispose(reinterpret_cast<FlKeyEvent*>(event));
405-
}

shell/platform/linux/fl_keyboard_manager_test.cc

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,31 @@ static void store_redispatched_event(gpointer event) {
219219
g_ptr_array_add(redispatched_events(), new_event);
220220
}
221221

222+
// Make sure that the keyboard can be disposed without crashes when there are
223+
// unresolved pending events.
224+
TEST(FlKeyboardManagerTest, DisposeWithUnresolvedPends) {
225+
FlKeyboardManager* manager =
226+
fl_keyboard_manager_new(nullptr, store_redispatched_event);
227+
228+
GPtrArray* call_records = g_ptr_array_new_with_free_func(g_object_unref);
229+
FlKeyMockResponder* responder = fl_key_mock_responder_new(call_records, 1);
230+
fl_keyboard_manager_add_responder(manager, FL_KEY_RESPONDER(responder));
231+
232+
responder->callback_handler = dont_respond;
233+
fl_keyboard_manager_handle_event(
234+
manager,
235+
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x10, false));
236+
237+
responder->callback_handler = respond_true;
238+
fl_keyboard_manager_handle_event(
239+
manager,
240+
fl_key_event_new_by_mock(true, GDK_KEY_a, kKeyCodeKeyA, 0x10, false));
241+
242+
// Passes if the cleanup of `manager` does not crash.
243+
g_object_unref(manager);
244+
g_ptr_array_unref(call_records);
245+
}
246+
222247
TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
223248
GPtrArray* call_records = g_ptr_array_new_with_free_func(g_object_unref);
224249
FlKeyboardCallRecord* record;
@@ -313,7 +338,7 @@ TEST(FlKeyboardManagerTest, SingleDelegateWithAsyncResponds) {
313338

314339
g_ptr_array_clear(redispatched_events());
315340
EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
316-
g_ptr_array_clear(call_records);
341+
g_ptr_array_unref(call_records);
317342
}
318343

319344
TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
@@ -369,7 +394,7 @@ TEST(FlKeyboardManagerTest, SingleDelegateWithSyncResponds) {
369394

370395
EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
371396
g_ptr_array_clear(redispatched_events());
372-
g_ptr_array_clear(call_records);
397+
g_ptr_array_unref(call_records);
373398
}
374399

375400
TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
@@ -437,6 +462,7 @@ TEST(FlKeyboardManagerTest, WithTwoAsyncDelegates) {
437462
g_ptr_array_clear(call_records);
438463

439464
g_ptr_array_clear(redispatched_events());
465+
g_ptr_array_unref(call_records);
440466
}
441467

442468
TEST(FlKeyboardManagerTest, TextInputPluginReturnsFalse) {
@@ -469,7 +495,7 @@ TEST(FlKeyboardManagerTest, TextInputPluginReturnsFalse) {
469495

470496
g_ptr_array_clear(redispatched_events());
471497
EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
472-
g_ptr_array_clear(call_records);
498+
g_ptr_array_unref(call_records);
473499
}
474500

475501
TEST(FlKeyboardManagerTest, TextInputPluginReturnsTrue) {
@@ -495,7 +521,7 @@ TEST(FlKeyboardManagerTest, TextInputPluginReturnsTrue) {
495521
EXPECT_EQ(redispatched_events()->len, 0u);
496522

497523
EXPECT_TRUE(fl_keyboard_manager_is_state_clear(manager));
498-
g_ptr_array_clear(call_records);
524+
g_ptr_array_unref(call_records);
499525
}
500526

501527
} // namespace

shell/platform/linux/fl_view.cc

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ enum { PROP_FLUTTER_PROJECT = 1, PROP_LAST };
6767
static void fl_view_plugin_registry_iface_init(
6868
FlPluginRegistryInterface* iface);
6969

70+
static gboolean text_input_im_filter_by_gtk(GtkIMContext* im_context,
71+
gpointer gdk_event);
72+
73+
static void redispatch_key_event_by_gtk(gpointer gdk_event);
74+
7075
G_DEFINE_TYPE_WITH_CODE(
7176
FlView,
7277
fl_view,
@@ -83,6 +88,33 @@ static void fl_view_update_semantics_node_cb(FlEngine* engine,
8388
self->accessibility_plugin, node);
8489
}
8590

91+
static void fl_view_init_keyboard(FlView* self) {
92+
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
93+
self->keyboard_manager = fl_keyboard_manager_new(
94+
fl_text_input_plugin_new(messenger, self, text_input_im_filter_by_gtk),
95+
redispatch_key_event_by_gtk);
96+
// The embedder responder must be added before the channel responder.
97+
fl_keyboard_manager_add_responder(
98+
self->keyboard_manager,
99+
FL_KEY_RESPONDER(fl_key_embedder_responder_new(self->engine)));
100+
fl_keyboard_manager_add_responder(
101+
self->keyboard_manager,
102+
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger)));
103+
}
104+
105+
// Called when the engine is restarted.
106+
//
107+
// This method should reset states to be as if the engine had just been started,
108+
// which usually indicates the user has requested a hot restart (Shift-R in the
109+
// Flutter CLI.)
110+
static void fl_view_on_pre_engine_restart_cb(FlEngine* engine,
111+
gpointer user_data) {
112+
FlView* self = FL_VIEW(user_data);
113+
114+
g_clear_object(&self->keyboard_manager);
115+
fl_view_init_keyboard(self);
116+
}
117+
86118
// Converts a GDK button event into a Flutter event and sends it to the engine.
87119
static gboolean fl_view_send_pointer_button_event(FlView* self,
88120
GdkEventButton* event) {
@@ -162,11 +194,6 @@ static void fl_view_plugin_registry_iface_init(
162194
iface->get_registrar_for_plugin = fl_view_get_registrar_for_plugin;
163195
}
164196

165-
static void redispatch_key_event_by_gtk(gpointer gdk_event);
166-
167-
static gboolean text_input_im_filter_by_gtk(GtkIMContext* im_context,
168-
gpointer gdk_event);
169-
170197
static gboolean event_box_button_release_event(GtkWidget* widget,
171198
GdkEventButton* event,
172199
FlView* view);
@@ -198,20 +225,13 @@ static void fl_view_constructed(GObject* object) {
198225
self->engine = fl_engine_new(self->project, self->renderer);
199226
fl_engine_set_update_semantics_node_handler(
200227
self->engine, fl_view_update_semantics_node_cb, self, nullptr);
228+
fl_engine_set_on_pre_engine_restart_handler(
229+
self->engine, fl_view_on_pre_engine_restart_cb, self, nullptr);
201230

202231
// Create system channel handlers.
203232
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(self->engine);
204233
self->accessibility_plugin = fl_accessibility_plugin_new(self);
205-
self->keyboard_manager = fl_keyboard_manager_new(
206-
fl_text_input_plugin_new(messenger, self, text_input_im_filter_by_gtk),
207-
redispatch_key_event_by_gtk);
208-
// The embedder responder must be added before the channel responder.
209-
fl_keyboard_manager_add_responder(
210-
self->keyboard_manager,
211-
FL_KEY_RESPONDER(fl_key_embedder_responder_new(self->engine)));
212-
fl_keyboard_manager_add_responder(
213-
self->keyboard_manager,
214-
FL_KEY_RESPONDER(fl_key_channel_responder_new(messenger)));
234+
fl_view_init_keyboard(self);
215235
self->mouse_cursor_plugin = fl_mouse_cursor_plugin_new(messenger, self);
216236
self->platform_plugin = fl_platform_plugin_new(messenger);
217237

@@ -288,6 +308,8 @@ static void fl_view_dispose(GObject* object) {
288308
if (self->engine != nullptr) {
289309
fl_engine_set_update_semantics_node_handler(self->engine, nullptr, nullptr,
290310
nullptr);
311+
fl_engine_set_on_pre_engine_restart_handler(self->engine, nullptr, nullptr,
312+
nullptr);
291313
}
292314

293315
g_clear_object(&self->project);

0 commit comments

Comments
 (0)