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_event.cc + ../../../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.h + ../../../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_response.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.h + ../../../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_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.h + ../../../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_settings.cc + ../../../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.h + ../../../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_task_runner.cc + ../../../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.h + ../../../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_event.cc
|
||||
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.h
|
||||
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_response.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.h
|
||||
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_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.h
|
||||
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_settings.cc
|
||||
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.h
|
||||
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_task_runner.cc
|
||||
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.h
|
||||
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_embedder_responder.cc",
|
||||
"fl_key_event.cc",
|
||||
"fl_keyboard_channel.cc",
|
||||
"fl_keyboard_handler.cc",
|
||||
"fl_keyboard_layout.cc",
|
||||
"fl_keyboard_manager.cc",
|
||||
@ -126,8 +127,10 @@ source_set("flutter_linux_sources") {
|
||||
"fl_method_channel.cc",
|
||||
"fl_method_codec.cc",
|
||||
"fl_method_response.cc",
|
||||
"fl_mouse_cursor_channel.cc",
|
||||
"fl_mouse_cursor_handler.cc",
|
||||
"fl_pixel_buffer_texture.cc",
|
||||
"fl_platform_channel.cc",
|
||||
"fl_platform_handler.cc",
|
||||
"fl_plugin_registrar.cc",
|
||||
"fl_plugin_registry.cc",
|
||||
@ -138,6 +141,7 @@ source_set("flutter_linux_sources") {
|
||||
"fl_renderer_headless.cc",
|
||||
"fl_scrolling_manager.cc",
|
||||
"fl_settings.cc",
|
||||
"fl_settings_channel.cc",
|
||||
"fl_settings_handler.cc",
|
||||
"fl_settings_portal.cc",
|
||||
"fl_socket_accessible.cc",
|
||||
@ -146,6 +150,7 @@ source_set("flutter_linux_sources") {
|
||||
"fl_string_codec.cc",
|
||||
"fl_task_runner.cc",
|
||||
"fl_task_runner.h",
|
||||
"fl_text_input_channel.cc",
|
||||
"fl_text_input_handler.cc",
|
||||
"fl_text_input_view_delegate.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/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";
|
||||
#include "flutter/shell/platform/linux/fl_keyboard_channel.h"
|
||||
|
||||
struct _FlKeyboardHandler {
|
||||
GObject parent_instance;
|
||||
@ -16,14 +12,16 @@ struct _FlKeyboardHandler {
|
||||
FlKeyboardManager* keyboard_manager;
|
||||
|
||||
// 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);
|
||||
|
||||
// Returns the keyboard pressed state.
|
||||
static FlMethodResponse* get_keyboard_state(FlKeyboardHandler* self) {
|
||||
g_autoptr(FlValue) result = fl_value_new_map();
|
||||
static FlValue* get_keyboard_state(gpointer user_data) {
|
||||
FlKeyboardHandler* self = FL_KEYBOARD_HANDLER(user_data);
|
||||
|
||||
FlValue* result = fl_value_new_map();
|
||||
|
||||
GHashTable* pressing_records =
|
||||
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));
|
||||
},
|
||||
result);
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(result));
|
||||
}
|
||||
|
||||
// Called when a method call on flutter/keyboard is received from Flutter.
|
||||
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);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
static FlKeyboardChannelVTable keyboard_channel_vtable = {
|
||||
.get_keyboard_state = get_keyboard_state};
|
||||
|
||||
static void fl_keyboard_handler_init(FlKeyboardHandler* self) {}
|
||||
|
||||
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));
|
||||
|
||||
// Setup the flutter/keyboard channel.
|
||||
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_handler,
|
||||
self, nullptr);
|
||||
fl_keyboard_channel_new(messenger, &keyboard_channel_vtable, 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 "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";
|
||||
#include "flutter/shell/platform/linux/fl_mouse_cursor_channel.h"
|
||||
|
||||
static constexpr char kFallbackCursor[] = "default";
|
||||
|
||||
struct _FlMouseCursorHandler {
|
||||
GObject parent_instance;
|
||||
|
||||
FlMethodChannel* channel;
|
||||
FlMouseCursorChannel* channel;
|
||||
|
||||
GHashTable* system_cursor_table;
|
||||
|
||||
@ -89,18 +83,8 @@ static void populate_system_cursor_table(GHashTable* table) {
|
||||
}
|
||||
|
||||
// Sets the mouse cursor.
|
||||
FlMethodResponse* activate_system_cursor(FlMouseCursorHandler* 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);
|
||||
}
|
||||
static void activate_system_cursor(const gchar* kind, gpointer user_data) {
|
||||
FlMouseCursorHandler* self = FL_MOUSE_CURSOR_HANDLER(user_data);
|
||||
|
||||
if (self->system_cursor_table == nullptr) {
|
||||
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);
|
||||
|
||||
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) {
|
||||
@ -166,6 +126,10 @@ static void fl_mouse_cursor_handler_init(FlMouseCursorHandler* self) {
|
||||
self->cursor_name = g_strdup("");
|
||||
}
|
||||
|
||||
static FlMouseCursorChannelVTable mouse_cursor_vtable = {
|
||||
.activate_system_cursor = activate_system_cursor,
|
||||
};
|
||||
|
||||
FlMouseCursorHandler* fl_mouse_cursor_handler_new(
|
||||
FlBinaryMessenger* messenger) {
|
||||
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(
|
||||
g_object_new(fl_mouse_cursor_handler_get_type(), nullptr));
|
||||
|
||||
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);
|
||||
fl_mouse_cursor_channel_new(messenger, &mouse_cursor_vtable, 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 <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"
|
||||
|
||||
static constexpr char kChannelName[] = "flutter/platform";
|
||||
static constexpr char kBadArgumentsError[] = "Bad Arguments";
|
||||
static constexpr char kInProgressError[] = "In Progress";
|
||||
static constexpr char kUnknownClipboardFormatError[] =
|
||||
"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";
|
||||
|
||||
@ -43,38 +22,23 @@ static constexpr char kSoundTypeClick[] = "SystemSoundType.click";
|
||||
struct _FlPlatformHandler {
|
||||
GObject parent_instance;
|
||||
|
||||
FlMethodChannel* channel;
|
||||
FlPlatformChannel* channel;
|
||||
|
||||
FlMethodCall* exit_application_method_call;
|
||||
GCancellable* cancellable;
|
||||
|
||||
bool app_initialization_complete;
|
||||
|
||||
GCancellable* cancellable;
|
||||
};
|
||||
|
||||
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.
|
||||
static void clipboard_text_cb(GtkClipboard* clipboard,
|
||||
const gchar* text,
|
||||
gpointer user_data) {
|
||||
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
||||
|
||||
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);
|
||||
fl_platform_channel_respond_clipboard_get_data(method_call, text);
|
||||
}
|
||||
|
||||
// Called when clipboard text received during has_strings.
|
||||
@ -82,50 +46,25 @@ static void clipboard_text_has_strings_cb(GtkClipboard* clipboard,
|
||||
const gchar* text,
|
||||
gpointer user_data) {
|
||||
g_autoptr(FlMethodCall) method_call = FL_METHOD_CALL(user_data);
|
||||
|
||||
g_autoptr(FlValue) result = fl_value_new_map();
|
||||
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);
|
||||
fl_platform_channel_respond_clipboard_has_strings(
|
||||
method_call, text != nullptr && strlen(text) > 0);
|
||||
}
|
||||
|
||||
// Called when Flutter wants to copy to the clipboard.
|
||||
static FlMethodResponse* clipboard_set_data(FlPlatformHandler* 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* 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));
|
||||
}
|
||||
|
||||
static FlMethodResponse* clipboard_set_data(FlMethodCall* method_call,
|
||||
const gchar* text,
|
||||
gpointer user_data) {
|
||||
GtkClipboard* clipboard =
|
||||
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));
|
||||
}
|
||||
|
||||
// Called when Flutter wants to paste from the clipboard.
|
||||
static FlMethodResponse* clipboard_get_data_async(FlPlatformHandler* 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);
|
||||
static FlMethodResponse* clipboard_get_data(FlMethodCall* method_call,
|
||||
const gchar* format,
|
||||
gpointer user_data) {
|
||||
if (strcmp(format, kTextPlainFormat) != 0) {
|
||||
return FL_METHOD_RESPONSE(fl_method_error_response_new(
|
||||
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
|
||||
// be pasted, without actually accessing the clipboard content itself.
|
||||
static FlMethodResponse* clipboard_has_strings_async(
|
||||
FlPlatformHandler* self,
|
||||
FlMethodCall* method_call) {
|
||||
static FlMethodResponse* clipboard_has_strings(FlMethodCall* method_call,
|
||||
gpointer user_data) {
|
||||
GtkClipboard* clipboard =
|
||||
gtk_clipboard_get_default(gdk_display_get_default());
|
||||
gtk_clipboard_request_text(clipboard, clipboard_text_has_strings_cb,
|
||||
@ -155,31 +93,6 @@ static FlMethodResponse* clipboard_has_strings_async(
|
||||
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
|
||||
static void quit_application() {
|
||||
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);
|
||||
|
||||
g_autoptr(GError) error = nullptr;
|
||||
g_autoptr(FlMethodResponse) method_response =
|
||||
fl_method_channel_invoke_method_finish(FL_METHOD_CHANNEL(object), result,
|
||||
&error);
|
||||
g_autofree gchar* exit_response = nullptr;
|
||||
if (method_response == nullptr) {
|
||||
FlPlatformChannelExitResponse exit_response;
|
||||
if (!fl_platform_channel_system_request_app_exit_finish(
|
||||
object, result, &exit_response, &error)) {
|
||||
if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
|
||||
return;
|
||||
}
|
||||
g_warning("Failed to complete System.requestAppExit: %s", error->message);
|
||||
} else {
|
||||
exit_response = get_exit_response(method_response);
|
||||
}
|
||||
// If something went wrong, then just exit.
|
||||
if (exit_response == nullptr) {
|
||||
exit_response = g_strdup(kExitResponseExit);
|
||||
quit_application();
|
||||
return;
|
||||
}
|
||||
|
||||
if (g_str_equal(exit_response, kExitResponseExit)) {
|
||||
if (exit_response == FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT) {
|
||||
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 (self->exit_application_method_call != nullptr) {
|
||||
g_autoptr(FlValue) exit_result = fl_value_new_map();
|
||||
fl_value_set_string_take(exit_result, kExitResponseKey,
|
||||
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);
|
||||
fl_platform_channel_respond_system_exit_application(
|
||||
self->exit_application_method_call, exit_response);
|
||||
}
|
||||
}
|
||||
|
||||
// Send a request to Flutter to exit the application, but only if it's ready for
|
||||
// a request.
|
||||
static void request_app_exit(FlPlatformHandler* self, const char* type) {
|
||||
g_autoptr(FlValue) args = fl_value_new_map();
|
||||
static void request_app_exit(FlPlatformHandler* self,
|
||||
FlPlatformChannelExitType type) {
|
||||
if (!self->app_initialization_complete ||
|
||||
g_str_equal(type, kExitTypeRequired)) {
|
||||
type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
|
||||
quit_application();
|
||||
return;
|
||||
}
|
||||
|
||||
fl_value_set_string_take(args, kExitTypeKey, fl_value_new_string(type));
|
||||
fl_method_channel_invoke_method(self->channel, kRequestAppExitMethod, args,
|
||||
self->cancellable,
|
||||
request_app_exit_response_cb, self);
|
||||
fl_platform_channel_system_request_app_exit(
|
||||
self->channel, type, self->cancellable, request_app_exit_response_cb,
|
||||
self);
|
||||
}
|
||||
|
||||
// Called when the Dart app has finished initialization and is ready to handle
|
||||
// requests. For the Flutter framework, this means after the ServicesBinding has
|
||||
// been initialized and it sends a System.initializationComplete message.
|
||||
static FlMethodResponse* system_intitialization_complete(
|
||||
FlPlatformHandler* self,
|
||||
FlMethodCall* method_call) {
|
||||
static void system_initialization_complete(gpointer user_data) {
|
||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
||||
self->app_initialization_complete = TRUE;
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
||||
|
||||
// Called when Flutter wants to exit the application.
|
||||
static FlMethodResponse* system_exit_application(FlPlatformHandler* 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 = fl_value_get_string(type_value);
|
||||
|
||||
static FlMethodResponse* system_exit_application(FlMethodCall* method_call,
|
||||
FlPlatformChannelExitType type,
|
||||
gpointer user_data) {
|
||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(user_data);
|
||||
// Save method call to respond to when our request to Flutter completes.
|
||||
if (self->exit_application_method_call != nullptr) {
|
||||
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
|
||||
// ready to handle requests, or if the type of exit requested is "required".
|
||||
if (!self->app_initialization_complete ||
|
||||
g_str_equal(type, kExitTypeRequired)) {
|
||||
type == FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED) {
|
||||
quit_application();
|
||||
g_autoptr(FlValue) exit_result = fl_value_new_map();
|
||||
fl_value_set_string_take(exit_result, kExitResponseKey,
|
||||
fl_value_new_string(kExitResponseExit));
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(exit_result));
|
||||
return fl_platform_channel_make_system_request_app_exit_response(
|
||||
FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static FlMethodResponse* system_sound_play(FlPlatformHandler* 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);
|
||||
static void system_sound_play(const gchar* type, gpointer user_data) {
|
||||
if (strcmp(type, kSoundTypeAlert) == 0) {
|
||||
GdkDisplay* display = gdk_display_get_default();
|
||||
if (display != nullptr) {
|
||||
@ -339,47 +211,11 @@ static FlMethodResponse* system_sound_play(FlPlatformHandler* self,
|
||||
} else {
|
||||
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.
|
||||
static FlMethodResponse* system_navigator_pop(FlPlatformHandler* self) {
|
||||
static void system_navigator_pop(gpointer user_data) {
|
||||
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) {
|
||||
@ -402,17 +238,24 @@ static void fl_platform_handler_init(FlPlatformHandler* self) {
|
||||
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) {
|
||||
g_return_val_if_fail(FL_IS_BINARY_MESSENGER(messenger), nullptr);
|
||||
|
||||
FlPlatformHandler* self = FL_PLATFORM_HANDLER(
|
||||
g_object_new(fl_platform_handler_get_type(), nullptr));
|
||||
|
||||
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);
|
||||
fl_platform_channel_new(messenger, &platform_channel_vtable, self);
|
||||
self->app_initialization_complete = FALSE;
|
||||
|
||||
return self;
|
||||
@ -421,5 +264,5 @@ FlPlatformHandler* fl_platform_handler_new(FlBinaryMessenger* messenger) {
|
||||
void fl_platform_handler_request_app_exit(FlPlatformHandler* self) {
|
||||
g_return_if_fail(FL_IS_PLATFORM_HANDLER(self));
|
||||
// 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/linux/fl_engine_private.h"
|
||||
#include "flutter/shell/platform/linux/public/flutter_linux/fl_basic_message_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";
|
||||
#include "flutter/shell/platform/linux/fl_settings_channel.h"
|
||||
|
||||
struct _FlSettingsHandler {
|
||||
GObject parent_instance;
|
||||
|
||||
FlBasicMessageChannel* channel;
|
||||
FlSettingsChannel* channel;
|
||||
GWeakRef engine;
|
||||
FlSettings* settings;
|
||||
};
|
||||
|
||||
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) {
|
||||
case FL_COLOR_SCHEME_LIGHT:
|
||||
return kPlatformBrightnessLight;
|
||||
return FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_LIGHT;
|
||||
case FL_COLOR_SCHEME_DARK:
|
||||
return kPlatformBrightnessDark;
|
||||
return FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_DARK;
|
||||
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);
|
||||
gdouble scaling_factor = fl_settings_get_text_scaling_factor(self->settings);
|
||||
|
||||
g_autoptr(FlValue) message = fl_value_new_map();
|
||||
fl_value_set_string_take(message, kTextScaleFactorKey,
|
||||
fl_value_new_float(scaling_factor));
|
||||
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);
|
||||
fl_settings_channel_send(self->channel, scaling_factor,
|
||||
clock_format == FL_CLOCK_FORMAT_24H,
|
||||
to_platform_brightness(color_scheme));
|
||||
|
||||
g_autoptr(FlEngine) engine = FL_ENGINE(g_weak_ref_get(&self->engine));
|
||||
if (engine != nullptr) {
|
||||
@ -96,9 +80,7 @@ FlSettingsHandler* fl_settings_handler_new(FlEngine* engine) {
|
||||
g_weak_ref_init(&self->engine, engine);
|
||||
|
||||
FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);
|
||||
g_autoptr(FlJsonMessageCodec) codec = fl_json_message_codec_new();
|
||||
self->channel = fl_basic_message_channel_new(messenger, kChannelName,
|
||||
FL_MESSAGE_CODEC(codec));
|
||||
self->channel = fl_settings_channel_new(messenger);
|
||||
|
||||
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_input_model.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 kTextAffinityDownstream[] = "TextAffinity.downstream";
|
||||
static constexpr char kMultilineInputType[] = "TextInputType.multiline";
|
||||
static constexpr char kNoneInputType[] = "TextInputType.none";
|
||||
#include "flutter/shell/platform/linux/fl_text_input_channel.h"
|
||||
|
||||
static constexpr char kNewlineInputAction[] = "TextInputAction.newline";
|
||||
|
||||
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 {
|
||||
GObject parent_instance;
|
||||
|
||||
FlMethodChannel* channel;
|
||||
FlTextInputChannel* channel;
|
||||
|
||||
// Client ID provided by Flutter to report events with.
|
||||
int64_t client_id;
|
||||
@ -101,26 +56,29 @@ struct _FlTextInputHandler {
|
||||
|
||||
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()
|
||||
static void update_editing_state_response_cb(GObject* object,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
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)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
@ -128,96 +86,36 @@ static void update_editing_state_response_cb(GObject* object,
|
||||
|
||||
// Informs Flutter of text input changes.
|
||||
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_extent = -1;
|
||||
if (!self->text_model->composing_range().collapsed()) {
|
||||
composing_base = self->text_model->composing_range().base();
|
||||
composing_extent = self->text_model->composing_range().extent();
|
||||
}
|
||||
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));
|
||||
|
||||
// The following keys are not implemented and set to default values.
|
||||
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,
|
||||
flutter::TextRange selection = self->text_model->selection();
|
||||
fl_text_input_channel_update_editing_state(
|
||||
self->channel, self->client_id, self->text_model->GetText().c_str(),
|
||||
selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
|
||||
composing_base, composing_extent, self->cancellable,
|
||||
update_editing_state_response_cb, self);
|
||||
}
|
||||
|
||||
// Informs Flutter of text input changes by passing just the delta.
|
||||
static void update_editing_state_with_delta(FlTextInputHandler* self,
|
||||
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();
|
||||
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_extent = -1;
|
||||
if (!self->text_model->composing_range().collapsed()) {
|
||||
composing_base = self->text_model->composing_range().base();
|
||||
composing_extent = self->text_model->composing_range().extent();
|
||||
}
|
||||
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,
|
||||
self->cancellable, update_editing_state_response_cb, self);
|
||||
fl_text_input_channel_update_editing_state_with_deltas(
|
||||
self->channel, self->client_id, delta->old_text().c_str(),
|
||||
delta->delta_text().c_str(), delta->delta_start(), delta->delta_end(),
|
||||
selection.base(), selection.extent(), FL_TEXT_AFFINITY_DOWNSTREAM, FALSE,
|
||||
composing_base, composing_extent, self->cancellable,
|
||||
update_editing_state_with_deltas_response_cb, self);
|
||||
}
|
||||
|
||||
// Called when a response is received from TextInputClient.performAction()
|
||||
@ -225,9 +123,9 @@ static void perform_action_response_cb(GObject* object,
|
||||
GAsyncResult* result,
|
||||
gpointer user_data) {
|
||||
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)) {
|
||||
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->input_action != nullptr);
|
||||
|
||||
g_autoptr(FlValue) args = fl_value_new_list();
|
||||
fl_value_append_take(args, fl_value_new_int(self->client_id));
|
||||
fl_value_append_take(args, fl_value_new_string(self->input_action));
|
||||
|
||||
fl_method_channel_invoke_method(self->channel, kPerformActionMethod, args,
|
||||
self->cancellable, perform_action_response_cb,
|
||||
self);
|
||||
fl_text_input_channel_perform_action(self->channel, self->client_id,
|
||||
self->input_action, self->cancellable,
|
||||
perform_action_response_cb, self);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static FlMethodResponse* set_client(FlTextInputHandler* 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));
|
||||
}
|
||||
static void set_client(int64_t client_id,
|
||||
const gchar* input_action,
|
||||
gboolean enable_delta_model,
|
||||
FlTextInputType input_type,
|
||||
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));
|
||||
FlValue* config_value = fl_value_get_list_value(args, 1);
|
||||
self->client_id = client_id;
|
||||
g_free(self->input_action);
|
||||
FlValue* input_action_value =
|
||||
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->input_action = g_strdup(input_action);
|
||||
self->enable_delta_model = enable_delta_model;
|
||||
|
||||
// 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));
|
||||
self->input_type = input_type;
|
||||
}
|
||||
|
||||
// Hides the input method.
|
||||
static FlMethodResponse* hide(FlTextInputHandler* self) {
|
||||
gtk_im_context_focus_out(self->im_context);
|
||||
static void hide(gpointer user_data) {
|
||||
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.
|
||||
static FlMethodResponse* show(FlTextInputHandler* self) {
|
||||
if (self->input_type == kFlTextInputTypeNone) {
|
||||
return hide(self);
|
||||
static void show(gpointer user_data) {
|
||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||
|
||||
if (self->input_type == FL_TEXT_INPUT_TYPE_NONE) {
|
||||
hide(user_data);
|
||||
return;
|
||||
}
|
||||
|
||||
gtk_im_context_focus_in(self->im_context);
|
||||
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
||||
|
||||
// Updates the editing state from Flutter.
|
||||
static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
||||
FlValue* args) {
|
||||
const gchar* text =
|
||||
fl_value_get_string(fl_value_lookup_string(args, kTextKey));
|
||||
static 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) {
|
||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||
|
||||
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.
|
||||
if (selection_base == -1 && selection_extent == -1) {
|
||||
selection_base = selection_extent = 0;
|
||||
@ -423,10 +291,6 @@ static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
||||
self->text_model->SetSelection(
|
||||
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) {
|
||||
self->text_model->EndComposing();
|
||||
} else {
|
||||
@ -435,25 +299,22 @@ static FlMethodResponse* set_editing_state(FlTextInputHandler* self,
|
||||
self->text_model->SetComposingRange(
|
||||
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.
|
||||
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;
|
||||
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
||||
|
||||
// Update the IM cursor position.
|
||||
//
|
||||
// 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
|
||||
// not in IME composing mode) and updates to the matrix transform from local
|
||||
// coordinates to Flutter root coordinates. This function is called after each
|
||||
// of these updates. It transforms the composing rect to GDK window coordinates
|
||||
// and notifies GTK of the updated cursor position.
|
||||
// over the text input channel: updates to the composing rect (cursor rect
|
||||
// when not in IME composing mode) and updates to the matrix transform from
|
||||
// local coordinates to Flutter root coordinates. This function is called
|
||||
// after each of these updates. It transforms the composing rect to GDK window
|
||||
// coordinates and notifies GTK of the updated cursor position.
|
||||
static void update_im_cursor_position(FlTextInputHandler* self) {
|
||||
g_autoptr(FlTextInputViewDelegate) 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(
|
||||
view_delegate, x, y, &preedit_rect.x, &preedit_rect.y);
|
||||
|
||||
// Set the cursor location in window coordinates so that GTK can position any
|
||||
// system input method windows.
|
||||
// Set the cursor location in window coordinates so that GTK can position
|
||||
// any system input method windows.
|
||||
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
|
||||
// transform from the local coordinate system of the EditableText to root
|
||||
// Flutter coordinate system.
|
||||
static FlMethodResponse* set_editable_size_and_transform(
|
||||
FlTextInputHandler* self,
|
||||
FlValue* args) {
|
||||
FlValue* transform = fl_value_lookup_string(args, kTransform);
|
||||
size_t transform_len = fl_value_get_length(transform);
|
||||
g_warn_if_fail(transform_len == 16);
|
||||
static void set_editable_size_and_transform(double* transform,
|
||||
gpointer user_data) {
|
||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(user_data);
|
||||
|
||||
for (size_t i = 0; i < transform_len; ++i) {
|
||||
double val = fl_value_get_float(fl_value_get_list_value(transform, i));
|
||||
self->editabletext_transform[i / 4][i % 4] = val;
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
self->editabletext_transform[i / 4][i % 4] = transform[i];
|
||||
}
|
||||
update_im_cursor_position(self);
|
||||
|
||||
return FL_METHOD_RESPONSE(fl_method_success_response_new(nullptr));
|
||||
}
|
||||
|
||||
// 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
|
||||
// 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(FlTextInputHandler* self,
|
||||
FlValue* args) {
|
||||
self->composing_rect.x =
|
||||
fl_value_get_float(fl_value_lookup_string(args, "x"));
|
||||
self->composing_rect.y =
|
||||
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,
|
||||
static void set_marked_text_rect(double x,
|
||||
double y,
|
||||
double width,
|
||||
double height,
|
||||
gpointer user_data) {
|
||||
FlTextInputHandler* self = FL_TEXT_INPUT_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, 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);
|
||||
}
|
||||
self->composing_rect.x = x;
|
||||
self->composing_rect.y = y;
|
||||
self->composing_rect.width = width;
|
||||
self->composing_rect.height = height;
|
||||
update_im_cursor_position(self);
|
||||
}
|
||||
|
||||
// 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.
|
||||
static void fl_text_input_handler_init(FlTextInputHandler* self) {
|
||||
self->client_id = kClientIdUnset;
|
||||
self->input_type = kFlTextInputTypeText;
|
||||
self->input_type = FL_TEXT_INPUT_TYPE_TEXT;
|
||||
self->text_model = new flutter::TextInputModel();
|
||||
self->cancellable = g_cancellable_new();
|
||||
}
|
||||
@ -622,6 +442,16 @@ static void init_im_context(FlTextInputHandler* self,
|
||||
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(
|
||||
FlBinaryMessenger* messenger,
|
||||
GtkIMContext* im_context,
|
||||
@ -633,11 +463,8 @@ FlTextInputHandler* fl_text_input_handler_new(
|
||||
FlTextInputHandler* self = FL_TEXT_INPUT_HANDLER(
|
||||
g_object_new(fl_text_input_handler_get_type(), nullptr));
|
||||
|
||||
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);
|
||||
fl_text_input_channel_new(messenger, &text_input_vtable, self);
|
||||
|
||||
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_KP_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) {
|
||||
self->text_model->AddCodePoint('\n');
|
||||
text = "\n";
|
||||
|
Loading…
x
Reference in New Issue
Block a user