7#include <fuchsia/ui/input/cpp/fidl.h>
8#include <fuchsia/ui/input3/cpp/fidl.h>
9#include <fuchsia/ui/views/cpp/fidl.h>
10#include <lib/fidl/cpp/binding.h>
12#include "flutter/fml/logging.h"
13#include "flutter/fml/mapping.h"
14#include "flutter/lib/ui/window/platform_message.h"
15#include "flutter/shell/platform/fuchsia/flutter/keyboard.h"
16#include "flutter/shell/platform/fuchsia/runtime/dart/utils/inlines.h"
17#include "third_party/rapidjson/include/rapidjson/document.h"
18#include "third_party/rapidjson/include/rapidjson/stringbuffer.h"
19#include "third_party/rapidjson/include/rapidjson/writer.h"
38 "TextInputAction.unspecified";
42 const std::string action_string) {
44 return fuchsia::ui::input::InputMethodAction::NEWLINE;
46 return fuchsia::ui::input::InputMethodAction::DONE;
48 return fuchsia::ui::input::InputMethodAction::GO;
50 return fuchsia::ui::input::InputMethodAction::NEXT;
52 return fuchsia::ui::input::InputMethodAction::PREVIOUS;
54 return fuchsia::ui::input::InputMethodAction::NONE;
56 return fuchsia::ui::input::InputMethodAction::SEARCH;
58 return fuchsia::ui::input::InputMethodAction::SEND;
60 return fuchsia::ui::input::InputMethodAction::UNSPECIFIED;
64 FML_VLOG(1) <<
"unexpected action_string: " << action_string;
66 return fuchsia::ui::input::InputMethodAction::DONE;
71 fuchsia::ui::input::InputMethodAction
action) {
72 if (
action == fuchsia::ui::input::InputMethodAction::NEWLINE) {
74 }
else if (
action == fuchsia::ui::input::InputMethodAction::DONE) {
76 }
else if (
action == fuchsia::ui::input::InputMethodAction::GO) {
78 }
else if (
action == fuchsia::ui::input::InputMethodAction::NEXT) {
80 }
else if (
action == fuchsia::ui::input::InputMethodAction::PREVIOUS) {
82 }
else if (
action == fuchsia::ui::input::InputMethodAction::NONE) {
84 }
else if (
action == fuchsia::ui::input::InputMethodAction::SEARCH) {
86 }
else if (
action == fuchsia::ui::input::InputMethodAction::SEND) {
88 }
else if (
action == fuchsia::ui::input::InputMethodAction::UNSPECIFIED) {
93 FML_VLOG(1) <<
"unexpected action: " <<
static_cast<uint32_t
>(
action);
104 interface.set_error_handler([
name](zx_status_t status) {
110 binding.set_error_handler([
name](zx_status_t status) {
116 fuchsia::ui::views::ViewRef view_ref,
117 fuchsia::ui::input::ImeServiceHandle ime_service,
118 fuchsia::ui::input3::KeyboardHandle keyboard,
119 std::function<
void(std::unique_ptr<flutter::PlatformMessage>)>
121 : dispatch_callback_(dispatch_callback),
123 text_sync_service_(ime_service.
Bind()),
124 keyboard_listener_binding_(this),
134 keyboard_->AddListener(std::move(view_ref),
135 keyboard_listener_binding_.NewBinding(), [] {});
140 fuchsia::ui::input::InputMethodAction::DONE));
146 requested_text_action_ =
action;
147 text_sync_service_->GetInputMethodEditor(
148 fuchsia::ui::input::KeyboardType::TEXT,
150 last_text_state_.value(),
151 ime_client_.NewBinding(),
158 text_sync_service_->HideKeyboard();
161 if (ime_client_.is_bound()) {
162 ime_client_.Unbind();
168 fuchsia::ui::input::TextInputState
state,
169 std::unique_ptr<fuchsia::ui::input::InputEvent> input_event) {
170 rapidjson::Document document;
171 auto& allocator = document.GetAllocator();
172 rapidjson::Value encoded_state(rapidjson::kObjectType);
173 encoded_state.AddMember(
"text",
state.text, allocator);
174 encoded_state.AddMember(
"selectionBase",
state.selection.base, allocator);
175 encoded_state.AddMember(
"selectionExtent",
state.selection.extent, allocator);
176 switch (
state.selection.affinity) {
177 case fuchsia::ui::input::TextAffinity::UPSTREAM:
178 encoded_state.AddMember(
"selectionAffinity",
179 rapidjson::Value(
"TextAffinity.upstream"),
182 case fuchsia::ui::input::TextAffinity::DOWNSTREAM:
183 encoded_state.AddMember(
"selectionAffinity",
184 rapidjson::Value(
"TextAffinity.downstream"),
188 encoded_state.AddMember(
"selectionIsDirectional",
true, allocator);
189 encoded_state.AddMember(
"composingBase",
state.composing.start, allocator);
190 encoded_state.AddMember(
"composingExtent",
state.composing.end, allocator);
192 rapidjson::Value
args(rapidjson::kArrayType);
193 args.PushBack(current_text_input_client_, allocator);
194 args.PushBack(encoded_state, allocator);
196 document.SetObject();
197 document.AddMember(
"method",
198 rapidjson::Value(
"TextInputClient.updateEditingState"),
200 document.AddMember(
"args",
args, allocator);
202 rapidjson::StringBuffer
buffer;
203 rapidjson::Writer<rapidjson::StringBuffer> writer(
buffer);
204 document.Accept(writer);
206 const uint8_t*
data =
reinterpret_cast<const uint8_t*
>(
buffer.GetString());
207 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
212 last_text_state_ = std::move(
state);
217 rapidjson::Document document;
218 auto& allocator = document.GetAllocator();
220 rapidjson::Value
args(rapidjson::kArrayType);
221 args.PushBack(current_text_input_client_, allocator);
224 args.PushBack(rapidjson::Value{}.SetString(action_string.c_str(),
225 action_string.length()),
228 document.SetObject();
230 "method", rapidjson::Value(
"TextInputClient.performAction"), allocator);
231 document.AddMember(
"args",
args, allocator);
233 rapidjson::StringBuffer
buffer;
234 rapidjson::Writer<rapidjson::StringBuffer> writer(
buffer);
235 document.Accept(writer);
237 const uint8_t*
data =
reinterpret_cast<const uint8_t*
>(
buffer.GetString());
238 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
247 std::unique_ptr<flutter::PlatformMessage>
message) {
251 rapidjson::Document document;
252 document.Parse(
reinterpret_cast<const char*
>(
data.GetMapping()),
254 if (document.HasParseError() || !document.IsObject()) {
257 auto root = document.GetObject();
258 auto method =
root.FindMember(
"method");
259 if (method ==
root.MemberEnd() || !method->value.IsString()) {
263 if (method->value ==
"TextInput.show") {
265 text_sync_service_->ShowKeyboard();
267 }
else if (method->value ==
"TextInput.hide") {
269 text_sync_service_->HideKeyboard();
271 }
else if (method->value ==
"TextInput.setClient") {
301 current_text_input_client_ = 0;
303 auto args =
root.FindMember(
"args");
304 if (
args ==
root.MemberEnd() || !
args->value.IsArray() ||
305 args->value.Size() != 2)
307 const auto& configuration =
args->value[1];
308 if (!configuration.IsObject()) {
312 current_text_input_client_ =
args->value[0].GetInt();
314 auto initial_text_input_state = fuchsia::ui::input::TextInputState{};
315 initial_text_input_state.text =
"";
316 last_text_state_ = std::move(initial_text_input_state);
318 const auto configuration_object = configuration.GetObject();
323 if (!action_object.IsString()) {
326 const auto action_string =
327 std::string(action_object.GetString(), action_object.GetStringLength());
329 }
else if (method->value ==
"TextInput.setEditingState") {
331 auto args_it =
root.FindMember(
"args");
332 if (args_it ==
root.MemberEnd() || !args_it->value.IsObject()) {
335 const auto&
args = args_it->value;
336 fuchsia::ui::input::TextInputState
state;
339 auto text =
args.FindMember(
"text");
340 if (
text !=
args.MemberEnd() &&
text->value.IsString()) {
343 auto selection_base =
args.FindMember(
"selectionBase");
344 if (selection_base !=
args.MemberEnd() && selection_base->value.IsInt()) {
345 state.selection.base = selection_base->value.GetInt();
347 auto selection_extent =
args.FindMember(
"selectionExtent");
348 if (selection_extent !=
args.MemberEnd() &&
349 selection_extent->value.IsInt()) {
350 state.selection.extent = selection_extent->value.GetInt();
352 auto selection_affinity =
args.FindMember(
"selectionAffinity");
353 if (selection_affinity !=
args.MemberEnd() &&
354 selection_affinity->value.IsString() &&
355 selection_affinity->value ==
"TextAffinity.upstream") {
356 state.selection.affinity = fuchsia::ui::input::TextAffinity::UPSTREAM;
358 state.selection.affinity = fuchsia::ui::input::TextAffinity::DOWNSTREAM;
362 auto composing_base =
args.FindMember(
"composingBase");
363 if (composing_base !=
args.MemberEnd() && composing_base->value.IsInt()) {
364 state.composing.start = composing_base->value.GetInt();
366 auto composing_extent =
args.FindMember(
"composingExtent");
367 if (composing_extent !=
args.MemberEnd() &&
368 composing_extent->value.IsInt()) {
369 state.composing.end = composing_extent->value.GetInt();
371 ime_->SetState(std::move(
state));
373 }
else if (method->value ==
"TextInput.clearClient") {
374 current_text_input_client_ = 0;
375 last_text_state_ = std::nullopt;
376 requested_text_action_ = std::nullopt;
378 }
else if (method->value ==
"TextInput.setCaretRect" ||
379 method->value ==
"TextInput.setEditableSizeAndTransform" ||
380 method->value ==
"TextInput.setMarkedTextRect" ||
381 method->value ==
"TextInput.setStyle") {
390 << method->value.GetString();
399 fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback
callback) {
400 const char*
type =
nullptr;
402 case fuchsia::ui::input3::KeyEventType::PRESSED:
405 case fuchsia::ui::input3::KeyEventType::RELEASED:
408 case fuchsia::ui::input3::KeyEventType::SYNC:
412 case fuchsia::ui::input3::KeyEventType::CANCEL:
420 if (
type ==
nullptr) {
421 FML_VLOG(1) <<
"Unknown key event phase.";
422 callback(fuchsia::ui::input3::KeyEventStatus::NOT_HANDLED);
427 rapidjson::Document document;
428 auto& allocator = document.GetAllocator();
429 document.SetObject();
430 document.AddMember(
"type", rapidjson::Value(
type, strlen(
type)), allocator);
431 document.AddMember(
"keymap", rapidjson::Value(
"fuchsia"), allocator);
432 document.AddMember(
"hidUsage", keyboard_translator_.
LastHIDUsage(),
434 document.AddMember(
"codePoint", keyboard_translator_.
LastCodePoint(),
436 document.AddMember(
"modifiers", keyboard_translator_.
Modifiers(), allocator);
437 rapidjson::StringBuffer
buffer;
438 rapidjson::Writer<rapidjson::StringBuffer> writer(
buffer);
439 document.Accept(writer);
441 const uint8_t*
data =
reinterpret_cast<const uint8_t*
>(
buffer.GetString());
442 dispatch_callback_(std::make_unique<flutter::PlatformMessage>(
447 callback(fuchsia::ui::input3::KeyEventStatus::HANDLED);
bool ConsumeEvent(fuchsia::ui::input3::KeyEvent event)
void DidUpdateState(fuchsia::ui::input::TextInputState state, std::unique_ptr< fuchsia::ui::input::InputEvent > event) override
void OnKeyEvent(fuchsia::ui::input3::KeyEvent key_event, fuchsia::ui::input3::KeyboardListener::OnKeyEventCallback callback) override
void OnAction(fuchsia::ui::input::InputMethodAction action) override
TextDelegate(fuchsia::ui::views::ViewRef view_ref, fuchsia::ui::input::ImeServiceHandle ime_service, fuchsia::ui::input3::KeyboardHandle keyboard, std::function< void(std::unique_ptr< flutter::PlatformMessage >)> dispatch_callback)
bool HandleFlutterTextInputChannelPlatformMessage(std::unique_ptr< flutter::PlatformMessage > message)
Channel handler for kTextInputChannel.
static MallocMapping Copy(const T *begin, const T *end)
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
FlKeyEvent uint64_t FlKeyResponderAsyncCallback callback
#define FML_VLOG(verbose_level)
#define FML_LOG(severity)
#define FML_DCHECK(condition)
Dart_NativeFunction function
FlutterKeyEvent key_event
static const std::string IntoTextInputAction(fuchsia::ui::input::InputMethodAction action)
static constexpr char kTextInputActionPrevious[]
static constexpr char kTextInputActionUnspecified[]
static constexpr char kTextInputActionGo[]
static constexpr char kTextInputActionDone[]
static constexpr char kTextInputActionNewline[]
static constexpr char kTextInputActionNone[]
static constexpr char kInputActionKey[]
constexpr char kTextInputChannel[]
The channel name used for text editing platofrm messages.
static constexpr char kTextInputActionSearch[]
void SetInterfaceErrorHandler(fidl::InterfacePtr< T > &interface, std::string name)
static fuchsia::ui::input::InputMethodAction IntoInputMethodAction(const std::string action_string)
constexpr char kKeyEventChannel[]
The channel name used for key event platform messages.
static constexpr char kTextInputActionNext[]
static constexpr char kTextInputActionSend[]
DEF_SWITCHES_START aot vmservice shared library name
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir Path to the cache directory This is different from the persistent_cache_path in embedder which is used for Skia shader cache icu native lib Path to the library file that exports the ICU data vm service The hostname IP address on which the Dart VM Service should be served If not defaults to or::depending on whether ipv6 is specified vm service A custom Dart VM Service port The default is to pick a randomly available open port disable vm Disable the Dart VM Service The Dart VM Service is never available in release mode disable vm service Disable mDNS Dart VM Service publication Bind to the IPv6 localhost address for the Dart VM Service Ignored if vm service host is set endless trace buffer
static bool Bind(PassBindingsCacheMTL &pass, ShaderStage stage, size_t bind_index, const BufferView &view)
FlutterKeyEventType type
The event kind.
std::shared_ptr< const fml::Mapping > data