Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions shell/platform/windows/fixtures/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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<int> data = <int>[
// Method name
valueString, method.length, ...utf8.encode(method),
// Method arguments: null
valueNull, 2,
];

final Completer<void> completer = Completer<void>();
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() {}

Expand Down
11 changes: 3 additions & 8 deletions shell/platform/windows/flutter_windows_engine.cc
Original file line number Diff line number Diff line change
Expand Up @@ -473,6 +473,8 @@ bool FlutterWindowsEngine::Run(std::string_view entrypoint) {
settings_plugin_->StartWatching();
settings_plugin_->SendSettings();

InitializeKeyboard();

return true;
}

Expand All @@ -497,7 +499,6 @@ std::unique_ptr<FlutterWindowsView> FlutterWindowsEngine::CreateView(
kImplicitViewId, this, std::move(window), windows_proc_table_);

views_[kImplicitViewId] = view.get();
InitializeKeyboard();

return std::move(view);
}
Expand Down Expand Up @@ -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 =
Expand Down Expand Up @@ -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 {
Expand Down
38 changes: 38 additions & 0 deletions shell/platform/windows/flutter_windows_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -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<std::string>::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