Split channel messaging out of handlers (flutter/engine#56667)
Split the channel messaging out of the system channels to make them simpler to understand and refactor. The new channel classes could be automatically generated in a second phase, e.g. using Pigeon to reduce code usage. The new classes don't have tests as they will already be covered by the existing code.
This commit is contained in:
parent
b8e58bbb0b
commit
56686525b2
@ -44986,6 +44986,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_private.
|
|||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_test.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_event.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_event.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_event.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_key_event.h + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_channel.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_channel.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc + ../../../flutter/LICENSE
|
||||||
@ -45011,11 +45013,15 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_method_codec_private.h + ../../
|
|||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_response.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_response.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_response_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_method_response_test.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_channel.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_channel.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_test.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_channel.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_channel.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_platform_handler_test.cc + ../../../flutter/LICENSE
|
||||||
@ -45040,6 +45046,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager.h + ../../../
|
|||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings.h + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_channel.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_channel.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_settings_handler_test.cc + ../../../flutter/LICENSE
|
||||||
@ -45056,6 +45064,8 @@ ORIGIN: ../../../flutter/shell/platform/linux/fl_string_codec.cc + ../../../flut
|
|||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_task_runner.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_task_runner.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_task_runner.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_task_runner.h + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_channel.cc + ../../../flutter/LICENSE
|
||||||
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_channel.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.h + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler.h + ../../../flutter/LICENSE
|
||||||
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc + ../../../flutter/LICENSE
|
ORIGIN: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc + ../../../flutter/LICENSE
|
||||||
@ -47886,6 +47896,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_private.h
|
|||||||
FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_key_embedder_responder_test.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_key_event.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_key_event.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_key_event.h
|
FILE: ../../../flutter/shell/platform/linux/fl_key_event.h
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_channel.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_channel.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h
|
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_keyboard_handler_test.cc
|
||||||
@ -47911,11 +47923,15 @@ FILE: ../../../flutter/shell/platform/linux/fl_method_codec_private.h
|
|||||||
FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_method_codec_test.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_method_response.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_method_response_test.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_channel.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_channel.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.h
|
FILE: ../../../flutter/shell/platform/linux/fl_mouse_cursor_handler.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h
|
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_private.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_pixel_buffer_texture_test.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_platform_channel.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_platform_channel.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler.h
|
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_platform_handler_test.cc
|
||||||
@ -47940,6 +47956,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager.h
|
|||||||
FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_scrolling_manager_test.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_settings.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_settings.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_settings.h
|
FILE: ../../../flutter/shell/platform/linux/fl_settings.h
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_settings_channel.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_settings_channel.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler.h
|
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_settings_handler_test.cc
|
||||||
@ -47956,6 +47974,8 @@ FILE: ../../../flutter/shell/platform/linux/fl_string_codec.cc
|
|||||||
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_string_codec_test.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h
|
FILE: ../../../flutter/shell/platform/linux/fl_task_runner.h
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_text_input_channel.cc
|
||||||
|
FILE: ../../../flutter/shell/platform/linux/fl_text_input_channel.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.cc
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.h
|
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler.h
|
||||||
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc
|
FILE: ../../../flutter/shell/platform/linux/fl_text_input_handler_test.cc
|
||||||
|
@ -116,6 +116,7 @@ source_set("flutter_linux_sources") {
|
|||||||
"fl_key_channel_responder.cc",
|
"fl_key_channel_responder.cc",
|
||||||
"fl_key_embedder_responder.cc",
|
"fl_key_embedder_responder.cc",
|
||||||
"fl_key_event.cc",
|
"fl_key_event.cc",
|
||||||
|
"fl_keyboard_channel.cc",
|
||||||
"fl_keyboard_handler.cc",
|
"fl_keyboard_handler.cc",
|
||||||
"fl_keyboard_layout.cc",
|
"fl_keyboard_layout.cc",
|
||||||
"fl_keyboard_manager.cc",
|
"fl_keyboard_manager.cc",
|
||||||
@ -126,8 +127,10 @@ source_set("flutter_linux_sources") {
|
|||||||
"fl_method_channel.cc",
|
"fl_method_channel.cc",
|
||||||
"fl_method_codec.cc",
|
"fl_method_codec.cc",
|
||||||
"fl_method_response.cc",
|
"fl_method_response.cc",
|
||||||
|
"fl_mouse_cursor_channel.cc",
|
||||||
"fl_mouse_cursor_handler.cc",
|
"fl_mouse_cursor_handler.cc",
|
||||||
"fl_pixel_buffer_texture.cc",
|
"fl_pixel_buffer_texture.cc",
|
||||||
|
"fl_platform_channel.cc",
|
||||||
"fl_platform_handler.cc",
|
"fl_platform_handler.cc",
|
||||||
"fl_plugin_registrar.cc",
|
"fl_plugin_registrar.cc",
|
||||||
"fl_plugin_registry.cc",
|
"fl_plugin_registry.cc",
|
||||||
@ -138,6 +141,7 @@ source_set("flutter_linux_sources") {
|
|||||||
"fl_renderer_headless.cc",
|
"fl_renderer_headless.cc",
|
||||||
"fl_scrolling_manager.cc",
|
"fl_scrolling_manager.cc",
|
||||||
"fl_settings.cc",
|
"fl_settings.cc",
|
||||||
|
"fl_settings_channel.cc",
|
||||||
"fl_settings_handler.cc",
|
"fl_settings_handler.cc",
|
||||||
"fl_settings_portal.cc",
|
"fl_settings_portal.cc",
|
||||||
"fl_socket_accessible.cc",
|
"fl_socket_accessible.cc",
|
||||||
@ -146,6 +150,7 @@ source_set("flutter_linux_sources") {
|
|||||||
"fl_string_codec.cc",
|
"fl_string_codec.cc",
|
||||||
"fl_task_runner.cc",
|
"fl_task_runner.cc",
|
||||||
"fl_task_runner.h",
|
"fl_task_runner.h",
|
||||||
|
"fl_text_input_channel.cc",
|
||||||
"fl_text_input_handler.cc",
|
"fl_text_input_handler.cc",
|
||||||
"fl_text_input_view_delegate.cc",
|
"fl_text_input_view_delegate.cc",
|
||||||
"fl_texture.cc",
|
"fl_texture.cc",
|
||||||
|
@ -0,0 +1,84 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/fl_keyboard_channel.h"
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
||||||
|
|
||||||
|
static constexpr char kChannelName[] = "flutter/keyboard";
|
||||||
|
|
||||||
|
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
|
||||||
|
|
||||||
|
struct _FlKeyboardChannel {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
FlMethodChannel* channel;
|
||||||
|
|
||||||
|
FlKeyboardChannelVTable* vtable;
|
||||||
|
|
||||||
|
gpointer user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlKeyboardChannel, fl_keyboard_channel, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static FlMethodResponse* get_keyboard_state(FlKeyboardChannel* self) {
|
||||||
|
g_autoptr(FlValue) result = self->vtable->get_keyboard_state(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a method call is received from Flutter.
|
||||||
|
static void method_call_cb(FlMethodChannel* channel,
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gpointer user_data) {
|
||||||
|
FlKeyboardChannel* self = FL_KEYBOARD_CHANNEL(user_data);
|
||||||
|
|
||||||
|
const gchar* method = fl_method_call_get_name(method_call);
|
||||||
|
g_autoptr(FlMethodResponse) response = nullptr;
|
||||||
|
if (strcmp(method, kGetKeyboardStateMethod) == 0) {
|
||||||
|
response = get_keyboard_state(self);
|
||||||
|
} else {
|
||||||
|
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send method call response: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_keyboard_channel_dispose(GObject* object) {
|
||||||
|
FlKeyboardChannel* self = FL_KEYBOARD_CHANNEL(object);
|
||||||
|
|
||||||
|
g_clear_object(&self->channel);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(fl_keyboard_channel_parent_class)->dispose(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_keyboard_channel_class_init(FlKeyboardChannelClass* klass) {
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_channel_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_keyboard_channel_init(FlKeyboardChannel* self) {}
|
||||||
|
|
||||||
|
FlKeyboardChannel* fl_keyboard_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlKeyboardChannelVTable* vtable,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
|
g_return_val_if_fail(vtable != nullptr, nullptr);
|
||||||
|
|
||||||
|
FlKeyboardChannel* self = FL_KEYBOARD_CHANNEL(
|
||||||
|
g_object_new(fl_keyboard_channel_get_type(), nullptr));
|
||||||
|
|
||||||
|
self->vtable = vtable;
|
||||||
|
self->user_data = user_data;
|
||||||
|
|
||||||
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||||
|
self->channel =
|
||||||
|
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
||||||
|
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
@ -0,0 +1,46 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_CHANNEL_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_CHANNEL_H_
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlKeyboardChannel,
|
||||||
|
fl_keyboard_channel,
|
||||||
|
FL,
|
||||||
|
KEYBOARD_CHANNEL,
|
||||||
|
GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlKeyboardChannel:
|
||||||
|
*
|
||||||
|
* #FlKeyboardChannel is a channel that implements the shell side
|
||||||
|
* of SystemChannels.keyboard from the Flutter services library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FlValue* (*get_keyboard_state)(gpointer user_data);
|
||||||
|
} FlKeyboardChannelVTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_keyboard_channel_new:
|
||||||
|
* @messenger: an #FlBinaryMessenger
|
||||||
|
* @vtable: callbacks for incoming method calls.
|
||||||
|
* @user_data: data to pass in callbacks.
|
||||||
|
*
|
||||||
|
* Creates a new channel that implements SystemChannels.keyboard from the
|
||||||
|
* Flutter services library.
|
||||||
|
*
|
||||||
|
* Returns: a new #FlKeyboardChannel
|
||||||
|
*/
|
||||||
|
FlKeyboardChannel* fl_keyboard_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlKeyboardChannelVTable* vtable,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_KEYBOARD_CHANNEL_H_
|
@ -4,11 +4,7 @@
|
|||||||
|
|
||||||
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
|
#include "flutter/shell/platform/linux/fl_keyboard_handler.h"
|
||||||
|
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
#include "flutter/shell/platform/linux/fl_keyboard_channel.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
|
||||||
|
|
||||||
static constexpr char kChannelName[] = "flutter/keyboard";
|
|
||||||
static constexpr char kGetKeyboardStateMethod[] = "getKeyboardState";
|
|
||||||
|
|
||||||
struct _FlKeyboardHandler {
|
struct _FlKeyboardHandler {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
@ -16,14 +12,16 @@ struct _FlKeyboardHandler {
|
|||||||
FlKeyboardManager* keyboard_manager;
|
FlKeyboardManager* keyboard_manager;
|
||||||
|
|
||||||
// The channel used by the framework to query the keyboard pressed state.
|
// The channel used by the framework to query the keyboard pressed state.
|
||||||
FlMethodChannel* channel;
|
FlKeyboardChannel* channel;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(FlKeyboardHandler, fl_keyboard_handler, G_TYPE_OBJECT);
|
G_DEFINE_TYPE(FlKeyboardHandler, fl_keyboard_handler, G_TYPE_OBJECT);
|
||||||
|
|
||||||
// Returns the keyboard pressed state.
|
// Returns the keyboard pressed state.
|
||||||
static FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
|
static FlValue* get_keyboard_state(gpointer user_data) {
|
||||||
g_autoptr(FlValue) result = fl_value_new_map();
|
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(user_data);
|
||||||
|
|
||||||
|
FlValue* result = fl_value_new_map();
|
||||||
|
|
||||||
GHashTable* pressing_records =
|
GHashTable* pressing_records =
|
||||||
fl_keyboard_manager_get_pressed_state(self->keyboard_manager);
|
fl_keyboard_manager_get_pressed_state(self->keyboard_manager);
|
||||||
@ -39,28 +37,8 @@ static FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
|
|||||||
fl_value_new_int(logical_key));
|
fl_value_new_int(logical_key));
|
||||||
},
|
},
|
||||||
result);
|
result);
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when a method call on flutter/keyboard is received from Flutter.
|
return result;
|
||||||
static void method_call_handler(FlMethodChannel* channel,
|
|
||||||
FlMethodCall* method_call,
|
|
||||||
gpointer user_data) {
|
|
||||||
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(user_data);
|
|
||||||
|
|
||||||
const gchar* method = fl_method_call_get_name(method_call);
|
|
||||||
|
|
||||||
g_autoptr(FlMethodResponse) response = nullptr;
|
|
||||||
if (strcmp(method, kGetKeyboardStateMethod) == 0) {
|
|
||||||
response = get_keyboard_state(self);
|
|
||||||
} else {
|
|
||||||
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(GError) error = nullptr;
|
|
||||||
if (!fl_method_call_respond(method_call, response, &error)) {
|
|
||||||
g_warning("Failed to send method call response: %s", error->message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fl_keyboard_handler_dispose(GObject* object) {
|
static void fl_keyboard_handler_dispose(GObject* object) {
|
||||||
@ -76,6 +54,9 @@ static void fl_keyboard_handler_class_init(FlKeyboardHandlerClass* klass) {
|
|||||||
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_dispose;
|
G_OBJECT_CLASS(klass)->dispose = fl_keyboard_handler_dispose;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FlKeyboardChannelVTable keyboard_channel_vtable = {
|
||||||
|
.get_keyboard_state = get_keyboard_state};
|
||||||
|
|
||||||
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {}
|
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {}
|
||||||
|
|
||||||
FlKeyboardHandler* fl_keyboard_handler_new(
|
FlKeyboardHandler* fl_keyboard_handler_new(
|
||||||
@ -87,10 +68,7 @@ FlKeyboardHandler* fl_keyboard_handler_new(
|
|||||||
self->keyboard_manager = FL_KEYBOARD_MANAGER(g_object_ref(keyboard_manager));
|
self->keyboard_manager = FL_KEYBOARD_MANAGER(g_object_ref(keyboard_manager));
|
||||||
|
|
||||||
// Setup the flutter/keyboard channel.
|
// Setup the flutter/keyboard channel.
|
||||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
|
||||||
self->channel =
|
self->channel =
|
||||||
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
fl_keyboard_channel_new(messenger, &keyboard_channel_vtable, self);
|
||||||
fl_method_channel_set_method_call_handler(self->channel, method_call_handler,
|
|
||||||
self, nullptr);
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,104 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/fl_mouse_cursor_channel.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
||||||
|
|
||||||
|
static constexpr char kChannelName[] = "flutter/mousecursor";
|
||||||
|
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
||||||
|
static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
|
||||||
|
static constexpr char kKindKey[] = "kind";
|
||||||
|
|
||||||
|
struct _FlMouseCursorChannel {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
FlMethodChannel* channel;
|
||||||
|
|
||||||
|
FlMouseCursorChannelVTable* vtable;
|
||||||
|
|
||||||
|
gpointer user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlMouseCursorChannel, fl_mouse_cursor_channel, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
// Sets the mouse cursor.
|
||||||
|
static FlMethodResponse* activate_system_cursor(FlMouseCursorChannel* self,
|
||||||
|
FlValue* args) {
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
FlValue* kind_value = fl_value_lookup_string(args, kKindKey);
|
||||||
|
const gchar* kind = nullptr;
|
||||||
|
if (fl_value_get_type(kind_value) == FL_VALUE_TYPE_STRING) {
|
||||||
|
kind = fl_value_get_string(kind_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
self->vtable->activate_system_cursor(kind, self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a method call is received from Flutter.
|
||||||
|
static void method_call_cb(FlMethodChannel* channel,
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gpointer user_data) {
|
||||||
|
FlMouseCursorChannel* self = FL_MOUSE_CURSOR_CHANNEL(user_data);
|
||||||
|
|
||||||
|
const gchar* method = fl_method_call_get_name(method_call);
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response = nullptr;
|
||||||
|
if (strcmp(method, kActivateSystemCursorMethod) == 0) {
|
||||||
|
response = activate_system_cursor(self, args);
|
||||||
|
} else {
|
||||||
|
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send method call response: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_mouse_cursor_channel_dispose(GObject* object) {
|
||||||
|
FlMouseCursorChannel* self = FL_MOUSE_CURSOR_CHANNEL(object);
|
||||||
|
|
||||||
|
g_clear_object(&self->channel);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(fl_mouse_cursor_channel_parent_class)->dispose(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_mouse_cursor_channel_class_init(
|
||||||
|
FlMouseCursorChannelClass* klass) {
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = fl_mouse_cursor_channel_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_mouse_cursor_channel_init(FlMouseCursorChannel* self) {}
|
||||||
|
|
||||||
|
FlMouseCursorChannel* fl_mouse_cursor_channel_new(
|
||||||
|
FlBinaryMessenger* messenger,
|
||||||
|
FlMouseCursorChannelVTable* vtable,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
|
|
||||||
|
FlMouseCursorChannel* self = FL_MOUSE_CURSOR_CHANNEL(
|
||||||
|
g_object_new(fl_mouse_cursor_channel_get_type(), nullptr));
|
||||||
|
|
||||||
|
self->vtable = vtable;
|
||||||
|
self->user_data = user_data;
|
||||||
|
|
||||||
|
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
||||||
|
self->channel =
|
||||||
|
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
||||||
|
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
@ -0,0 +1,47 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_MOUSE_CURSOR_CHANNEL_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_MOUSE_CURSOR_CHANNEL_H_
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlMouseCursorChannel,
|
||||||
|
fl_mouse_cursor_channel,
|
||||||
|
FL,
|
||||||
|
MOUSE_CURSOR_CHANNEL,
|
||||||
|
GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlMouseCursorChannel:
|
||||||
|
*
|
||||||
|
* #FlMouseCursorChannel is a cursor channel that implements the shell
|
||||||
|
* side of SystemChannels.mouseCursor from the Flutter services library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void (*activate_system_cursor)(const gchar* kind, gpointer user_data);
|
||||||
|
} FlMouseCursorChannelVTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_mouse_cursor_channel_new:
|
||||||
|
* @messenger: an #FlBinaryMessenger.
|
||||||
|
* @vtable: callbacks for incoming method calls.
|
||||||
|
* @user_data: data to pass in callbacks.
|
||||||
|
*
|
||||||
|
* Creates a new channel that implements SystemChannels.mouseCursor from the
|
||||||
|
* Flutter services library.
|
||||||
|
*
|
||||||
|
* Returns: a new #FlMouseCursorChannel.
|
||||||
|
*/
|
||||||
|
FlMouseCursorChannel* fl_mouse_cursor_channel_new(
|
||||||
|
FlBinaryMessenger* messenger,
|
||||||
|
FlMouseCursorChannelVTable* vtable,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_MOUSE_CURSOR_CHANNEL_H_
|
@ -6,20 +6,14 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
#include "flutter/shell/platform/linux/fl_mouse_cursor_channel.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_standard_method_codec.h"
|
|
||||||
|
|
||||||
static constexpr char kChannelName[] = "flutter/mousecursor";
|
|
||||||
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
|
||||||
static constexpr char kActivateSystemCursorMethod[] = "activateSystemCursor";
|
|
||||||
static constexpr char kKindKey[] = "kind";
|
|
||||||
|
|
||||||
static constexpr char kFallbackCursor[] = "default";
|
static constexpr char kFallbackCursor[] = "default";
|
||||||
|
|
||||||
struct _FlMouseCursorHandler {
|
struct _FlMouseCursorHandler {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
|
||||||
FlMethodChannel* channel;
|
FlMouseCursorChannel* channel;
|
||||||
|
|
||||||
GHashTable* system_cursor_table;
|
GHashTable* system_cursor_table;
|
||||||
|
|
||||||
@ -89,18 +83,8 @@ static void populate_system_cursor_table(GHashTable* table) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Sets the mouse cursor.
|
// Sets the mouse cursor.
|
||||||
FlMethodResponse* activate_system_cursor(FlMouseCursorHandler* self,
|
static void activate_system_cursor(const gchar* kind, gpointer user_data) {
|
||||||
FlValue* args) {
|
FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(user_data);
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
FlValue* kind_value = fl_value_lookup_string(args, kKindKey);
|
|
||||||
const gchar* kind = nullptr;
|
|
||||||
if (fl_value_get_type(kind_value) == FL_VALUE_TYPE_STRING) {
|
|
||||||
kind = fl_value_get_string(kind_value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self->system_cursor_table == nullptr) {
|
if (self->system_cursor_table == nullptr) {
|
||||||
self->system_cursor_table = g_hash_table_new(g_str_hash, g_str_equal);
|
self->system_cursor_table = g_hash_table_new(g_str_hash, g_str_equal);
|
||||||
@ -117,30 +101,6 @@ FlMethodResponse* activate_system_cursor(FlMouseCursorHandler* self,
|
|||||||
self->cursor_name = g_strdup(cursor_name);
|
self->cursor_name = g_strdup(cursor_name);
|
||||||
|
|
||||||
g_signal_emit(self, fl_mouse_cursor_handler_signals[kSignalCursorChanged], 0);
|
g_signal_emit(self, fl_mouse_cursor_handler_signals[kSignalCursorChanged], 0);
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when a method call is received from Flutter.
|
|
||||||
static void method_call_cb(FlMethodChannel* channel,
|
|
||||||
FlMethodCall* method_call,
|
|
||||||
gpointer user_data) {
|
|
||||||
FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(user_data);
|
|
||||||
|
|
||||||
const gchar* method = fl_method_call_get_name(method_call);
|
|
||||||
FlValue* args = fl_method_call_get_args(method_call);
|
|
||||||
|
|
||||||
g_autoptr(FlMethodResponse) response = nullptr;
|
|
||||||
if (strcmp(method, kActivateSystemCursorMethod) == 0) {
|
|
||||||
response = activate_system_cursor(self, args);
|
|
||||||
} else {
|
|
||||||
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(GError) error = nullptr;
|
|
||||||
if (!fl_method_call_respond(method_call, response, &error)) {
|
|
||||||
g_warning("Failed to send method call response: %s", error->message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fl_mouse_cursor_handler_dispose(GObject* object) {
|
static void fl_mouse_cursor_handler_dispose(GObject* object) {
|
||||||
@ -166,6 +126,10 @@ static void fl_mouse_cursor_handler_init(FlMouseCursorHandler* self) {
|
|||||||
self->cursor_name = g_strdup("");
|
self->cursor_name = g_strdup("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FlMouseCursorChannelVTable mouse_cursor_vtable = {
|
||||||
|
.activate_system_cursor = activate_system_cursor,
|
||||||
|
};
|
||||||
|
|
||||||
FlMouseCursorHandler* fl_mouse_cursor_handler_new(
|
FlMouseCursorHandler* fl_mouse_cursor_handler_new(
|
||||||
FlBinaryMessenger* messenger) {
|
FlBinaryMessenger* messenger) {
|
||||||
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
@ -173,11 +137,8 @@ FlMouseCursorHandler* fl_mouse_cursor_handler_new(
|
|||||||
FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(
|
FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(
|
||||||
g_object_new(fl_mouse_cursor_handler_get_type(), nullptr));
|
g_object_new(fl_mouse_cursor_handler_get_type(), nullptr));
|
||||||
|
|
||||||
g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();
|
|
||||||
self->channel =
|
self->channel =
|
||||||
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
fl_mouse_cursor_channel_new(messenger, &mouse_cursor_vtable, self);
|
||||||
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
347
engine/src/flutter/shell/platform/linux/fl_platform_channel.cc
Normal file
347
engine/src/flutter/shell/platform/linux/fl_platform_channel.cc
Normal file
@ -0,0 +1,347 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/fl_platform_channel.h"
|
||||||
|
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||||
|
|
||||||
|
static constexpr char kChannelName[] = "flutter/platform";
|
||||||
|
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
||||||
|
static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
|
||||||
|
static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
|
||||||
|
static constexpr char kClipboardHasStringsMethod[] = "Clipboard.hasStrings";
|
||||||
|
static constexpr char kExitApplicationMethod[] = "System.exitApplication";
|
||||||
|
static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
|
||||||
|
static constexpr char kInitializationCompleteMethod[] =
|
||||||
|
"System.initializationComplete";
|
||||||
|
static constexpr char kPlaySoundMethod[] = "SystemSound.play";
|
||||||
|
static constexpr char kSystemNavigatorPopMethod[] = "SystemNavigator.pop";
|
||||||
|
static constexpr char kTextKey[] = "text";
|
||||||
|
static constexpr char kValueKey[] = "value";
|
||||||
|
|
||||||
|
static constexpr char kExitTypeKey[] = "type";
|
||||||
|
static constexpr char kExitTypeCancelable[] = "cancelable";
|
||||||
|
static constexpr char kExitTypeRequired[] = "required";
|
||||||
|
|
||||||
|
static constexpr char kExitResponseKey[] = "response";
|
||||||
|
static constexpr char kExitResponseCancel[] = "cancel";
|
||||||
|
static constexpr char kExitResponseExit[] = "exit";
|
||||||
|
|
||||||
|
struct _FlPlatformChannel {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
FlMethodChannel* channel;
|
||||||
|
|
||||||
|
// Handlers for incoming method calls.
|
||||||
|
FlPlatformChannelVTable* vtable;
|
||||||
|
|
||||||
|
// User data to pass to method call handlers.
|
||||||
|
gpointer user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlPlatformChannel, fl_platform_channel, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static FlMethodResponse* clipboard_set_data(FlPlatformChannel* self,
|
||||||
|
FlMethodCall* method_call) {
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
FlValue* text_value = fl_value_lookup_string(args, kTextKey);
|
||||||
|
if (text_value == nullptr ||
|
||||||
|
fl_value_get_type(text_value) != FL_VALUE_TYPE_STRING) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Missing clipboard text", nullptr));
|
||||||
|
}
|
||||||
|
const gchar* text = fl_value_get_string(text_value);
|
||||||
|
|
||||||
|
return self->vtable->clipboard_set_data(method_call, text, self->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* clipboard_get_data(FlPlatformChannel* self,
|
||||||
|
FlMethodCall* method_call) {
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Expected string", nullptr));
|
||||||
|
}
|
||||||
|
const gchar* format = fl_value_get_string(args);
|
||||||
|
|
||||||
|
return self->vtable->clipboard_get_data(method_call, format, self->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* clipboard_has_strings(FlPlatformChannel* self,
|
||||||
|
FlMethodCall* method_call) {
|
||||||
|
return self->vtable->clipboard_has_strings(method_call, self->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the exit response from a System.requestAppExit method call.
|
||||||
|
FlPlatformChannelExitResponse get_exit_response(FlMethodResponse* response) {
|
||||||
|
if (response == nullptr) {
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
FlValue* result = fl_method_response_get_result(response, &error);
|
||||||
|
if (result == nullptr) {
|
||||||
|
g_warning("Error returned from System.requestAppExit: %s", error->message);
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT;
|
||||||
|
}
|
||||||
|
if (fl_value_get_type(result) != FL_VALUE_TYPE_MAP) {
|
||||||
|
g_warning("System.requestAppExit result argument map missing or malformed");
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
FlValue* response_value = fl_value_lookup_string(result, kExitResponseKey);
|
||||||
|
if (fl_value_get_type(response_value) != FL_VALUE_TYPE_STRING) {
|
||||||
|
g_warning("Invalid response from System.requestAppExit");
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT;
|
||||||
|
}
|
||||||
|
const char* response_string = fl_value_get_string(response_value);
|
||||||
|
|
||||||
|
if (strcmp(response_string, kExitResponseCancel) == 0) {
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_CANCEL;
|
||||||
|
} else if (strcmp(response_string, kExitResponseExit) == 0) {
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If something went wrong, then just exit.
|
||||||
|
return FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT;
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* system_exit_application(FlPlatformChannel* self,
|
||||||
|
FlMethodCall* method_call) {
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
FlValue* type_value = fl_value_lookup_string(args, kExitTypeKey);
|
||||||
|
if (type_value == nullptr ||
|
||||||
|
fl_value_get_type(type_value) != FL_VALUE_TYPE_STRING) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Missing type argument", nullptr));
|
||||||
|
}
|
||||||
|
const char* type_string = fl_value_get_string(type_value);
|
||||||
|
FlPlatformChannelExitType type;
|
||||||
|
if (strcmp(type_string, kExitTypeCancelable) == 0) {
|
||||||
|
type = FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE;
|
||||||
|
} else if (strcmp(type_string, kExitTypeRequired) == 0) {
|
||||||
|
type = FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED;
|
||||||
|
} else {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Invalid exit type", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
return self->vtable->system_exit_application(method_call, type,
|
||||||
|
self->user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* system_initialization_complete(
|
||||||
|
FlPlatformChannel* self) {
|
||||||
|
self->vtable->system_initialization_complete(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* system_sound_play(FlPlatformChannel* self,
|
||||||
|
FlValue* args) {
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Expected string", nullptr));
|
||||||
|
}
|
||||||
|
const gchar* type = fl_value_get_string(args);
|
||||||
|
|
||||||
|
self->vtable->system_sound_play(type, self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static FlMethodResponse* system_navigator_pop(FlPlatformChannel* self) {
|
||||||
|
self->vtable->system_navigator_pop(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void method_call_cb(FlMethodChannel* channel,
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gpointer user_data) {
|
||||||
|
FlPlatformChannel* self = FL_PLATFORM_CHANNEL(user_data);
|
||||||
|
|
||||||
|
const gchar* method = fl_method_call_get_name(method_call);
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response = nullptr;
|
||||||
|
if (strcmp(method, kSetClipboardDataMethod) == 0) {
|
||||||
|
response = clipboard_set_data(self, method_call);
|
||||||
|
} else if (strcmp(method, kGetClipboardDataMethod) == 0) {
|
||||||
|
response = clipboard_get_data(self, method_call);
|
||||||
|
} else if (strcmp(method, kClipboardHasStringsMethod) == 0) {
|
||||||
|
response = clipboard_has_strings(self, method_call);
|
||||||
|
} else if (strcmp(method, kExitApplicationMethod) == 0) {
|
||||||
|
response = system_exit_application(self, method_call);
|
||||||
|
} else if (strcmp(method, kInitializationCompleteMethod) == 0) {
|
||||||
|
response = system_initialization_complete(self);
|
||||||
|
} else if (strcmp(method, kPlaySoundMethod) == 0) {
|
||||||
|
response = system_sound_play(self, args);
|
||||||
|
} else if (strcmp(method, kSystemNavigatorPopMethod) == 0) {
|
||||||
|
response = system_navigator_pop(self);
|
||||||
|
} else {
|
||||||
|
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (response != nullptr) {
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send method call response: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_platform_channel_dispose(GObject* object) {
|
||||||
|
FlPlatformChannel* self = FL_PLATFORM_CHANNEL(object);
|
||||||
|
|
||||||
|
g_clear_object(&self->channel);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(fl_platform_channel_parent_class)->dispose(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_platform_channel_class_init(FlPlatformChannelClass* klass) {
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = fl_platform_channel_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_platform_channel_init(FlPlatformChannel* self) {}
|
||||||
|
|
||||||
|
FlPlatformChannel* fl_platform_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlPlatformChannelVTable* vtable,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
|
g_return_val_if_fail(vtable != nullptr, nullptr);
|
||||||
|
|
||||||
|
FlPlatformChannel* self = FL_PLATFORM_CHANNEL(
|
||||||
|
g_object_new(fl_platform_channel_get_type(), nullptr));
|
||||||
|
|
||||||
|
self->vtable = vtable;
|
||||||
|
self->user_data = user_data;
|
||||||
|
|
||||||
|
g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
|
||||||
|
self->channel =
|
||||||
|
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
||||||
|
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_platform_channel_system_request_app_exit(FlPlatformChannel* self,
|
||||||
|
FlPlatformChannelExitType type,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_if_fail(FL_IS_PLATFORM_CHANNEL(self));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) args = fl_value_new_map();
|
||||||
|
const gchar* type_string;
|
||||||
|
switch (type) {
|
||||||
|
case FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE:
|
||||||
|
type_string = kExitTypeCancelable;
|
||||||
|
break;
|
||||||
|
case FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED:
|
||||||
|
type_string = kExitTypeRequired;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
fl_value_set_string_take(args, kExitTypeKey,
|
||||||
|
fl_value_new_string(type_string));
|
||||||
|
fl_method_channel_invoke_method(self->channel, kRequestAppExitMethod, args,
|
||||||
|
cancellable, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean fl_platform_channel_system_request_app_exit_finish(
|
||||||
|
GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
FlPlatformChannelExitResponse* exit_response,
|
||||||
|
GError** error) {
|
||||||
|
g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
|
||||||
|
FL_METHOD_CHANNEL(object), result, error);
|
||||||
|
if (response == nullptr) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*exit_response = get_exit_response(response);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_clipboard_get_data(FlMethodCall* method_call,
|
||||||
|
const gchar* text) {
|
||||||
|
g_autoptr(FlValue) result = nullptr;
|
||||||
|
if (text != nullptr) {
|
||||||
|
result = fl_value_new_map();
|
||||||
|
fl_value_set_string_take(result, kTextKey, fl_value_new_string(text));
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response =
|
||||||
|
FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send response to %s: %s", kGetClipboardDataMethod,
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_clipboard_has_strings(
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gboolean has_strings) {
|
||||||
|
g_autoptr(FlValue) result = fl_value_new_map();
|
||||||
|
fl_value_set_string_take(result, kValueKey, fl_value_new_bool(has_strings));
|
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response =
|
||||||
|
FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send response to %s: %s", kClipboardHasStringsMethod,
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_system_exit_application(
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
FlPlatformChannelExitResponse exit_response) {
|
||||||
|
g_autoptr(FlMethodResponse) response =
|
||||||
|
fl_platform_channel_make_system_request_app_exit_response(exit_response);
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send response to System.exitApplication: %s",
|
||||||
|
error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FlMethodResponse* fl_platform_channel_make_system_request_app_exit_response(
|
||||||
|
FlPlatformChannelExitResponse exit_response) {
|
||||||
|
g_autoptr(FlValue) exit_result = fl_value_new_map();
|
||||||
|
const gchar* exit_response_string;
|
||||||
|
switch (exit_response) {
|
||||||
|
case FL_PLATFORM_CHANNEL_EXIT_RESPONSE_CANCEL:
|
||||||
|
exit_response_string = kExitResponseCancel;
|
||||||
|
break;
|
||||||
|
case FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT:
|
||||||
|
exit_response_string = kExitResponseExit;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
fl_value_set_string_take(exit_result, kExitResponseKey,
|
||||||
|
fl_value_new_string(exit_response_string));
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(exit_result));
|
||||||
|
}
|
112
engine/src/flutter/shell/platform/linux/fl_platform_channel.h
Normal file
112
engine/src/flutter/shell/platform/linux/fl_platform_channel.h
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_CHANNEL_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_CHANNEL_H_
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_call.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE,
|
||||||
|
FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED,
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
} FlPlatformChannelExitType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
FL_PLATFORM_CHANNEL_EXIT_RESPONSE_CANCEL,
|
||||||
|
FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT,
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
} FlPlatformChannelExitResponse;
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlPlatformChannel,
|
||||||
|
fl_platform_channel,
|
||||||
|
FL,
|
||||||
|
PLATFORM_CHANNEL,
|
||||||
|
GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlPlatformChannel:
|
||||||
|
*
|
||||||
|
* #FlPlatformChannel is a channel that implements the shell side
|
||||||
|
* of SystemChannels.platform from the Flutter services library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
FlMethodResponse* (*clipboard_set_data)(FlMethodCall* method_call,
|
||||||
|
const gchar* text,
|
||||||
|
gpointer user_data);
|
||||||
|
FlMethodResponse* (*clipboard_get_data)(FlMethodCall* method_call,
|
||||||
|
const gchar* format,
|
||||||
|
gpointer user_data);
|
||||||
|
FlMethodResponse* (*clipboard_has_strings)(FlMethodCall* method_call,
|
||||||
|
gpointer user_data);
|
||||||
|
FlMethodResponse* (*system_exit_application)(FlMethodCall* method_call,
|
||||||
|
FlPlatformChannelExitType type,
|
||||||
|
gpointer user_data);
|
||||||
|
void (*system_initialization_complete)(gpointer user_data);
|
||||||
|
void (*system_sound_play)(const gchar* type, gpointer user_data);
|
||||||
|
void (*system_navigator_pop)(gpointer user_data);
|
||||||
|
} FlPlatformChannelVTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_platform_channel_new:
|
||||||
|
* @messenger: an #FlBinaryMessenger
|
||||||
|
* @vtable: callbacks for incoming method calls.
|
||||||
|
* @user_data: data to pass in callbacks.
|
||||||
|
*
|
||||||
|
* Creates a new channel that implements SystemChannels.platform from the
|
||||||
|
* Flutter services library.
|
||||||
|
*
|
||||||
|
* Returns: a new #FlPlatformChannel
|
||||||
|
*/
|
||||||
|
FlPlatformChannel* fl_platform_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlPlatformChannelVTable* vtable,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_platform_channel_system_request_app_exit:
|
||||||
|
* @channel: an #FlPlatformChannel
|
||||||
|
*
|
||||||
|
* Request the application exits (i.e. due to the window being requested to be
|
||||||
|
* closed).
|
||||||
|
*
|
||||||
|
* Calling this will only send an exit request to the framework if the framework
|
||||||
|
* has already indicated that it is ready to receive requests by sending a
|
||||||
|
* "System.initializationComplete" method call on the platform channel. Calls
|
||||||
|
* before initialization is complete will result in an immediate exit.
|
||||||
|
*/
|
||||||
|
void fl_platform_channel_system_request_app_exit(FlPlatformChannel* channel,
|
||||||
|
FlPlatformChannelExitType type,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
gboolean fl_platform_channel_system_request_app_exit_finish(
|
||||||
|
GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
FlPlatformChannelExitResponse* exit_response,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_system_exit_application(
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
FlPlatformChannelExitResponse exit_response);
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_clipboard_get_data(FlMethodCall* method_call,
|
||||||
|
const gchar* text);
|
||||||
|
|
||||||
|
void fl_platform_channel_respond_clipboard_has_strings(
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gboolean has_strings);
|
||||||
|
|
||||||
|
FlMethodResponse* fl_platform_channel_make_system_request_app_exit_response(
|
||||||
|
FlPlatformChannelExitResponse exit_response);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_PLATFORM_CHANNEL_H_
|
@ -7,33 +7,12 @@
|
|||||||
#include <gtk/gtk.h>
|
#include <gtk/gtk.h>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
|
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
|
#include "flutter/shell/platform/linux/fl_platform_channel.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||||
|
|
||||||
static constexpr char kChannelName[] = "flutter/platform";
|
static constexpr char kInProgressError[] = "In Progress";
|
||||||
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
|
||||||
static constexpr char kUnknownClipboardFormatError[] =
|
static constexpr char kUnknownClipboardFormatError[] =
|
||||||
"Unknown Clipboard Format";
|
"Unknown Clipboard Format";
|
||||||
static constexpr char kInProgressError[] = "In Progress";
|
|
||||||
static constexpr char kGetClipboardDataMethod[] = "Clipboard.getData";
|
|
||||||
static constexpr char kSetClipboardDataMethod[] = "Clipboard.setData";
|
|
||||||
static constexpr char kClipboardHasStringsMethod[] = "Clipboard.hasStrings";
|
|
||||||
static constexpr char kExitApplicationMethod[] = "System.exitApplication";
|
|
||||||
static constexpr char kRequestAppExitMethod[] = "System.requestAppExit";
|
|
||||||
static constexpr char kInitializationCompleteMethod[] =
|
|
||||||
"System.initializationComplete";
|
|
||||||
static constexpr char kPlaySoundMethod[] = "SystemSound.play";
|
|
||||||
static constexpr char kSystemNavigatorPopMethod[] = "SystemNavigator.pop";
|
|
||||||
static constexpr char kTextKey[] = "text";
|
|
||||||
static constexpr char kValueKey[] = "value";
|
|
||||||
|
|
||||||
static constexpr char kExitTypeKey[] = "type";
|
|
||||||
static constexpr char kExitTypeCancelable[] = "cancelable";
|
|
||||||
static constexpr char kExitTypeRequired[] = "required";
|
|
||||||
|
|
||||||
static constexpr char kExitResponseKey[] = "response";
|
|
||||||
static constexpr char kExitResponseCancel[] = "cancel";
|
|
||||||
static constexpr char kExitResponseExit[] = "exit";
|
|
||||||
|
|
||||||
static constexpr char kTextPlainFormat[] = "text/plain";
|
static constexpr char kTextPlainFormat[] = "text/plain";
|
||||||
|
|
||||||
@ -43,38 +22,23 @@ static constexpr char kSoundTypeClick[] = "SystemSoundType.click";
|
|||||||
struct _FlPlatformHandler {
|
struct _FlPlatformHandler {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
|
||||||
FlMethodChannel* channel;
|
FlPlatformChannel* channel;
|
||||||
|
|
||||||
FlMethodCall* exit_application_method_call;
|
FlMethodCall* exit_application_method_call;
|
||||||
GCancellable* cancellable;
|
|
||||||
bool app_initialization_complete;
|
bool app_initialization_complete;
|
||||||
|
|
||||||
|
GCancellable* cancellable;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(FlPlatformHandler, fl_platform_handler, G_TYPE_OBJECT)
|
G_DEFINE_TYPE(FlPlatformHandler, fl_platform_handler, G_TYPE_OBJECT)
|
||||||
|
|
||||||
// Sends the method call response to Flutter.
|
|
||||||
static void send_response(FlMethodCall* method_call,
|
|
||||||
FlMethodResponse* response) {
|
|
||||||
g_autoptr(GError) error = nullptr;
|
|
||||||
if (!fl_method_call_respond(method_call, response, &error)) {
|
|
||||||
g_warning("Failed to send method call response: %s", error->message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when clipboard text received.
|
// Called when clipboard text received.
|
||||||
static void clipboard_text_cb(GtkClipboard* clipboard,
|
static void clipboard_text_cb(GtkClipboard* clipboard,
|
||||||
const gchar* text,
|
const gchar* text,
|
||||||
gpointer user_data) {
|
gpointer user_data) {
|
||||||
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
||||||
|
fl_platform_channel_respond_clipboard_get_data(method_call, text);
|
||||||
g_autoptr(FlValue) result = nullptr;
|
|
||||||
if (text != nullptr) {
|
|
||||||
result = fl_value_new_map();
|
|
||||||
fl_value_set_string_take(result, kTextKey, fl_value_new_string(text));
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(FlMethodResponse) response =
|
|
||||||
FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
|
||||||
send_response(method_call, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when clipboard text received during has_strings.
|
// Called when clipboard text received during has_strings.
|
||||||
@ -82,50 +46,25 @@ static void clipboard_text_has_strings_cb(GtkClipboard* clipboard,
|
|||||||
const gchar* text,
|
const gchar* text,
|
||||||
gpointer user_data) {
|
gpointer user_data) {
|
||||||
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
||||||
|
fl_platform_channel_respond_clipboard_has_strings(
|
||||||
g_autoptr(FlValue) result = fl_value_new_map();
|
method_call, text != nullptr && strlen(text) > 0);
|
||||||
fl_value_set_string_take(
|
|
||||||
result, kValueKey,
|
|
||||||
fl_value_new_bool(text != nullptr && strlen(text) > 0));
|
|
||||||
|
|
||||||
g_autoptr(FlMethodResponse) response =
|
|
||||||
FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
|
||||||
send_response(method_call, response);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when Flutter wants to copy to the clipboard.
|
// Called when Flutter wants to copy to the clipboard.
|
||||||
static FlMethodResponse* clipboard_set_data(FlPlatformHandler* self,
|
static FlMethodResponse* clipboard_set_data(FlMethodCall* method_call,
|
||||||
FlValue* args) {
|
const gchar* text,
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
gpointer user_data) {
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
FlValue* text_value = fl_value_lookup_string(args, kTextKey);
|
|
||||||
if (text_value == nullptr ||
|
|
||||||
fl_value_get_type(text_value) != FL_VALUE_TYPE_STRING) {
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Missing clipboard text", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
GtkClipboard* clipboard =
|
GtkClipboard* clipboard =
|
||||||
gtk_clipboard_get_default(gdk_display_get_default());
|
gtk_clipboard_get_default(gdk_display_get_default());
|
||||||
gtk_clipboard_set_text(clipboard, fl_value_get_string(text_value), -1);
|
gtk_clipboard_set_text(clipboard, text, -1);
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when Flutter wants to paste from the clipboard.
|
// Called when Flutter wants to paste from the clipboard.
|
||||||
static FlMethodResponse* clipboard_get_data_async(FlPlatformHandler* self,
|
static FlMethodResponse* clipboard_get_data(FlMethodCall* method_call,
|
||||||
FlMethodCall* method_call) {
|
const gchar* format,
|
||||||
FlValue* args = fl_method_call_get_args(method_call);
|
gpointer user_data) {
|
||||||
|
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) {
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Expected string", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
const gchar* format = fl_value_get_string(args);
|
|
||||||
if (strcmp(format, kTextPlainFormat) != 0) {
|
if (strcmp(format, kTextPlainFormat) != 0) {
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
kUnknownClipboardFormatError, "GTK clipboard API only supports text",
|
kUnknownClipboardFormatError, "GTK clipboard API only supports text",
|
||||||
@ -143,9 +82,8 @@ static FlMethodResponse* clipboard_get_data_async(FlPlatformHandler* self,
|
|||||||
|
|
||||||
// Called when Flutter wants to know if the content of the clipboard is able to
|
// Called when Flutter wants to know if the content of the clipboard is able to
|
||||||
// be pasted, without actually accessing the clipboard content itself.
|
// be pasted, without actually accessing the clipboard content itself.
|
||||||
static FlMethodResponse* clipboard_has_strings_async(
|
static FlMethodResponse* clipboard_has_strings(FlMethodCall* method_call,
|
||||||
FlPlatformHandler* self,
|
gpointer user_data) {
|
||||||
FlMethodCall* method_call) {
|
|
||||||
GtkClipboard* clipboard =
|
GtkClipboard* clipboard =
|
||||||
gtk_clipboard_get_default(gdk_display_get_default());
|
gtk_clipboard_get_default(gdk_display_get_default());
|
||||||
gtk_clipboard_request_text(clipboard, clipboard_text_has_strings_cb,
|
gtk_clipboard_request_text(clipboard, clipboard_text_has_strings_cb,
|
||||||
@ -155,31 +93,6 @@ static FlMethodResponse* clipboard_has_strings_async(
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the exit response from a System.requestAppExit method call.
|
|
||||||
static gchar* get_exit_response(FlMethodResponse* response) {
|
|
||||||
if (response == nullptr) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(GError) error = nullptr;
|
|
||||||
FlValue* result = fl_method_response_get_result(response, &error);
|
|
||||||
if (result == nullptr) {
|
|
||||||
g_warning("Error returned from System.requestAppExit: %s", error->message);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
if (fl_value_get_type(result) != FL_VALUE_TYPE_MAP) {
|
|
||||||
g_warning("System.requestAppExit result argument map missing or malformed");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FlValue* response_value = fl_value_lookup_string(result, kExitResponseKey);
|
|
||||||
if (fl_value_get_type(response_value) != FL_VALUE_TYPE_STRING) {
|
|
||||||
g_warning("Invalid response from System.requestAppExit");
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
return g_strdup(fl_value_get_string(response_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quit this application
|
// Quit this application
|
||||||
static void quit_application() {
|
static void quit_application() {
|
||||||
GApplication* app = g_application_get_default();
|
GApplication* app = g_application_get_default();
|
||||||
@ -212,88 +125,56 @@ static void request_app_exit_response_cb(GObject* object,
|
|||||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
||||||
|
|
||||||
g_autoptr(GError) error = nullptr;
|
g_autoptr(GError) error = nullptr;
|
||||||
g_autoptr(FlMethodResponse) method_response =
|
FlPlatformChannelExitResponse exit_response;
|
||||||
fl_method_channel_invoke_method_finish(FL_METHOD_CHANNEL(object), result,
|
if (!fl_platform_channel_system_request_app_exit_finish(
|
||||||
&error);
|
object, result, &exit_response, &error)) {
|
||||||
g_autofree gchar* exit_response = nullptr;
|
|
||||||
if (method_response == nullptr) {
|
|
||||||
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
g_warning("Failed to complete System.requestAppExit: %s", error->message);
|
g_warning("Failed to complete System.requestAppExit: %s", error->message);
|
||||||
} else {
|
quit_application();
|
||||||
exit_response = get_exit_response(method_response);
|
return;
|
||||||
}
|
|
||||||
// If something went wrong, then just exit.
|
|
||||||
if (exit_response == nullptr) {
|
|
||||||
exit_response = g_strdup(kExitResponseExit);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_str_equal(exit_response, kExitResponseExit)) {
|
if (exit_response == FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT) {
|
||||||
quit_application();
|
quit_application();
|
||||||
} else if (g_str_equal(exit_response, kExitResponseCancel)) {
|
|
||||||
// Canceled - no action to take.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// If request was due to a request from Flutter, pass result back.
|
// If request was due to a request from Flutter, pass result back.
|
||||||
if (self->exit_application_method_call != nullptr) {
|
if (self->exit_application_method_call != nullptr) {
|
||||||
g_autoptr(FlValue) exit_result = fl_value_new_map();
|
fl_platform_channel_respond_system_exit_application(
|
||||||
fl_value_set_string_take(exit_result, kExitResponseKey,
|
self->exit_application_method_call, exit_response);
|
||||||
fl_value_new_string(exit_response));
|
|
||||||
g_autoptr(FlMethodResponse) exit_response =
|
|
||||||
FL_METHOD_RESPONSE(fl_method_success_response_new(exit_result));
|
|
||||||
if (!fl_method_call_respond(self->exit_application_method_call,
|
|
||||||
exit_response, &error)) {
|
|
||||||
g_warning("Failed to send response to System.exitApplication: %s",
|
|
||||||
error->message);
|
|
||||||
}
|
|
||||||
g_clear_object(&self->exit_application_method_call);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send a request to Flutter to exit the application, but only if it's ready for
|
// Send a request to Flutter to exit the application, but only if it's ready for
|
||||||
// a request.
|
// a request.
|
||||||
static void request_app_exit(FlPlatformHandler* self, const char* type) {
|
static void request_app_exit(FlPlatformHandler* self,
|
||||||
g_autoptr(FlValue) args = fl_value_new_map();
|
FlPlatformChannelExitType type) {
|
||||||
if (!self->app_initialization_complete ||
|
if (!self->app_initialization_complete ||
|
||||||
g_str_equal(type, kExitTypeRequired)) {
|
type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
|
||||||
quit_application();
|
quit_application();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
fl_value_set_string_take(args, kExitTypeKey, fl_value_new_string(type));
|
fl_platform_channel_system_request_app_exit(
|
||||||
fl_method_channel_invoke_method(self->channel, kRequestAppExitMethod, args,
|
self->channel, type, self->cancellable, request_app_exit_response_cb,
|
||||||
self->cancellable,
|
self);
|
||||||
request_app_exit_response_cb, self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the Dart app has finished initialization and is ready to handle
|
// Called when the Dart app has finished initialization and is ready to handle
|
||||||
// requests. For the Flutter framework, this means after the ServicesBinding has
|
// requests. For the Flutter framework, this means after the ServicesBinding has
|
||||||
// been initialized and it sends a System.initializationComplete message.
|
// been initialized and it sends a System.initializationComplete message.
|
||||||
static FlMethodResponse* system_intitialization_complete(
|
static void system_initialization_complete(gpointer user_data) {
|
||||||
FlPlatformHandler* self,
|
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
||||||
FlMethodCall* method_call) {
|
|
||||||
self->app_initialization_complete = TRUE;
|
self->app_initialization_complete = TRUE;
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when Flutter wants to exit the application.
|
// Called when Flutter wants to exit the application.
|
||||||
static FlMethodResponse* system_exit_application(FlPlatformHandler* self,
|
static FlMethodResponse* system_exit_application(FlMethodCall* method_call,
|
||||||
FlMethodCall* method_call) {
|
FlPlatformChannelExitType type,
|
||||||
FlValue* args = fl_method_call_get_args(method_call);
|
gpointer user_data) {
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_MAP) {
|
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Argument map missing or malformed", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
FlValue* type_value = fl_value_lookup_string(args, kExitTypeKey);
|
|
||||||
if (type_value == nullptr ||
|
|
||||||
fl_value_get_type(type_value) != FL_VALUE_TYPE_STRING) {
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Missing type argument", nullptr));
|
|
||||||
}
|
|
||||||
const char* type = fl_value_get_string(type_value);
|
|
||||||
|
|
||||||
// Save method call to respond to when our request to Flutter completes.
|
// Save method call to respond to when our request to Flutter completes.
|
||||||
if (self->exit_application_method_call != nullptr) {
|
if (self->exit_application_method_call != nullptr) {
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
@ -305,12 +186,10 @@ static FlMethodResponse* system_exit_application(FlPlatformHandler* self,
|
|||||||
// Requested to immediately quit if the app hasn't yet signaled that it is
|
// Requested to immediately quit if the app hasn't yet signaled that it is
|
||||||
// ready to handle requests, or if the type of exit requested is "required".
|
// ready to handle requests, or if the type of exit requested is "required".
|
||||||
if (!self->app_initialization_complete ||
|
if (!self->app_initialization_complete ||
|
||||||
g_str_equal(type, kExitTypeRequired)) {
|
type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
|
||||||
quit_application();
|
quit_application();
|
||||||
g_autoptr(FlValue) exit_result = fl_value_new_map();
|
return fl_platform_channel_make_system_request_app_exit_response(
|
||||||
fl_value_set_string_take(exit_result, kExitResponseKey,
|
FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT);
|
||||||
fl_value_new_string(kExitResponseExit));
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(exit_result));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the request back to Flutter to follow the standard process.
|
// Send the request back to Flutter to follow the standard process.
|
||||||
@ -321,14 +200,7 @@ static FlMethodResponse* system_exit_application(FlPlatformHandler* self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called when Flutter wants to play a sound.
|
// Called when Flutter wants to play a sound.
|
||||||
static FlMethodResponse* system_sound_play(FlPlatformHandler* self,
|
static void system_sound_play(const gchar* type, gpointer user_data) {
|
||||||
FlValue* args) {
|
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_STRING) {
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
|
||||||
kBadArgumentsError, "Expected string", nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
const gchar* type = fl_value_get_string(args);
|
|
||||||
if (strcmp(type, kSoundTypeAlert) == 0) {
|
if (strcmp(type, kSoundTypeAlert) == 0) {
|
||||||
GdkDisplay* display = gdk_display_get_default();
|
GdkDisplay* display = gdk_display_get_default();
|
||||||
if (display != nullptr) {
|
if (display != nullptr) {
|
||||||
@ -339,47 +211,11 @@ static FlMethodResponse* system_sound_play(FlPlatformHandler* self,
|
|||||||
} else {
|
} else {
|
||||||
g_warning("Ignoring unknown sound type %s in SystemSound.play.\n", type);
|
g_warning("Ignoring unknown sound type %s in SystemSound.play.\n", type);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when Flutter wants to quit the application.
|
// Called when Flutter wants to quit the application.
|
||||||
static FlMethodResponse* system_navigator_pop(FlPlatformHandler* self) {
|
static void system_navigator_pop(gpointer user_data) {
|
||||||
quit_application();
|
quit_application();
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when a method call is received from Flutter.
|
|
||||||
static void method_call_cb(FlMethodChannel* channel,
|
|
||||||
FlMethodCall* method_call,
|
|
||||||
gpointer user_data) {
|
|
||||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
|
||||||
|
|
||||||
const gchar* method = fl_method_call_get_name(method_call);
|
|
||||||
FlValue* args = fl_method_call_get_args(method_call);
|
|
||||||
|
|
||||||
g_autoptr(FlMethodResponse) response = nullptr;
|
|
||||||
if (strcmp(method, kSetClipboardDataMethod) == 0) {
|
|
||||||
response = clipboard_set_data(self, args);
|
|
||||||
} else if (strcmp(method, kGetClipboardDataMethod) == 0) {
|
|
||||||
response = clipboard_get_data_async(self, method_call);
|
|
||||||
} else if (strcmp(method, kClipboardHasStringsMethod) == 0) {
|
|
||||||
response = clipboard_has_strings_async(self, method_call);
|
|
||||||
} else if (strcmp(method, kExitApplicationMethod) == 0) {
|
|
||||||
response = system_exit_application(self, method_call);
|
|
||||||
} else if (strcmp(method, kInitializationCompleteMethod) == 0) {
|
|
||||||
response = system_intitialization_complete(self, method_call);
|
|
||||||
} else if (strcmp(method, kPlaySoundMethod) == 0) {
|
|
||||||
response = system_sound_play(self, args);
|
|
||||||
} else if (strcmp(method, kSystemNavigatorPopMethod) == 0) {
|
|
||||||
response = system_navigator_pop(self);
|
|
||||||
} else {
|
|
||||||
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
|
||||||
}
|
|
||||||
|
|
||||||
if (response != nullptr) {
|
|
||||||
send_response(method_call, response);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fl_platform_handler_dispose(GObject* object) {
|
static void fl_platform_handler_dispose(GObject* object) {
|
||||||
@ -402,17 +238,24 @@ static void fl_platform_handler_init(FlPlatformHandler* self) {
|
|||||||
self->cancellable = g_cancellable_new();
|
self->cancellable = g_cancellable_new();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FlPlatformChannelVTable platform_channel_vtable = {
|
||||||
|
.clipboard_set_data = clipboard_set_data,
|
||||||
|
.clipboard_get_data = clipboard_get_data,
|
||||||
|
.clipboard_has_strings = clipboard_has_strings,
|
||||||
|
.system_exit_application = system_exit_application,
|
||||||
|
.system_initialization_complete = system_initialization_complete,
|
||||||
|
.system_sound_play = system_sound_play,
|
||||||
|
.system_navigator_pop = system_navigator_pop,
|
||||||
|
};
|
||||||
|
|
||||||
FlPlatformHandler* fl_platform_handler_new(FlBinaryMessenger* messenger) {
|
FlPlatformHandler* fl_platform_handler_new(FlBinaryMessenger* messenger) {
|
||||||
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
|
|
||||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(
|
FlPlatformHandler* self = FL_PLATFORM_HANDLER(
|
||||||
g_object_new(fl_platform_handler_get_type(), nullptr));
|
g_object_new(fl_platform_handler_get_type(), nullptr));
|
||||||
|
|
||||||
g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
|
|
||||||
self->channel =
|
self->channel =
|
||||||
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
fl_platform_channel_new(messenger, &platform_channel_vtable, self);
|
||||||
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
|
||||||
nullptr);
|
|
||||||
self->app_initialization_complete = FALSE;
|
self->app_initialization_complete = FALSE;
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
@ -421,5 +264,5 @@ FlPlatformHandler* fl_platform_handler_new(FlBinaryMessenger* messenger) {
|
|||||||
void fl_platform_handler_request_app_exit(FlPlatformHandler* self) {
|
void fl_platform_handler_request_app_exit(FlPlatformHandler* self) {
|
||||||
g_return_if_fail(FL_IS_PLATFORM_HANDLER(self));
|
g_return_if_fail(FL_IS_PLATFORM_HANDLER(self));
|
||||||
// Request a cancellable exit.
|
// Request a cancellable exit.
|
||||||
request_app_exit(self, kExitTypeCancelable);
|
request_app_exit(self, FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE);
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,77 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/fl_settings_channel.h"
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
|
||||||
|
|
||||||
|
static constexpr char kChannelName[] = "flutter/settings";
|
||||||
|
static constexpr char kTextScaleFactorKey[] = "textScaleFactor";
|
||||||
|
static constexpr char kAlwaysUse24HourFormatKey[] = "alwaysUse24HourFormat";
|
||||||
|
static constexpr char kPlatformBrightnessKey[] = "platformBrightness";
|
||||||
|
static constexpr char kPlatformBrightnessLight[] = "light";
|
||||||
|
static constexpr char kPlatformBrightnessDark[] = "dark";
|
||||||
|
|
||||||
|
struct _FlSettingsChannel {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
FlBasicMessageChannel* channel;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlSettingsChannel, fl_settings_channel, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static void fl_settings_channel_dispose(GObject* object) {
|
||||||
|
FlSettingsChannel* self = FL_SETTINGS_CHANNEL(object);
|
||||||
|
|
||||||
|
g_clear_object(&self->channel);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(fl_settings_channel_parent_class)->dispose(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_settings_channel_class_init(FlSettingsChannelClass* klass) {
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = fl_settings_channel_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_settings_channel_init(FlSettingsChannel* self) {}
|
||||||
|
|
||||||
|
FlSettingsChannel* fl_settings_channel_new(FlBinaryMessenger* messenger) {
|
||||||
|
FlSettingsChannel* self = FL_SETTINGS_CHANNEL(
|
||||||
|
g_object_new(fl_settings_channel_get_type(), nullptr));
|
||||||
|
|
||||||
|
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
|
||||||
|
self->channel = fl_basic_message_channel_new(messenger, kChannelName,
|
||||||
|
FL_MESSAGE_CODEC(codec));
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_settings_channel_send(
|
||||||
|
FlSettingsChannel* self,
|
||||||
|
double text_scale_factor,
|
||||||
|
gboolean always_use_24_hour_format,
|
||||||
|
FlSettingsChannelPlatformBrightness platform_brightness) {
|
||||||
|
g_return_if_fail(FL_IS_SETTINGS_CHANNEL(self));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) message = fl_value_new_map();
|
||||||
|
fl_value_set_string_take(message, kTextScaleFactorKey,
|
||||||
|
fl_value_new_float(text_scale_factor));
|
||||||
|
fl_value_set_string_take(message, kAlwaysUse24HourFormatKey,
|
||||||
|
fl_value_new_bool(always_use_24_hour_format));
|
||||||
|
const gchar* platform_brightness_string;
|
||||||
|
switch (platform_brightness) {
|
||||||
|
case FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_LIGHT:
|
||||||
|
platform_brightness_string = kPlatformBrightnessLight;
|
||||||
|
break;
|
||||||
|
case FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_DARK:
|
||||||
|
platform_brightness_string = kPlatformBrightnessDark;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
fl_value_set_string_take(message, kPlatformBrightnessKey,
|
||||||
|
fl_value_new_string(platform_brightness_string));
|
||||||
|
fl_basic_message_channel_send(self->channel, message, nullptr, nullptr,
|
||||||
|
nullptr);
|
||||||
|
}
|
@ -0,0 +1,60 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_CHANNEL_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_CHANNEL_H_
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_LIGHT,
|
||||||
|
FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_DARK
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
} FlSettingsChannelPlatformBrightness;
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlSettingsChannel,
|
||||||
|
fl_settings_channel,
|
||||||
|
FL,
|
||||||
|
SETTINGS_CHANNEL,
|
||||||
|
GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlSettingsChannel:
|
||||||
|
*
|
||||||
|
* #FlSettingsChannel is a channel that implements the Flutter user settings
|
||||||
|
* channel.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_settings_channel_new:
|
||||||
|
* @messenger: an #FlBinaryMessenger
|
||||||
|
*
|
||||||
|
* Creates a new channel that sends user settings to the platform.
|
||||||
|
*
|
||||||
|
* Returns: a new #FlSettingsChannel
|
||||||
|
*/
|
||||||
|
FlSettingsChannel* fl_settings_channel_new(FlBinaryMessenger* messenger);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* fl_settings_channel_send:
|
||||||
|
* @channel: an #FlSettingsChannel.
|
||||||
|
* @text_scale_factor: scale factor for text.
|
||||||
|
* @always_use_24_hour_format: TRUE if time should always be shown in 24 hour
|
||||||
|
* format.
|
||||||
|
* @platform_brightness: The brightness theme in use.
|
||||||
|
*
|
||||||
|
* Send a settings update to the platform.
|
||||||
|
*/
|
||||||
|
void fl_settings_channel_send(
|
||||||
|
FlSettingsChannel* channel,
|
||||||
|
double text_scale_factor,
|
||||||
|
gboolean always_use_24_hour_format,
|
||||||
|
FlSettingsChannelPlatformBrightness platform_brightness);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_SETTINGS_CHANNEL_H_
|
@ -8,35 +8,27 @@
|
|||||||
|
|
||||||
#include "flutter/shell/platform/embedder/embedder.h"
|
#include "flutter/shell/platform/embedder/embedder.h"
|
||||||
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
#include "flutter/shell/platform/linux/fl_engine_private.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_channel.h"
|
#include "flutter/shell/platform/linux/fl_settings_channel.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_message_codec.h"
|
|
||||||
|
|
||||||
static constexpr char kChannelName[] = "flutter/settings";
|
|
||||||
static constexpr char kTextScaleFactorKey[] = "textScaleFactor";
|
|
||||||
static constexpr char kAlwaysUse24HourFormatKey[] = "alwaysUse24HourFormat";
|
|
||||||
static constexpr char kPlatformBrightnessKey[] = "platformBrightness";
|
|
||||||
static constexpr char kPlatformBrightnessLight[] = "light";
|
|
||||||
static constexpr char kPlatformBrightnessDark[] = "dark";
|
|
||||||
|
|
||||||
struct _FlSettingsHandler {
|
struct _FlSettingsHandler {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
|
||||||
FlBasicMessageChannel* channel;
|
FlSettingsChannel* channel;
|
||||||
GWeakRef engine;
|
GWeakRef engine;
|
||||||
FlSettings* settings;
|
FlSettings* settings;
|
||||||
};
|
};
|
||||||
|
|
||||||
G_DEFINE_TYPE(FlSettingsHandler, fl_settings_handler, G_TYPE_OBJECT)
|
G_DEFINE_TYPE(FlSettingsHandler, fl_settings_handler, G_TYPE_OBJECT)
|
||||||
|
|
||||||
static const gchar* to_platform_brightness(FlColorScheme color_scheme) {
|
static FlSettingsChannelPlatformBrightness to_platform_brightness(
|
||||||
|
FlColorScheme color_scheme) {
|
||||||
switch (color_scheme) {
|
switch (color_scheme) {
|
||||||
case FL_COLOR_SCHEME_LIGHT:
|
case FL_COLOR_SCHEME_LIGHT:
|
||||||
return kPlatformBrightnessLight;
|
return FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_LIGHT;
|
||||||
case FL_COLOR_SCHEME_DARK:
|
case FL_COLOR_SCHEME_DARK:
|
||||||
return kPlatformBrightnessDark;
|
return FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_DARK;
|
||||||
default:
|
default:
|
||||||
g_return_val_if_reached(nullptr);
|
g_assert_not_reached();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -46,17 +38,9 @@ static void update_settings(FlSettingsHandler* self) {
|
|||||||
FlColorScheme color_scheme = fl_settings_get_color_scheme(self->settings);
|
FlColorScheme color_scheme = fl_settings_get_color_scheme(self->settings);
|
||||||
gdouble scaling_factor = fl_settings_get_text_scaling_factor(self->settings);
|
gdouble scaling_factor = fl_settings_get_text_scaling_factor(self->settings);
|
||||||
|
|
||||||
g_autoptr(FlValue) message = fl_value_new_map();
|
fl_settings_channel_send(self->channel, scaling_factor,
|
||||||
fl_value_set_string_take(message, kTextScaleFactorKey,
|
clock_format == FL_CLOCK_FORMAT_24H,
|
||||||
fl_value_new_float(scaling_factor));
|
to_platform_brightness(color_scheme));
|
||||||
fl_value_set_string_take(
|
|
||||||
message, kAlwaysUse24HourFormatKey,
|
|
||||||
fl_value_new_bool(clock_format == FL_CLOCK_FORMAT_24H));
|
|
||||||
fl_value_set_string_take(
|
|
||||||
message, kPlatformBrightnessKey,
|
|
||||||
fl_value_new_string(to_platform_brightness(color_scheme)));
|
|
||||||
fl_basic_message_channel_send(self->channel, message, nullptr, nullptr,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
||||||
if (engine != nullptr) {
|
if (engine != nullptr) {
|
||||||
@ -96,9 +80,7 @@ FlSettingsHandler* fl_settings_handler_new(FlEngine* engine) {
|
|||||||
g_weak_ref_init(&self->engine, engine);
|
g_weak_ref_init(&self->engine, engine);
|
||||||
|
|
||||||
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
|
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
|
||||||
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
|
self->channel = fl_settings_channel_new(messenger);
|
||||||
self->channel = fl_basic_message_channel_new(messenger, kChannelName,
|
|
||||||
FL_MESSAGE_CODEC(codec));
|
|
||||||
|
|
||||||
return self;
|
return self;
|
||||||
}
|
}
|
||||||
|
409
engine/src/flutter/shell/platform/linux/fl_text_input_channel.cc
Normal file
409
engine/src/flutter/shell/platform/linux/fl_text_input_channel.cc
Normal file
@ -0,0 +1,409 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/fl_text_input_channel.h"
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
||||||
|
|
||||||
|
static constexpr char kChannelName[] = "flutter/textinput";
|
||||||
|
|
||||||
|
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
||||||
|
|
||||||
|
static constexpr char kSetClientMethod[] = "TextInput.setClient";
|
||||||
|
static constexpr char kShowMethod[] = "TextInput.show";
|
||||||
|
static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
|
||||||
|
static constexpr char kClearClientMethod[] = "TextInput.clearClient";
|
||||||
|
static constexpr char kHideMethod[] = "TextInput.hide";
|
||||||
|
static constexpr char kUpdateEditingStateMethod[] =
|
||||||
|
"TextInputClient.updateEditingState";
|
||||||
|
static constexpr char kUpdateEditingStateWithDeltasMethod[] =
|
||||||
|
"TextInputClient.updateEditingStateWithDeltas";
|
||||||
|
static constexpr char kPerformActionMethod[] = "TextInputClient.performAction";
|
||||||
|
static constexpr char kSetEditableSizeAndTransform[] =
|
||||||
|
"TextInput.setEditableSizeAndTransform";
|
||||||
|
static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect";
|
||||||
|
|
||||||
|
static constexpr char kInputActionKey[] = "inputAction";
|
||||||
|
static constexpr char kTextInputTypeKey[] = "inputType";
|
||||||
|
static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
|
||||||
|
static constexpr char kTextInputTypeNameKey[] = "name";
|
||||||
|
static constexpr char kTextKey[] = "text";
|
||||||
|
static constexpr char kSelectionBaseKey[] = "selectionBase";
|
||||||
|
static constexpr char kSelectionExtentKey[] = "selectionExtent";
|
||||||
|
static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
|
||||||
|
static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
|
||||||
|
static constexpr char kComposingBaseKey[] = "composingBase";
|
||||||
|
static constexpr char kComposingExtentKey[] = "composingExtent";
|
||||||
|
|
||||||
|
static constexpr char kTransform[] = "transform";
|
||||||
|
|
||||||
|
static constexpr char kMultilineInputType[] = "TextInputType.multiline";
|
||||||
|
static constexpr char kNoneInputType[] = "TextInputType.none";
|
||||||
|
|
||||||
|
static constexpr char kTextAffinityUpstream[] = "TextAffinity.upstream";
|
||||||
|
static constexpr char kTextAffinityDownstream[] = "TextAffinity.downstream";
|
||||||
|
|
||||||
|
struct _FlTextInputChannel {
|
||||||
|
GObject parent_instance;
|
||||||
|
|
||||||
|
FlMethodChannel* channel;
|
||||||
|
|
||||||
|
FlTextInputChannelVTable* vtable;
|
||||||
|
|
||||||
|
gpointer user_data;
|
||||||
|
};
|
||||||
|
|
||||||
|
G_DEFINE_TYPE(FlTextInputChannel, fl_text_input_channel, G_TYPE_OBJECT)
|
||||||
|
|
||||||
|
static const gchar* text_affinity_to_string(FlTextAffinity affinity) {
|
||||||
|
switch (affinity) {
|
||||||
|
case FL_TEXT_AFFINITY_UPSTREAM:
|
||||||
|
return kTextAffinityUpstream;
|
||||||
|
case FL_TEXT_AFFINITY_DOWNSTREAM:
|
||||||
|
return kTextAffinityDownstream;
|
||||||
|
default:
|
||||||
|
g_assert_not_reached();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the input method client is set up.
|
||||||
|
static FlMethodResponse* set_client(FlTextInputChannel* self, FlValue* args) {
|
||||||
|
if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
|
||||||
|
fl_value_get_length(args) < 2) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Expected 2-element list", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
int64_t client_id = fl_value_get_int(fl_value_get_list_value(args, 0));
|
||||||
|
FlValue* config_value = fl_value_get_list_value(args, 1);
|
||||||
|
const gchar* input_action = nullptr;
|
||||||
|
FlValue* input_action_value =
|
||||||
|
fl_value_lookup_string(config_value, kInputActionKey);
|
||||||
|
if (fl_value_get_type(input_action_value) == FL_VALUE_TYPE_STRING) {
|
||||||
|
input_action = fl_value_get_string(input_action_value);
|
||||||
|
}
|
||||||
|
|
||||||
|
FlValue* enable_delta_model_value =
|
||||||
|
fl_value_lookup_string(config_value, kEnableDeltaModel);
|
||||||
|
gboolean enable_delta_model = fl_value_get_bool(enable_delta_model_value);
|
||||||
|
|
||||||
|
// Reset the input type, then set only if appropriate.
|
||||||
|
FlTextInputType input_type = FL_TEXT_INPUT_TYPE_TEXT;
|
||||||
|
FlValue* input_type_value =
|
||||||
|
fl_value_lookup_string(config_value, kTextInputTypeKey);
|
||||||
|
if (fl_value_get_type(input_type_value) == FL_VALUE_TYPE_MAP) {
|
||||||
|
FlValue* input_type_name_value =
|
||||||
|
fl_value_lookup_string(input_type_value, kTextInputTypeNameKey);
|
||||||
|
if (fl_value_get_type(input_type_name_value) == FL_VALUE_TYPE_STRING) {
|
||||||
|
const gchar* input_type_name = fl_value_get_string(input_type_name_value);
|
||||||
|
if (g_strcmp0(input_type_name, kMultilineInputType) == 0) {
|
||||||
|
input_type = FL_TEXT_INPUT_TYPE_MULTILINE;
|
||||||
|
} else if (g_strcmp0(input_type_name, kNoneInputType) == 0) {
|
||||||
|
input_type = FL_TEXT_INPUT_TYPE_NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self->vtable->set_client(client_id, input_action, enable_delta_model,
|
||||||
|
input_type, self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hides the input method.
|
||||||
|
static FlMethodResponse* hide(FlTextInputChannel* self) {
|
||||||
|
self->vtable->hide(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Shows the input method.
|
||||||
|
static FlMethodResponse* show(FlTextInputChannel* self) {
|
||||||
|
self->vtable->show(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Updates the editing state from Flutter.
|
||||||
|
static FlMethodResponse* set_editing_state(FlTextInputChannel* self,
|
||||||
|
FlValue* args) {
|
||||||
|
const gchar* text =
|
||||||
|
fl_value_get_string(fl_value_lookup_string(args, kTextKey));
|
||||||
|
int64_t selection_base =
|
||||||
|
fl_value_get_int(fl_value_lookup_string(args, kSelectionBaseKey));
|
||||||
|
int64_t selection_extent =
|
||||||
|
fl_value_get_int(fl_value_lookup_string(args, kSelectionExtentKey));
|
||||||
|
int64_t composing_base =
|
||||||
|
fl_value_get_int(fl_value_lookup_string(args, kComposingBaseKey));
|
||||||
|
int64_t composing_extent =
|
||||||
|
fl_value_get_int(fl_value_lookup_string(args, kComposingExtentKey));
|
||||||
|
|
||||||
|
self->vtable->set_editing_state(text, selection_base, selection_extent,
|
||||||
|
composing_base, composing_extent,
|
||||||
|
self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when the input method client is complete.
|
||||||
|
static FlMethodResponse* clear_client(FlTextInputChannel* self) {
|
||||||
|
self->vtable->clear_client(self->user_data);
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles updates to the EditableText size and position from the framework.
|
||||||
|
//
|
||||||
|
// On changes to the size or position of the RenderObject underlying the
|
||||||
|
// EditableText, this update may be triggered. It provides an updated size and
|
||||||
|
// transform from the local coordinate system of the EditableText to root
|
||||||
|
// Flutter coordinate system.
|
||||||
|
static FlMethodResponse* set_editable_size_and_transform(
|
||||||
|
FlTextInputChannel* self,
|
||||||
|
FlValue* args) {
|
||||||
|
FlValue* transform_value = fl_value_lookup_string(args, kTransform);
|
||||||
|
if (fl_value_get_length(transform_value) != 16) {
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||||
|
kBadArgumentsError, "Invalid transform", nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
double transform[16];
|
||||||
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
transform[i] =
|
||||||
|
fl_value_get_float(fl_value_get_list_value(transform_value, i));
|
||||||
|
}
|
||||||
|
self->vtable->set_editable_size_and_transform(transform, self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handles updates to the composing rect from the framework.
|
||||||
|
//
|
||||||
|
// On changes to the state of the EditableText in the framework, this update
|
||||||
|
// may be triggered. It provides an updated rect for the composing region in
|
||||||
|
// local coordinates of the EditableText. In the case where there is no
|
||||||
|
// composing region, the cursor rect is sent.
|
||||||
|
static FlMethodResponse* set_marked_text_rect(FlTextInputChannel* self,
|
||||||
|
FlValue* args) {
|
||||||
|
double x = fl_value_get_float(fl_value_lookup_string(args, "x"));
|
||||||
|
double y = fl_value_get_float(fl_value_lookup_string(args, "y"));
|
||||||
|
double width = fl_value_get_float(fl_value_lookup_string(args, "width"));
|
||||||
|
double height = fl_value_get_float(fl_value_lookup_string(args, "height"));
|
||||||
|
|
||||||
|
self->vtable->set_marked_text_rect(x, y, width, height, self->user_data);
|
||||||
|
|
||||||
|
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a method call is received from Flutter.
|
||||||
|
static void method_call_cb(FlMethodChannel* channel,
|
||||||
|
FlMethodCall* method_call,
|
||||||
|
gpointer user_data) {
|
||||||
|
FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(user_data);
|
||||||
|
|
||||||
|
const gchar* method = fl_method_call_get_name(method_call);
|
||||||
|
FlValue* args = fl_method_call_get_args(method_call);
|
||||||
|
|
||||||
|
g_autoptr(FlMethodResponse) response = nullptr;
|
||||||
|
if (strcmp(method, kSetClientMethod) == 0) {
|
||||||
|
response = set_client(self, args);
|
||||||
|
} else if (strcmp(method, kShowMethod) == 0) {
|
||||||
|
response = show(self);
|
||||||
|
} else if (strcmp(method, kSetEditingStateMethod) == 0) {
|
||||||
|
response = set_editing_state(self, args);
|
||||||
|
} else if (strcmp(method, kClearClientMethod) == 0) {
|
||||||
|
response = clear_client(self);
|
||||||
|
} else if (strcmp(method, kHideMethod) == 0) {
|
||||||
|
response = hide(self);
|
||||||
|
} else if (strcmp(method, kSetEditableSizeAndTransform) == 0) {
|
||||||
|
response = set_editable_size_and_transform(self, args);
|
||||||
|
} else if (strcmp(method, kSetMarkedTextRect) == 0) {
|
||||||
|
response = set_marked_text_rect(self, args);
|
||||||
|
} else {
|
||||||
|
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
||||||
|
}
|
||||||
|
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_method_call_respond(method_call, response, &error)) {
|
||||||
|
g_warning("Failed to send method call response: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_text_input_channel_dispose(GObject* object) {
|
||||||
|
FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(object);
|
||||||
|
|
||||||
|
g_clear_object(&self->channel);
|
||||||
|
|
||||||
|
G_OBJECT_CLASS(fl_text_input_channel_parent_class)->dispose(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_text_input_channel_class_init(FlTextInputChannelClass* klass) {
|
||||||
|
G_OBJECT_CLASS(klass)->dispose = fl_text_input_channel_dispose;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void fl_text_input_channel_init(FlTextInputChannel* self) {}
|
||||||
|
|
||||||
|
FlTextInputChannel* fl_text_input_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlTextInputChannelVTable* vtable,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||||
|
g_return_val_if_fail(vtable != nullptr, nullptr);
|
||||||
|
|
||||||
|
FlTextInputChannel* self = FL_TEXT_INPUT_CHANNEL(
|
||||||
|
g_object_new(fl_text_input_channel_get_type(), nullptr));
|
||||||
|
|
||||||
|
self->vtable = vtable;
|
||||||
|
self->user_data = user_data;
|
||||||
|
|
||||||
|
g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
|
||||||
|
self->channel =
|
||||||
|
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
||||||
|
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_text_input_channel_update_editing_state(
|
||||||
|
FlTextInputChannel* self,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* text,
|
||||||
|
int64_t selection_base,
|
||||||
|
int64_t selection_extent,
|
||||||
|
FlTextAffinity selection_affinity,
|
||||||
|
gboolean selection_is_directional,
|
||||||
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) args = fl_value_new_list();
|
||||||
|
fl_value_append_take(args, fl_value_new_int(client_id));
|
||||||
|
g_autoptr(FlValue) value = fl_value_new_map();
|
||||||
|
|
||||||
|
fl_value_set_string_take(value, kTextKey, fl_value_new_string(text));
|
||||||
|
fl_value_set_string_take(value, kSelectionBaseKey,
|
||||||
|
fl_value_new_int(selection_base));
|
||||||
|
fl_value_set_string_take(value, kSelectionExtentKey,
|
||||||
|
fl_value_new_int(selection_extent));
|
||||||
|
fl_value_set_string_take(
|
||||||
|
value, kSelectionAffinityKey,
|
||||||
|
fl_value_new_string(text_affinity_to_string(selection_affinity)));
|
||||||
|
fl_value_set_string_take(value, kSelectionIsDirectionalKey,
|
||||||
|
fl_value_new_bool(selection_is_directional));
|
||||||
|
fl_value_set_string_take(value, kComposingBaseKey,
|
||||||
|
fl_value_new_int(composing_base));
|
||||||
|
fl_value_set_string_take(value, kComposingExtentKey,
|
||||||
|
fl_value_new_int(composing_extent));
|
||||||
|
|
||||||
|
fl_value_append(args, value);
|
||||||
|
|
||||||
|
fl_method_channel_invoke_method(self->channel, kUpdateEditingStateMethod,
|
||||||
|
args, cancellable, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean fl_text_input_channel_update_editing_state_finish(GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error) {
|
||||||
|
g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
|
||||||
|
FL_METHOD_CHANNEL(object), result, error);
|
||||||
|
if (response == nullptr) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return fl_method_response_get_result(response, error) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_text_input_channel_update_editing_state_with_deltas(
|
||||||
|
FlTextInputChannel* self,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* old_text,
|
||||||
|
const gchar* delta_text,
|
||||||
|
int64_t delta_start,
|
||||||
|
int64_t delta_end,
|
||||||
|
int64_t selection_base,
|
||||||
|
int64_t selection_extent,
|
||||||
|
FlTextAffinity selection_affinity,
|
||||||
|
gboolean selection_is_directional,
|
||||||
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) args = fl_value_new_list();
|
||||||
|
fl_value_append_take(args, fl_value_new_int(client_id));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) deltaValue = fl_value_new_map();
|
||||||
|
fl_value_set_string_take(deltaValue, "oldText",
|
||||||
|
fl_value_new_string(old_text));
|
||||||
|
fl_value_set_string_take(deltaValue, "deltaText",
|
||||||
|
fl_value_new_string(delta_text));
|
||||||
|
fl_value_set_string_take(deltaValue, "deltaStart",
|
||||||
|
fl_value_new_int(delta_start));
|
||||||
|
fl_value_set_string_take(deltaValue, "deltaEnd", fl_value_new_int(delta_end));
|
||||||
|
fl_value_set_string_take(deltaValue, "selectionBase",
|
||||||
|
fl_value_new_int(selection_base));
|
||||||
|
fl_value_set_string_take(deltaValue, "selectionExtent",
|
||||||
|
fl_value_new_int(selection_extent));
|
||||||
|
fl_value_set_string_take(
|
||||||
|
deltaValue, "selectionAffinity",
|
||||||
|
fl_value_new_string(text_affinity_to_string(selection_affinity)));
|
||||||
|
fl_value_set_string_take(deltaValue, "selectionIsDirectional",
|
||||||
|
fl_value_new_bool(selection_is_directional));
|
||||||
|
fl_value_set_string_take(deltaValue, "composingBase",
|
||||||
|
fl_value_new_int(composing_base));
|
||||||
|
fl_value_set_string_take(deltaValue, "composingExtent",
|
||||||
|
fl_value_new_int(composing_extent));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) deltas = fl_value_new_list();
|
||||||
|
fl_value_append(deltas, deltaValue);
|
||||||
|
g_autoptr(FlValue) value = fl_value_new_map();
|
||||||
|
fl_value_set_string(value, "deltas", deltas);
|
||||||
|
|
||||||
|
fl_value_append(args, value);
|
||||||
|
|
||||||
|
fl_method_channel_invoke_method(self->channel,
|
||||||
|
kUpdateEditingStateWithDeltasMethod, args,
|
||||||
|
cancellable, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean fl_text_input_channel_update_editing_state_with_deltas_finish(
|
||||||
|
GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error) {
|
||||||
|
g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
|
||||||
|
FL_METHOD_CHANNEL(object), result, error);
|
||||||
|
if (response == nullptr) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return fl_method_response_get_result(response, error) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void fl_text_input_channel_perform_action(FlTextInputChannel* self,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* input_action,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_return_if_fail(FL_IS_TEXT_INPUT_CHANNEL(self));
|
||||||
|
|
||||||
|
g_autoptr(FlValue) args = fl_value_new_list();
|
||||||
|
fl_value_append_take(args, fl_value_new_int(client_id));
|
||||||
|
fl_value_append_take(args, fl_value_new_string(input_action));
|
||||||
|
|
||||||
|
fl_method_channel_invoke_method(self->channel, kPerformActionMethod, args,
|
||||||
|
cancellable, callback, user_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
gboolean fl_text_input_channel_perform_action_finish(GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error) {
|
||||||
|
g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
|
||||||
|
FL_METHOD_CHANNEL(object), result, error);
|
||||||
|
if (response == nullptr) {
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
return fl_method_response_get_result(response, error) != nullptr;
|
||||||
|
}
|
215
engine/src/flutter/shell/platform/linux/fl_text_input_channel.h
Normal file
215
engine/src/flutter/shell/platform/linux/fl_text_input_channel.h
Normal file
@ -0,0 +1,215 @@
|
|||||||
|
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
#ifndef FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_CHANNEL_H_
|
||||||
|
#define FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_CHANNEL_H_
|
||||||
|
|
||||||
|
#include <gtk/gtk.h>
|
||||||
|
|
||||||
|
#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h"
|
||||||
|
|
||||||
|
G_BEGIN_DECLS
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
FL_TEXT_INPUT_TYPE_TEXT,
|
||||||
|
// Send newline when multi-line and enter is pressed.
|
||||||
|
FL_TEXT_INPUT_TYPE_MULTILINE,
|
||||||
|
// The input method is not shown at all.
|
||||||
|
FL_TEXT_INPUT_TYPE_NONE,
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
} FlTextInputType;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
// NOLINTBEGIN(readability-identifier-naming)
|
||||||
|
FL_TEXT_AFFINITY_UPSTREAM,
|
||||||
|
FL_TEXT_AFFINITY_DOWNSTREAM,
|
||||||
|
// NOLINTEND(readability-identifier-naming)
|
||||||
|
} FlTextAffinity;
|
||||||
|
|
||||||
|
G_DECLARE_FINAL_TYPE(FlTextInputChannel,
|
||||||
|
fl_text_input_channel,
|
||||||
|
FL,
|
||||||
|
TEXT_INPUT_CHANNEL,
|
||||||
|
GObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* FlTextInputChannel:
|
||||||
|
*
|
||||||
|
* #FlTextInputChannel is a channel that implements the shell side
|
||||||
|
* of SystemChannels.textInput from the Flutter services library.
|
||||||
|
*/
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
void (*set_client)(int64_t client_id,
|
||||||
|
const gchar* input_action,
|
||||||
|
gboolean enable_delta_model,
|
||||||
|
FlTextInputType input_type,
|
||||||
|
gpointer user_data);
|
||||||
|
void (*hide)(gpointer user_data);
|
||||||
|
void (*show)(gpointer user_data);
|
||||||
|
void (*set_editing_state)(const gchar* text,
|
||||||
|
int64_t selection_base,
|
||||||
|
int64_t selection_extent,
|
||||||
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
gpointer user_data);
|
||||||
|
void (*clear_client)(gpointer user_data);
|
||||||
|
void (*set_editable_size_and_transform)(double* transform,
|
||||||
|
gpointer user_data);
|
||||||
|
void (*set_marked_text_rect)(double x,
|
||||||
|
double y,
|
||||||
|
double width,
|
||||||
|
double height,
|
||||||
|
gpointer user_data);
|
||||||
|
} FlTextInputChannelVTable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_new:
|
||||||
|
* @messenger: an #FlBinaryMessenger.
|
||||||
|
* @vtable: callbacks for incoming method calls.
|
||||||
|
* @user_data: data to pass in callbacks.
|
||||||
|
*
|
||||||
|
* Creates a new channel that implements SystemChannels.textInput from the
|
||||||
|
* Flutter services library.
|
||||||
|
*
|
||||||
|
* Returns: a new #FlTextInputChannel.
|
||||||
|
*/
|
||||||
|
FlTextInputChannel* fl_text_input_channel_new(FlBinaryMessenger* messenger,
|
||||||
|
FlTextInputChannelVTable* vtable,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_update_editing_state:
|
||||||
|
* @channel: an #FlTextInputChannel.
|
||||||
|
* @client_id:
|
||||||
|
* @text:
|
||||||
|
* @selection_base:
|
||||||
|
* @selection_extent:
|
||||||
|
* @selection_affinity:
|
||||||
|
* @selection_is_directional:
|
||||||
|
* @composing_base:
|
||||||
|
* @composing_extent:
|
||||||
|
* @cancellable: (allow-none): a #GCancellable or %NULL.
|
||||||
|
* @callback: (scope async): a #GAsyncReadyCallback to call when the method
|
||||||
|
* returns.
|
||||||
|
* @user_data: (closure): user data to pass to @callback.
|
||||||
|
*/
|
||||||
|
void fl_text_input_channel_update_editing_state(
|
||||||
|
FlTextInputChannel* channel,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* text,
|
||||||
|
int64_t selection_base,
|
||||||
|
int64_t selection_extent,
|
||||||
|
FlTextAffinity selection_affinity,
|
||||||
|
gboolean selection_is_directional,
|
||||||
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_update_editing_state_finish:
|
||||||
|
* @object:
|
||||||
|
* @result: a #GAsyncResult.
|
||||||
|
* @error: (allow-none): #GError location to store the error occurring, or %NULL
|
||||||
|
* to ignore.
|
||||||
|
*
|
||||||
|
* Completes request started with fl_text_input_channel_update_editing_state().
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success.
|
||||||
|
*/
|
||||||
|
gboolean fl_text_input_channel_update_editing_state_finish(GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_update_editing_state_with_deltas:
|
||||||
|
* @channel: an #FlTextInputChannel.
|
||||||
|
* @client_id:
|
||||||
|
* @old_text:
|
||||||
|
* @delta_text:
|
||||||
|
* @delta_start:
|
||||||
|
* @delta_end:
|
||||||
|
* @selection_base:
|
||||||
|
* @selection_extent:
|
||||||
|
* @selection_affinity:
|
||||||
|
* @selection_is_directional:
|
||||||
|
* @composing_base:
|
||||||
|
* @composing_extent:
|
||||||
|
* @cancellable: (allow-none): a #GCancellable or %NULL.
|
||||||
|
* @callback: (scope async): a #GAsyncReadyCallback to call when the method
|
||||||
|
* returns.
|
||||||
|
* @user_data: (closure): user data to pass to @callback.
|
||||||
|
*/
|
||||||
|
void fl_text_input_channel_update_editing_state_with_deltas(
|
||||||
|
FlTextInputChannel* channel,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* old_text,
|
||||||
|
const gchar* delta_text,
|
||||||
|
int64_t delta_start,
|
||||||
|
int64_t delta_end,
|
||||||
|
int64_t selection_base,
|
||||||
|
int64_t selection_extent,
|
||||||
|
FlTextAffinity selection_affinity,
|
||||||
|
gboolean selection_is_directional,
|
||||||
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_update_editing_state_with_deltas_finish:
|
||||||
|
* @object:
|
||||||
|
* @result: a #GAsyncResult.
|
||||||
|
* @error: (allow-none): #GError location to store the error occurring, or %NULL
|
||||||
|
* to ignore.
|
||||||
|
*
|
||||||
|
* Completes request started with
|
||||||
|
* fl_text_input_channel_update_editing_state_with_deltas().
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success.
|
||||||
|
*/
|
||||||
|
gboolean fl_text_input_channel_update_editing_state_with_deltas_finish(
|
||||||
|
GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_perform_action:
|
||||||
|
* @channel: an #FlTextInputChannel.
|
||||||
|
* @client_id:
|
||||||
|
* @input_action: action to perform.
|
||||||
|
* @cancellable: (allow-none): a #GCancellable or %NULL.
|
||||||
|
* @callback: (scope async): a #GAsyncReadyCallback to call when the method
|
||||||
|
* returns.
|
||||||
|
* @user_data: (closure): user data to pass to @callback.
|
||||||
|
*/
|
||||||
|
void fl_text_input_channel_perform_action(FlTextInputChannel* channel,
|
||||||
|
int64_t client_id,
|
||||||
|
const gchar* input_action,
|
||||||
|
GCancellable* cancellable,
|
||||||
|
GAsyncReadyCallback callback,
|
||||||
|
gpointer user_data);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* fl_text_input_channel_perform_action_finish:
|
||||||
|
* @object:
|
||||||
|
* @result: a #GAsyncResult.
|
||||||
|
* @error: (allow-none): #GError location to store the error occurring, or %NULL
|
||||||
|
* to ignore.
|
||||||
|
*
|
||||||
|
* Completes request started with fl_text_input_channel_perform_action().
|
||||||
|
*
|
||||||
|
* Returns: %TRUE on success.
|
||||||
|
*/
|
||||||
|
gboolean fl_text_input_channel_perform_action_finish(GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
GError** error);
|
||||||
|
|
||||||
|
G_END_DECLS
|
||||||
|
|
||||||
|
#endif // FLUTTER_SHELL_PLATFORM_LINUX_FL_TEXT_INPUT_CHANNEL_H_
|
@ -8,61 +8,16 @@
|
|||||||
|
|
||||||
#include "flutter/shell/platform/common/text_editing_delta.h"
|
#include "flutter/shell/platform/common/text_editing_delta.h"
|
||||||
#include "flutter/shell/platform/common/text_input_model.h"
|
#include "flutter/shell/platform/common/text_input_model.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_json_method_codec.h"
|
#include "flutter/shell/platform/linux/fl_text_input_channel.h"
|
||||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_method_channel.h"
|
|
||||||
|
|
||||||
static constexpr char kChannelName[] = "flutter/textinput";
|
|
||||||
|
|
||||||
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
|
||||||
|
|
||||||
static constexpr char kSetClientMethod[] = "TextInput.setClient";
|
|
||||||
static constexpr char kShowMethod[] = "TextInput.show";
|
|
||||||
static constexpr char kSetEditingStateMethod[] = "TextInput.setEditingState";
|
|
||||||
static constexpr char kClearClientMethod[] = "TextInput.clearClient";
|
|
||||||
static constexpr char kHideMethod[] = "TextInput.hide";
|
|
||||||
static constexpr char kUpdateEditingStateMethod[] =
|
|
||||||
"TextInputClient.updateEditingState";
|
|
||||||
static constexpr char kUpdateEditingStateWithDeltasMethod[] =
|
|
||||||
"TextInputClient.updateEditingStateWithDeltas";
|
|
||||||
static constexpr char kPerformActionMethod[] = "TextInputClient.performAction";
|
|
||||||
static constexpr char kSetEditableSizeAndTransform[] =
|
|
||||||
"TextInput.setEditableSizeAndTransform";
|
|
||||||
static constexpr char kSetMarkedTextRect[] = "TextInput.setMarkedTextRect";
|
|
||||||
|
|
||||||
static constexpr char kInputActionKey[] = "inputAction";
|
|
||||||
static constexpr char kTextInputTypeKey[] = "inputType";
|
|
||||||
static constexpr char kEnableDeltaModel[] = "enableDeltaModel";
|
|
||||||
static constexpr char kTextInputTypeNameKey[] = "name";
|
|
||||||
static constexpr char kTextKey[] = "text";
|
|
||||||
static constexpr char kSelectionBaseKey[] = "selectionBase";
|
|
||||||
static constexpr char kSelectionExtentKey[] = "selectionExtent";
|
|
||||||
static constexpr char kSelectionAffinityKey[] = "selectionAffinity";
|
|
||||||
static constexpr char kSelectionIsDirectionalKey[] = "selectionIsDirectional";
|
|
||||||
static constexpr char kComposingBaseKey[] = "composingBase";
|
|
||||||
static constexpr char kComposingExtentKey[] = "composingExtent";
|
|
||||||
|
|
||||||
static constexpr char kTransform[] = "transform";
|
|
||||||
|
|
||||||
static constexpr char kTextAffinityDownstream[] = "TextAffinity.downstream";
|
|
||||||
static constexpr char kMultilineInputType[] = "TextInputType.multiline";
|
|
||||||
static constexpr char kNoneInputType[] = "TextInputType.none";
|
|
||||||
|
|
||||||
static constexpr char kNewlineInputAction[] = "TextInputAction.newline";
|
static constexpr char kNewlineInputAction[] = "TextInputAction.newline";
|
||||||
|
|
||||||
static constexpr int64_t kClientIdUnset = -1;
|
static constexpr int64_t kClientIdUnset = -1;
|
||||||
|
|
||||||
typedef enum {
|
|
||||||
kFlTextInputTypeText,
|
|
||||||
// Send newline when multi-line and enter is pressed.
|
|
||||||
kFlTextInputTypeMultiline,
|
|
||||||
// The input method is not shown at all.
|
|
||||||
kFlTextInputTypeNone,
|
|
||||||
} FlTextInputType;
|
|
||||||
|
|
||||||
struct _FlTextInputHandler {
|
struct _FlTextInputHandler {
|
||||||
GObject parent_instance;
|
GObject parent_instance;
|
||||||
|
|
||||||
FlMethodChannel* channel;
|
FlTextInputChannel* channel;
|
||||||
|
|
||||||
// Client ID provided by Flutter to report events with.
|
// Client ID provided by Flutter to report events with.
|
||||||
int64_t client_id;
|
int64_t client_id;
|
||||||
@ -101,26 +56,29 @@ struct _FlTextInputHandler {
|
|||||||
|
|
||||||
G_DEFINE_TYPE(FlTextInputHandler, fl_text_input_handler, G_TYPE_OBJECT)
|
G_DEFINE_TYPE(FlTextInputHandler, fl_text_input_handler, G_TYPE_OBJECT)
|
||||||
|
|
||||||
// Completes method call and returns TRUE if the call was successful.
|
|
||||||
static gboolean finish_method(GObject* object,
|
|
||||||
GAsyncResult* result,
|
|
||||||
GError** error) {
|
|
||||||
g_autoptr(FlMethodResponse) response = fl_method_channel_invoke_method_finish(
|
|
||||||
FL_METHOD_CHANNEL(object), result, error);
|
|
||||||
if (response == nullptr) {
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
return fl_method_response_get_result(response, error) != nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when a response is received from TextInputClient.updateEditingState()
|
// Called when a response is received from TextInputClient.updateEditingState()
|
||||||
static void update_editing_state_response_cb(GObject* object,
|
static void update_editing_state_response_cb(GObject* object,
|
||||||
GAsyncResult* result,
|
GAsyncResult* result,
|
||||||
gpointer user_data) {
|
gpointer user_data) {
|
||||||
g_autoptr(GError) error = nullptr;
|
g_autoptr(GError) error = nullptr;
|
||||||
if (!finish_method(object, result, &error)) {
|
if (!fl_text_input_channel_update_editing_state_finish(object, result,
|
||||||
|
&error)) {
|
||||||
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||||
g_warning("Failed to call %s: %s", kUpdateEditingStateMethod,
|
g_warning("Failed to update editing state: %s", error->message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Called when a response is received from
|
||||||
|
// TextInputClient.updateEditingStateWithDeltas()
|
||||||
|
static void update_editing_state_with_deltas_response_cb(GObject* object,
|
||||||
|
GAsyncResult* result,
|
||||||
|
gpointer user_data) {
|
||||||
|
g_autoptr(GError) error = nullptr;
|
||||||
|
if (!fl_text_input_channel_update_editing_state_with_deltas_finish(
|
||||||
|
object, result, &error)) {
|
||||||
|
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||||
|
g_warning("Failed to update editing state with deltas: %s",
|
||||||
error->message);
|
error->message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -128,96 +86,36 @@ static void update_editing_state_response_cb(GObject* object,
|
|||||||
|
|
||||||
// Informs Flutter of text input changes.
|
// Informs Flutter of text input changes.
|
||||||
static void update_editing_state(FlTextInputHandler* self) {
|
static void update_editing_state(FlTextInputHandler* self) {
|
||||||
g_autoptr(FlValue) args = fl_value_new_list();
|
|
||||||
fl_value_append_take(args, fl_value_new_int(self->client_id));
|
|
||||||
g_autoptr(FlValue) value = fl_value_new_map();
|
|
||||||
|
|
||||||
flutter::TextRange selection = self->text_model->selection();
|
|
||||||
fl_value_set_string_take(
|
|
||||||
value, kTextKey,
|
|
||||||
fl_value_new_string(self->text_model->GetText().c_str()));
|
|
||||||
fl_value_set_string_take(value, kSelectionBaseKey,
|
|
||||||
fl_value_new_int(selection.base()));
|
|
||||||
fl_value_set_string_take(value, kSelectionExtentKey,
|
|
||||||
fl_value_new_int(selection.extent()));
|
|
||||||
|
|
||||||
int composing_base = -1;
|
int composing_base = -1;
|
||||||
int composing_extent = -1;
|
int composing_extent = -1;
|
||||||
if (!self->text_model->composing_range().collapsed()) {
|
if (!self->text_model->composing_range().collapsed()) {
|
||||||
composing_base = self->text_model->composing_range().base();
|
composing_base = self->text_model->composing_range().base();
|
||||||
composing_extent = self->text_model->composing_range().extent();
|
composing_extent = self->text_model->composing_range().extent();
|
||||||
}
|
}
|
||||||
fl_value_set_string_take(value, kComposingBaseKey,
|
flutter::TextRange selection = self->text_model->selection();
|
||||||
fl_value_new_int(composing_base));
|
fl_text_input_channel_update_editing_state(
|
||||||
fl_value_set_string_take(value, kComposingExtentKey,
|
self->channel, self->client_id, self->text_model->GetText().c_str(),
|
||||||
fl_value_new_int(composing_extent));
|
selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
|
||||||
|
composing_base, composing_extent, self->cancellable,
|
||||||
// The following keys are not implemented and set to default values.
|
update_editing_state_response_cb, self);
|
||||||
fl_value_set_string_take(value, kSelectionAffinityKey,
|
|
||||||
fl_value_new_string(kTextAffinityDownstream));
|
|
||||||
fl_value_set_string_take(value, kSelectionIsDirectionalKey,
|
|
||||||
fl_value_new_bool(FALSE));
|
|
||||||
|
|
||||||
fl_value_append(args, value);
|
|
||||||
|
|
||||||
fl_method_channel_invoke_method(self->channel, kUpdateEditingStateMethod,
|
|
||||||
args, self->cancellable,
|
|
||||||
update_editing_state_response_cb, self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Informs Flutter of text input changes by passing just the delta.
|
// Informs Flutter of text input changes by passing just the delta.
|
||||||
static void update_editing_state_with_delta(FlTextInputHandler* self,
|
static void update_editing_state_with_delta(FlTextInputHandler* self,
|
||||||
flutter::TextEditingDelta* delta) {
|
flutter::TextEditingDelta* delta) {
|
||||||
g_autoptr(FlValue) args = fl_value_new_list();
|
|
||||||
fl_value_append_take(args, fl_value_new_int(self->client_id));
|
|
||||||
|
|
||||||
g_autoptr(FlValue) deltaValue = fl_value_new_map();
|
|
||||||
fl_value_set_string_take(deltaValue, "oldText",
|
|
||||||
fl_value_new_string(delta->old_text().c_str()));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "deltaText",
|
|
||||||
fl_value_new_string(delta->delta_text().c_str()));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "deltaStart",
|
|
||||||
fl_value_new_int(delta->delta_start()));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "deltaEnd",
|
|
||||||
fl_value_new_int(delta->delta_end()));
|
|
||||||
|
|
||||||
flutter::TextRange selection = self->text_model->selection();
|
flutter::TextRange selection = self->text_model->selection();
|
||||||
fl_value_set_string_take(deltaValue, "selectionBase",
|
|
||||||
fl_value_new_int(selection.base()));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "selectionExtent",
|
|
||||||
fl_value_new_int(selection.extent()));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "selectionAffinity",
|
|
||||||
fl_value_new_string(kTextAffinityDownstream));
|
|
||||||
|
|
||||||
fl_value_set_string_take(deltaValue, "selectionIsDirectional",
|
|
||||||
fl_value_new_bool(FALSE));
|
|
||||||
|
|
||||||
int composing_base = -1;
|
int composing_base = -1;
|
||||||
int composing_extent = -1;
|
int composing_extent = -1;
|
||||||
if (!self->text_model->composing_range().collapsed()) {
|
if (!self->text_model->composing_range().collapsed()) {
|
||||||
composing_base = self->text_model->composing_range().base();
|
composing_base = self->text_model->composing_range().base();
|
||||||
composing_extent = self->text_model->composing_range().extent();
|
composing_extent = self->text_model->composing_range().extent();
|
||||||
}
|
}
|
||||||
fl_value_set_string_take(deltaValue, "composingBase",
|
fl_text_input_channel_update_editing_state_with_deltas(
|
||||||
fl_value_new_int(composing_base));
|
self->channel, self->client_id, delta->old_text().c_str(),
|
||||||
fl_value_set_string_take(deltaValue, "composingExtent",
|
delta->delta_text().c_str(), delta->delta_start(), delta->delta_end(),
|
||||||
fl_value_new_int(composing_extent));
|
selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
|
||||||
|
composing_base, composing_extent, self->cancellable,
|
||||||
g_autoptr(FlValue) deltas = fl_value_new_list();
|
update_editing_state_with_deltas_response_cb, self);
|
||||||
fl_value_append(deltas, deltaValue);
|
|
||||||
g_autoptr(FlValue) value = fl_value_new_map();
|
|
||||||
fl_value_set_string(value, "deltas", deltas);
|
|
||||||
|
|
||||||
fl_value_append(args, value);
|
|
||||||
|
|
||||||
fl_method_channel_invoke_method(
|
|
||||||
self->channel, kUpdateEditingStateWithDeltasMethod, args,
|
|
||||||
self->cancellable, update_editing_state_response_cb, self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when a response is received from TextInputClient.performAction()
|
// Called when a response is received from TextInputClient.performAction()
|
||||||
@ -225,9 +123,9 @@ static void perform_action_response_cb(GObject* object,
|
|||||||
GAsyncResult* result,
|
GAsyncResult* result,
|
||||||
gpointer user_data) {
|
gpointer user_data) {
|
||||||
g_autoptr(GError) error = nullptr;
|
g_autoptr(GError) error = nullptr;
|
||||||
if (!finish_method(object, result, &error)) {
|
if (!fl_text_input_channel_perform_action_finish(object, result, &error)) {
|
||||||
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
if (!g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||||
g_warning("Failed to call %s: %s", kPerformActionMethod, error->message);
|
g_warning("Failed to perform action: %s", error->message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -238,13 +136,9 @@ static void perform_action(FlTextInputHandler* self) {
|
|||||||
g_return_if_fail(self->client_id != 0);
|
g_return_if_fail(self->client_id != 0);
|
||||||
g_return_if_fail(self->input_action != nullptr);
|
g_return_if_fail(self->input_action != nullptr);
|
||||||
|
|
||||||
g_autoptr(FlValue) args = fl_value_new_list();
|
fl_text_input_channel_perform_action(self->channel, self->client_id,
|
||||||
fl_value_append_take(args, fl_value_new_int(self->client_id));
|
self->input_action, self->cancellable,
|
||||||
fl_value_append_take(args, fl_value_new_string(self->input_action));
|
perform_action_response_cb, self);
|
||||||
|
|
||||||
fl_method_channel_invoke_method(self->channel, kPerformActionMethod, args,
|
|
||||||
self->cancellable, perform_action_response_cb,
|
|
||||||
self);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal handler for GtkIMContext::preedit-start
|
// Signal handler for GtkIMContext::preedit-start
|
||||||
@ -344,76 +238,50 @@ static gboolean im_delete_surrounding_cb(FlTextInputHandler* self,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Called when the input method client is set up.
|
// Called when the input method client is set up.
|
||||||
static FlMethodResponse* set_client(FlTextInputHandler* self, FlValue* args) {
|
static void set_client(int64_t client_id,
|
||||||
if (fl_value_get_type(args) != FL_VALUE_TYPE_LIST ||
|
const gchar* input_action,
|
||||||
fl_value_get_length(args) < 2) {
|
gboolean enable_delta_model,
|
||||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
FlTextInputType input_type,
|
||||||
kBadArgumentsError, "Expected 2-element list", nullptr));
|
gpointer user_data) {
|
||||||
}
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
|
|
||||||
self->client_id = fl_value_get_int(fl_value_get_list_value(args, 0));
|
self->client_id = client_id;
|
||||||
FlValue* config_value = fl_value_get_list_value(args, 1);
|
|
||||||
g_free(self->input_action);
|
g_free(self->input_action);
|
||||||
FlValue* input_action_value =
|
self->input_action = g_strdup(input_action);
|
||||||
fl_value_lookup_string(config_value, kInputActionKey);
|
|
||||||
if (fl_value_get_type(input_action_value) == FL_VALUE_TYPE_STRING) {
|
|
||||||
self->input_action = g_strdup(fl_value_get_string(input_action_value));
|
|
||||||
}
|
|
||||||
|
|
||||||
FlValue* enable_delta_model_value =
|
|
||||||
fl_value_lookup_string(config_value, kEnableDeltaModel);
|
|
||||||
gboolean enable_delta_model = fl_value_get_bool(enable_delta_model_value);
|
|
||||||
self->enable_delta_model = enable_delta_model;
|
self->enable_delta_model = enable_delta_model;
|
||||||
|
self->input_type = input_type;
|
||||||
// Reset the input type, then set only if appropriate.
|
|
||||||
self->input_type = kFlTextInputTypeText;
|
|
||||||
FlValue* input_type_value =
|
|
||||||
fl_value_lookup_string(config_value, kTextInputTypeKey);
|
|
||||||
if (fl_value_get_type(input_type_value) == FL_VALUE_TYPE_MAP) {
|
|
||||||
FlValue* input_type_name =
|
|
||||||
fl_value_lookup_string(input_type_value, kTextInputTypeNameKey);
|
|
||||||
if (fl_value_get_type(input_type_name) == FL_VALUE_TYPE_STRING) {
|
|
||||||
const gchar* input_type = fl_value_get_string(input_type_name);
|
|
||||||
if (g_strcmp0(input_type, kMultilineInputType) == 0) {
|
|
||||||
self->input_type = kFlTextInputTypeMultiline;
|
|
||||||
} else if (g_strcmp0(input_type, kNoneInputType) == 0) {
|
|
||||||
self->input_type = kFlTextInputTypeNone;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hides the input method.
|
// Hides the input method.
|
||||||
static FlMethodResponse* hide(FlTextInputHandler* self) {
|
static void hide(gpointer user_data) {
|
||||||
gtk_im_context_focus_out(self->im_context);
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
gtk_im_context_focus_out(self->im_context);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Shows the input method.
|
// Shows the input method.
|
||||||
static FlMethodResponse* show(FlTextInputHandler* self) {
|
static void show(gpointer user_data) {
|
||||||
if (self->input_type == kFlTextInputTypeNone) {
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
return hide(self);
|
|
||||||
|
if (self->input_type == FL_TEXT_INPUT_TYPE_NONE) {
|
||||||
|
hide(user_data);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_im_context_focus_in(self->im_context);
|
gtk_im_context_focus_in(self->im_context);
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates the editing state from Flutter.
|
// Updates the editing state from Flutter.
|
||||||
static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
static void set_editing_state(const gchar* text,
|
||||||
FlValue* args) {
|
int64_t selection_base,
|
||||||
const gchar* text =
|
int64_t selection_extent,
|
||||||
fl_value_get_string(fl_value_lookup_string(args, kTextKey));
|
int64_t composing_base,
|
||||||
|
int64_t composing_extent,
|
||||||
|
gpointer user_data) {
|
||||||
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
|
|
||||||
self->text_model->SetText(text);
|
self->text_model->SetText(text);
|
||||||
|
|
||||||
int64_t selection_base =
|
|
||||||
fl_value_get_int(fl_value_lookup_string(args, kSelectionBaseKey));
|
|
||||||
int64_t selection_extent =
|
|
||||||
fl_value_get_int(fl_value_lookup_string(args, kSelectionExtentKey));
|
|
||||||
// Flutter uses -1/-1 for invalid; translate that to 0/0 for the model.
|
// Flutter uses -1/-1 for invalid; translate that to 0/0 for the model.
|
||||||
if (selection_base == -1 && selection_extent == -1) {
|
if (selection_base == -1 && selection_extent == -1) {
|
||||||
selection_base = selection_extent = 0;
|
selection_base = selection_extent = 0;
|
||||||
@ -423,10 +291,6 @@ static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
|||||||
self->text_model->SetSelection(
|
self->text_model->SetSelection(
|
||||||
flutter::TextRange(selection_base, selection_extent));
|
flutter::TextRange(selection_base, selection_extent));
|
||||||
|
|
||||||
int64_t composing_base =
|
|
||||||
fl_value_get_int(fl_value_lookup_string(args, kComposingBaseKey));
|
|
||||||
int64_t composing_extent =
|
|
||||||
fl_value_get_int(fl_value_lookup_string(args, kComposingExtentKey));
|
|
||||||
if (composing_base == -1 && composing_extent == -1) {
|
if (composing_base == -1 && composing_extent == -1) {
|
||||||
self->text_model->EndComposing();
|
self->text_model->EndComposing();
|
||||||
} else {
|
} else {
|
||||||
@ -435,25 +299,22 @@ static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
|||||||
self->text_model->SetComposingRange(
|
self->text_model->SetComposingRange(
|
||||||
flutter::TextRange(composing_base, composing_extent), cursor_offset);
|
flutter::TextRange(composing_base, composing_extent), cursor_offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Called when the input method client is complete.
|
// Called when the input method client is complete.
|
||||||
static FlMethodResponse* clear_client(FlTextInputHandler* self) {
|
static void clear_client(gpointer user_data) {
|
||||||
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
self->client_id = kClientIdUnset;
|
self->client_id = kClientIdUnset;
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the IM cursor position.
|
// Update the IM cursor position.
|
||||||
//
|
//
|
||||||
// As text is input by the user, the framework sends two streams of updates
|
// As text is input by the user, the framework sends two streams of updates
|
||||||
// over the text input channel: updates to the composing rect (cursor rect when
|
// over the text input channel: updates to the composing rect (cursor rect
|
||||||
// not in IME composing mode) and updates to the matrix transform from local
|
// when not in IME composing mode) and updates to the matrix transform from
|
||||||
// coordinates to Flutter root coordinates. This function is called after each
|
// local coordinates to Flutter root coordinates. This function is called
|
||||||
// of these updates. It transforms the composing rect to GDK window coordinates
|
// after each of these updates. It transforms the composing rect to GDK window
|
||||||
// and notifies GTK of the updated cursor position.
|
// coordinates and notifies GTK of the updated cursor position.
|
||||||
static void update_im_cursor_position(FlTextInputHandler* self) {
|
static void update_im_cursor_position(FlTextInputHandler* self) {
|
||||||
g_autoptr(FlTextInputViewDelegate) view_delegate =
|
g_autoptr(FlTextInputViewDelegate) view_delegate =
|
||||||
FL_TEXT_INPUT_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
|
FL_TEXT_INPUT_VIEW_DELEGATE(g_weak_ref_get(&self->view_delegate));
|
||||||
@ -480,8 +341,8 @@ static void update_im_cursor_position(FlTextInputHandler* self) {
|
|||||||
fl_text_input_view_delegate_translate_coordinates(
|
fl_text_input_view_delegate_translate_coordinates(
|
||||||
view_delegate, x, y, &preedit_rect.x, &preedit_rect.y);
|
view_delegate, x, y, &preedit_rect.x, &preedit_rect.y);
|
||||||
|
|
||||||
// Set the cursor location in window coordinates so that GTK can position any
|
// Set the cursor location in window coordinates so that GTK can position
|
||||||
// system input method windows.
|
// any system input method windows.
|
||||||
gtk_im_context_set_cursor_location(self->im_context, &preedit_rect);
|
gtk_im_context_set_cursor_location(self->im_context, &preedit_rect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,20 +352,14 @@ static void update_im_cursor_position(FlTextInputHandler* self) {
|
|||||||
// EditableText, this update may be triggered. It provides an updated size and
|
// EditableText, this update may be triggered. It provides an updated size and
|
||||||
// transform from the local coordinate system of the EditableText to root
|
// transform from the local coordinate system of the EditableText to root
|
||||||
// Flutter coordinate system.
|
// Flutter coordinate system.
|
||||||
static FlMethodResponse* set_editable_size_and_transform(
|
static void set_editable_size_and_transform(double* transform,
|
||||||
FlTextInputHandler* self,
|
gpointer user_data) {
|
||||||
FlValue* args) {
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
FlValue* transform = fl_value_lookup_string(args, kTransform);
|
|
||||||
size_t transform_len = fl_value_get_length(transform);
|
|
||||||
g_warn_if_fail(transform_len == 16);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < transform_len; ++i) {
|
for (size_t i = 0; i < 16; i++) {
|
||||||
double val = fl_value_get_float(fl_value_get_list_value(transform, i));
|
self->editabletext_transform[i / 4][i % 4] = transform[i];
|
||||||
self->editabletext_transform[i / 4][i % 4] = val;
|
|
||||||
}
|
}
|
||||||
update_im_cursor_position(self);
|
update_im_cursor_position(self);
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handles updates to the composing rect from the framework.
|
// Handles updates to the composing rect from the framework.
|
||||||
@ -513,53 +368,18 @@ static FlMethodResponse* set_editable_size_and_transform(
|
|||||||
// may be triggered. It provides an updated rect for the composing region in
|
// may be triggered. It provides an updated rect for the composing region in
|
||||||
// local coordinates of the EditableText. In the case where there is no
|
// local coordinates of the EditableText. In the case where there is no
|
||||||
// composing region, the cursor rect is sent.
|
// composing region, the cursor rect is sent.
|
||||||
static FlMethodResponse* set_marked_text_rect(FlTextInputHandler* self,
|
static void set_marked_text_rect(double x,
|
||||||
FlValue* args) {
|
double y,
|
||||||
self->composing_rect.x =
|
double width,
|
||||||
fl_value_get_float(fl_value_lookup_string(args, "x"));
|
double height,
|
||||||
self->composing_rect.y =
|
gpointer user_data) {
|
||||||
fl_value_get_float(fl_value_lookup_string(args, "y"));
|
|
||||||
self->composing_rect.width =
|
|
||||||
fl_value_get_float(fl_value_lookup_string(args, "width"));
|
|
||||||
self->composing_rect.height =
|
|
||||||
fl_value_get_float(fl_value_lookup_string(args, "height"));
|
|
||||||
update_im_cursor_position(self);
|
|
||||||
|
|
||||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Called when a method call is received from Flutter.
|
|
||||||
static void method_call_cb(FlMethodChannel* channel,
|
|
||||||
FlMethodCall* method_call,
|
|
||||||
gpointer user_data) {
|
|
||||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||||
|
|
||||||
const gchar* method = fl_method_call_get_name(method_call);
|
self->composing_rect.x = x;
|
||||||
FlValue* args = fl_method_call_get_args(method_call);
|
self->composing_rect.y = y;
|
||||||
|
self->composing_rect.width = width;
|
||||||
g_autoptr(FlMethodResponse) response = nullptr;
|
self->composing_rect.height = height;
|
||||||
if (strcmp(method, kSetClientMethod) == 0) {
|
update_im_cursor_position(self);
|
||||||
response = set_client(self, args);
|
|
||||||
} else if (strcmp(method, kShowMethod) == 0) {
|
|
||||||
response = show(self);
|
|
||||||
} else if (strcmp(method, kSetEditingStateMethod) == 0) {
|
|
||||||
response = set_editing_state(self, args);
|
|
||||||
} else if (strcmp(method, kClearClientMethod) == 0) {
|
|
||||||
response = clear_client(self);
|
|
||||||
} else if (strcmp(method, kHideMethod) == 0) {
|
|
||||||
response = hide(self);
|
|
||||||
} else if (strcmp(method, kSetEditableSizeAndTransform) == 0) {
|
|
||||||
response = set_editable_size_and_transform(self, args);
|
|
||||||
} else if (strcmp(method, kSetMarkedTextRect) == 0) {
|
|
||||||
response = set_marked_text_rect(self, args);
|
|
||||||
} else {
|
|
||||||
response = FL_METHOD_RESPONSE(fl_method_not_implemented_response_new());
|
|
||||||
}
|
|
||||||
|
|
||||||
g_autoptr(GError) error = nullptr;
|
|
||||||
if (!fl_method_call_respond(method_call, response, &error)) {
|
|
||||||
g_warning("Failed to send method call response: %s", error->message);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disposes of an FlTextInputHandler.
|
// Disposes of an FlTextInputHandler.
|
||||||
@ -589,7 +409,7 @@ static void fl_text_input_handler_class_init(FlTextInputHandlerClass* klass) {
|
|||||||
// Initializes an instance of the FlTextInputHandler class.
|
// Initializes an instance of the FlTextInputHandler class.
|
||||||
static void fl_text_input_handler_init(FlTextInputHandler* self) {
|
static void fl_text_input_handler_init(FlTextInputHandler* self) {
|
||||||
self->client_id = kClientIdUnset;
|
self->client_id = kClientIdUnset;
|
||||||
self->input_type = kFlTextInputTypeText;
|
self->input_type = FL_TEXT_INPUT_TYPE_TEXT;
|
||||||
self->text_model = new flutter::TextInputModel();
|
self->text_model = new flutter::TextInputModel();
|
||||||
self->cancellable = g_cancellable_new();
|
self->cancellable = g_cancellable_new();
|
||||||
}
|
}
|
||||||
@ -622,6 +442,16 @@ static void init_im_context(FlTextInputHandler* self,
|
|||||||
G_CONNECT_SWAPPED);
|
G_CONNECT_SWAPPED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static FlTextInputChannelVTable text_input_vtable = {
|
||||||
|
.set_client = set_client,
|
||||||
|
.hide = hide,
|
||||||
|
.show = show,
|
||||||
|
.set_editing_state = set_editing_state,
|
||||||
|
.clear_client = clear_client,
|
||||||
|
.set_editable_size_and_transform = set_editable_size_and_transform,
|
||||||
|
.set_marked_text_rect = set_marked_text_rect,
|
||||||
|
};
|
||||||
|
|
||||||
FlTextInputHandler* fl_text_input_handler_new(
|
FlTextInputHandler* fl_text_input_handler_new(
|
||||||
FlBinaryMessenger* messenger,
|
FlBinaryMessenger* messenger,
|
||||||
GtkIMContext* im_context,
|
GtkIMContext* im_context,
|
||||||
@ -633,11 +463,8 @@ FlTextInputHandler* fl_text_input_handler_new(
|
|||||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(
|
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(
|
||||||
g_object_new(fl_text_input_handler_get_type(), nullptr));
|
g_object_new(fl_text_input_handler_get_type(), nullptr));
|
||||||
|
|
||||||
g_autoptr(FlJsonMethodCodec) codec = fl_json_method_codec_new();
|
|
||||||
self->channel =
|
self->channel =
|
||||||
fl_method_channel_new(messenger, kChannelName, FL_METHOD_CODEC(codec));
|
fl_text_input_channel_new(messenger, &text_input_vtable, self);
|
||||||
fl_method_channel_set_method_call_handler(self->channel, method_call_cb, self,
|
|
||||||
nullptr);
|
|
||||||
|
|
||||||
init_im_context(self, im_context);
|
init_im_context(self, im_context);
|
||||||
|
|
||||||
@ -681,7 +508,7 @@ gboolean fl_text_input_handler_filter_keypress(FlTextInputHandler* self,
|
|||||||
case GDK_KEY_Return:
|
case GDK_KEY_Return:
|
||||||
case GDK_KEY_KP_Enter:
|
case GDK_KEY_KP_Enter:
|
||||||
case GDK_KEY_ISO_Enter:
|
case GDK_KEY_ISO_Enter:
|
||||||
if (self->input_type == kFlTextInputTypeMultiline &&
|
if (self->input_type == FL_TEXT_INPUT_TYPE_MULTILINE &&
|
||||||
strcmp(self->input_action, kNewlineInputAction) == 0) {
|
strcmp(self->input_action, kNewlineInputAction) == 0) {
|
||||||
self->text_model->AddCodePoint('\n');
|
self->text_model->AddCodePoint('\n');
|
||||||
text = "\n";
|
text = "\n";
|
||||||
|
Loading…
x
Reference in New Issue
Block a user