From f50f9dd35bfb66e0bea370b83bfc6faec4e1a907 Mon Sep 17 00:00:00 2001 From: Loic Sharma Date: Thu, 28 Mar 2024 12:12:20 -0700 Subject: [PATCH] [Windows] Move keyboard initialization --- shell/platform/windows/fixtures/main.dart | 41 +++++++++++++++++++ .../windows/flutter_windows_engine.cc | 11 ++--- .../windows/flutter_windows_unittests.cc | 38 +++++++++++++++++ 3 files changed, 82 insertions(+), 8 deletions(-) diff --git a/shell/platform/windows/fixtures/main.dart b/shell/platform/windows/fixtures/main.dart index 5f3d61c9e0a26..9c6ae19a38dc9 100644 --- a/shell/platform/windows/fixtures/main.dart +++ b/shell/platform/windows/fixtures/main.dart @@ -231,6 +231,47 @@ void sendCreatePlatformViewMethod() async { await completed.future; } +@pragma('vm:entry-point') +void sendGetKeyboardState() async { + // The keyboard method channel uses the standard method codec. + // See https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/services/message_codecs.dart#L262 + // for the implementation of the encoding and magic number identifiers. + const int valueNull = 0; + const int valueString = 7; + const int valueMap = 13; + + const String method = 'getKeyboardState'; + final List data = [ + // Method name + valueString, method.length, ...utf8.encode(method), + // Method arguments: null + valueNull, 2, + ]; + + final Completer completer = Completer(); + final ByteData bytes = ByteData.sublistView(Uint8List.fromList(data)); + ui.PlatformDispatcher.instance.sendPlatformMessage('flutter/keyboard', bytes, (ByteData? response) { + // For magic numbers for decoding a reply envelope, see: + // https://github.com/flutter/flutter/blob/67271f69f7f88a4edba6d8023099e3bd27a072d2/packages/flutter/lib/src/services/message_codecs.dart#L577-L587 + const int replyEnvelopeSuccess = 0; + + // Ensure the response is a success containing a map of keyboard states. + if (response == null) { + signalStringValue('Unexpected null response'); + } else if (response.lengthInBytes < 2) { + signalStringValue('Unexpected response length of ${response.lengthInBytes} bytes'); + } else if (response.getUint8(0) != replyEnvelopeSuccess) { + signalStringValue('Unexpected response envelope status: ${response.getUint8(0)}'); + } else if (response.getUint8(1) != valueMap) { + signalStringValue('Unexpected response value magic number: ${response.getUint8(1)}'); + } else { + signalStringValue('Success'); + } + completer.complete(); + }); + await completer.future; +} + @pragma('vm:entry-point') void customEntrypoint() {} diff --git a/shell/platform/windows/flutter_windows_engine.cc b/shell/platform/windows/flutter_windows_engine.cc index 988e1f47954c6..b920e1c829295 100644 --- a/shell/platform/windows/flutter_windows_engine.cc +++ b/shell/platform/windows/flutter_windows_engine.cc @@ -473,6 +473,8 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) { settings_plugin_->StartWatching(); settings_plugin_->SendSettings(); + InitializeKeyboard(); + return true; } @@ -497,7 +499,6 @@ std::unique_ptr FlutterWindowsEngine::CreateView( kImplicitViewId, this, std::move(window), windows_proc_table_); views_[kImplicitViewId] = view.get(); - InitializeKeyboard(); return std::move(view); } @@ -675,10 +676,6 @@ void FlutterWindowsEngine::SendSystemLocales() { } void FlutterWindowsEngine::InitializeKeyboard() { - if (views_.empty()) { - FML_LOG(ERROR) << "Cannot initialize keyboard on Windows headless mode."; - } - auto internal_plugin_messenger = internal_plugin_registrar_->messenger(); KeyboardKeyEmbedderHandler::GetKeyStateHandler get_key_state = GetKeyState; KeyboardKeyEmbedderHandler::MapVirtualKeyToScanCode map_vk_to_scan = @@ -773,9 +770,7 @@ void FlutterWindowsEngine::UpdateSemanticsEnabled(bool enabled) { void FlutterWindowsEngine::OnPreEngineRestart() { // Reset the keyboard's state on hot restart. - if (!views_.empty()) { - InitializeKeyboard(); - } + InitializeKeyboard(); } std::string FlutterWindowsEngine::GetExecutableName() const { diff --git a/shell/platform/windows/flutter_windows_unittests.cc b/shell/platform/windows/flutter_windows_unittests.cc index 37fbbcabd5c24..2a8cbf6bad924 100644 --- a/shell/platform/windows/flutter_windows_unittests.cc +++ b/shell/platform/windows/flutter_windows_unittests.cc @@ -477,5 +477,43 @@ TEST_F(WindowsTest, Lifecycle) { /* bRepaint*/ false); } +TEST_F(WindowsTest, GetKeyboardStateHeadless) { + // Run the test on its own thread so that it can pump its event loop while + // this thread waits. + fml::AutoResetWaitableEvent latch; + auto platform_task_runner = CreateNewThread("test_platform_thread"); + platform_task_runner->PostTask([&]() { + auto& context = GetContext(); + WindowsConfigBuilder builder(context); + builder.SetDartEntrypoint("sendGetKeyboardState"); + + bool done = false; + context.AddNativeFunction( + "SignalStringValue", + CREATE_NATIVE_ENTRY([&](Dart_NativeArguments args) { + auto handle = Dart_GetNativeArgument(args, 0); + ASSERT_FALSE(Dart_IsError(handle)); + auto value = tonic::DartConverter::FromDart(handle); + EXPECT_EQ(value, "Success"); + done = true; + latch.Signal(); + })); + + ViewControllerPtr controller{builder.Run()}; + ASSERT_NE(controller, nullptr); + + // Pump messages for the Windows platform task runner. + ::MSG msg; + while (!done) { + if (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + } + }); + + latch.Wait(); +} + } // namespace testing } // namespace flutter