From a7bf4007cbf279313b09dec8610244d5dc01ce00 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 14 Nov 2024 14:12:00 +1300 Subject: [PATCH 1/8] Split channel messaging out of FlSettingsHandler --- shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_settings_channel.cc | 77 +++++++++++++++++++++ shell/platform/linux/fl_settings_channel.h | 58 ++++++++++++++++ shell/platform/linux/fl_settings_handler.cc | 40 +++-------- 4 files changed, 147 insertions(+), 29 deletions(-) create mode 100644 shell/platform/linux/fl_settings_channel.cc create mode 100644 shell/platform/linux/fl_settings_channel.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index af14c332c7b38..eb27ec39bac07 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -138,6 +138,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", diff --git a/shell/platform/linux/fl_settings_channel.cc b/shell/platform/linux/fl_settings_channel.cc new file mode 100644 index 0000000000000..2ddec19ea685b --- /dev/null +++ b/shell/platform/linux/fl_settings_channel.cc @@ -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); +} diff --git a/shell/platform/linux/fl_settings_channel.h b/shell/platform/linux/fl_settings_channel.h new file mode 100644 index 0000000000000..3c3409a2f90e2 --- /dev/null +++ b/shell/platform/linux/fl_settings_channel.h @@ -0,0 +1,58 @@ +// 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 { + FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_LIGHT, + FL_SETTINGS_CHANNEL_PLATFORM_BRIGHTNESS_DARK +} 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_ diff --git a/shell/platform/linux/fl_settings_handler.cc b/shell/platform/linux/fl_settings_handler.cc index a62044b746874..11718a68fff46 100644 --- a/shell/platform/linux/fl_settings_handler.cc +++ b/shell/platform/linux/fl_settings_handler.cc @@ -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; } From da6e24e51463fb7970c384ac9563f4b4606ad364 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 14 Nov 2024 16:40:12 +1300 Subject: [PATCH 2/8] Split channel messaging out of FlPlatformHandler --- shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_platform_channel.cc | 347 ++++++++++++++++++++ shell/platform/linux/fl_platform_channel.h | 108 ++++++ shell/platform/linux/fl_platform_handler.cc | 269 ++++----------- 4 files changed, 512 insertions(+), 213 deletions(-) create mode 100644 shell/platform/linux/fl_platform_channel.cc create mode 100644 shell/platform/linux/fl_platform_channel.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index eb27ec39bac07..d1ec4248aa8a0 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -128,6 +128,7 @@ source_set("flutter_linux_sources") { "fl_method_response.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", diff --git a/shell/platform/linux/fl_platform_channel.cc b/shell/platform/linux/fl_platform_channel.cc new file mode 100644 index 0000000000000..3e45f03cd9ea5 --- /dev/null +++ b/shell/platform/linux/fl_platform_channel.cc @@ -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 + +#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)); +} diff --git a/shell/platform/linux/fl_platform_channel.h b/shell/platform/linux/fl_platform_channel.h new file mode 100644 index 0000000000000..3dd5e424b44f7 --- /dev/null +++ b/shell/platform/linux/fl_platform_channel.h @@ -0,0 +1,108 @@ +// 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 { + FL_PLATFORM_CHANNEL_EXIT_TYPE_CANCELABLE, + FL_PLATFORM_CHANNEL_EXIT_TYPE_REQUIRED, +} FlPlatformChannelExitType; + +typedef enum { + FL_PLATFORM_CHANNEL_EXIT_RESPONSE_CANCEL, + FL_PLATFORM_CHANNEL_EXIT_RESPONSE_EXIT, +} 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_ diff --git a/shell/platform/linux/fl_platform_handler.cc b/shell/platform/linux/fl_platform_handler.cc index 28db474174956..5be3470f0a1e1 100644 --- a/shell/platform/linux/fl_platform_handler.cc +++ b/shell/platform/linux/fl_platform_handler.cc @@ -7,33 +7,12 @@ #include #include -#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); } From 0e0dd0eceff431a69ab5541f46d05c5c78aa3d37 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Thu, 14 Nov 2024 17:00:03 +1300 Subject: [PATCH 3/8] Split channel messaging out of FlMouseCursorHandler --- shell/platform/linux/BUILD.gn | 1 + .../platform/linux/fl_mouse_cursor_channel.cc | 104 ++++++++++++++++++ .../platform/linux/fl_mouse_cursor_channel.h | 47 ++++++++ .../platform/linux/fl_mouse_cursor_handler.cc | 57 ++-------- 4 files changed, 161 insertions(+), 48 deletions(-) create mode 100644 shell/platform/linux/fl_mouse_cursor_channel.cc create mode 100644 shell/platform/linux/fl_mouse_cursor_channel.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index d1ec4248aa8a0..1cd15d1c4f672 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -126,6 +126,7 @@ 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", diff --git a/shell/platform/linux/fl_mouse_cursor_channel.cc b/shell/platform/linux/fl_mouse_cursor_channel.cc new file mode 100644 index 0000000000000..6314f7b498ba1 --- /dev/null +++ b/shell/platform/linux/fl_mouse_cursor_channel.cc @@ -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 + +#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; +} diff --git a/shell/platform/linux/fl_mouse_cursor_channel.h b/shell/platform/linux/fl_mouse_cursor_channel.h new file mode 100644 index 0000000000000..bc8a6b968cc0f --- /dev/null +++ b/shell/platform/linux/fl_mouse_cursor_channel.h @@ -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_ diff --git a/shell/platform/linux/fl_mouse_cursor_handler.cc b/shell/platform/linux/fl_mouse_cursor_handler.cc index 3b87567fa2083..449a063f2d532 100644 --- a/shell/platform/linux/fl_mouse_cursor_handler.cc +++ b/shell/platform/linux/fl_mouse_cursor_handler.cc @@ -6,20 +6,14 @@ #include -#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; } From 677a4ef5769ba4f9271da7f39b4e477b0909639f Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 18 Nov 2024 14:48:22 +1300 Subject: [PATCH 4/8] Split channel messaging out of FlTextInputHandler --- shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_text_input_channel.cc | 409 ++++++++++++++++++ shell/platform/linux/fl_text_input_channel.h | 211 +++++++++ shell/platform/linux/fl_text_input_handler.cc | 357 ++++----------- 4 files changed, 712 insertions(+), 266 deletions(-) create mode 100644 shell/platform/linux/fl_text_input_channel.cc create mode 100644 shell/platform/linux/fl_text_input_channel.h diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 1cd15d1c4f672..53c506baac4a5 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -149,6 +149,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", diff --git a/shell/platform/linux/fl_text_input_channel.cc b/shell/platform/linux/fl_text_input_channel.cc new file mode 100644 index 0000000000000..cc37e2aace26c --- /dev/null +++ b/shell/platform/linux/fl_text_input_channel.cc @@ -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; +} diff --git a/shell/platform/linux/fl_text_input_channel.h b/shell/platform/linux/fl_text_input_channel.h new file mode 100644 index 0000000000000..566cd8a06549e --- /dev/null +++ b/shell/platform/linux/fl_text_input_channel.h @@ -0,0 +1,211 @@ +// 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 + +#include "flutter/shell/platform/linux/public/flutter_linux/fl_binary_messenger.h" + +G_BEGIN_DECLS + +typedef enum { + 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, +} FlTextInputType; + +typedef enum { + FL_TEXT_AFFINITY_UPSTREAM, + FL_TEXT_AFFINITY_DOWNSTREAM, +} 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_ diff --git a/shell/platform/linux/fl_text_input_handler.cc b/shell/platform/linux/fl_text_input_handler.cc index 8dc44ba290b97..6b941ac5c3cc0 100644 --- a/shell/platform/linux/fl_text_input_handler.cc +++ b/shell/platform/linux/fl_text_input_handler.cc @@ -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; @@ -99,121 +54,61 @@ 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)) { - g_warning("Failed to call %s: %s", kUpdateEditingStateMethod, - error->message); + if (!fl_text_input_channel_update_editing_state_finish(object, result, + &error)) { + 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)) { + g_warning("Failed to update editing state with deltas: %s", error->message); } } // 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, nullptr, - update_editing_state_response_cb, self); + 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, nullptr, + 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, nullptr, - 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, nullptr, + update_editing_state_with_deltas_response_cb, self); } // Called when a response is received from TextInputClient.performAction() @@ -221,8 +116,8 @@ static void perform_action_response_cb(GObject* object, GAsyncResult* result, gpointer user_data) { g_autoptr(GError) error = nullptr; - if (!finish_method(object, result, &error)) { - g_warning("Failed to call %s: %s", kPerformActionMethod, error->message); + if (!fl_text_input_channel_perform_action_finish(object, result, &error)) { + g_warning("Failed to perform action: %s", error->message); } } @@ -232,12 +127,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, - nullptr, perform_action_response_cb, self); + fl_text_input_channel_perform_action(self->channel, self->client_id, + self->input_action, nullptr, + perform_action_response_cb, self); } // Signal handler for GtkIMContext::preedit-start @@ -337,76 +229,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; @@ -416,10 +282,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 { @@ -428,15 +290,12 @@ 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. @@ -484,20 +343,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); - - 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; +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 < 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. @@ -506,53 +359,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, - gpointer user_data) { +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. @@ -579,7 +397,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(); } @@ -611,6 +429,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, @@ -622,11 +450,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); @@ -670,7 +495,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"; From 8c297c49822dae1b3f940bf922ffaac816e71958 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 18 Nov 2024 17:57:29 +1300 Subject: [PATCH 5/8] Split channel messaging out of FlKeyboardHandler --- ci/licenses_golden/licenses_flutter | 4 + shell/platform/linux/BUILD.gn | 1 + shell/platform/linux/fl_keyboard_channel.cc | 84 +++++++++++++++++++++ shell/platform/linux/fl_keyboard_channel.h | 46 +++++++++++ shell/platform/linux/fl_keyboard_handler.cc | 44 +++-------- 5 files changed, 146 insertions(+), 33 deletions(-) create mode 100644 shell/platform/linux/fl_keyboard_channel.cc create mode 100644 shell/platform/linux/fl_keyboard_channel.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index c8b2aeae37e3e..2b8ef0b31ba54 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -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 @@ -47886,6 +47888,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 diff --git a/shell/platform/linux/BUILD.gn b/shell/platform/linux/BUILD.gn index 53c506baac4a5..14285debb0c59 100644 --- a/shell/platform/linux/BUILD.gn +++ b/shell/platform/linux/BUILD.gn @@ -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", diff --git a/shell/platform/linux/fl_keyboard_channel.cc b/shell/platform/linux/fl_keyboard_channel.cc new file mode 100644 index 0000000000000..8b7bc2706dc4d --- /dev/null +++ b/shell/platform/linux/fl_keyboard_channel.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; +} diff --git a/shell/platform/linux/fl_keyboard_channel.h b/shell/platform/linux/fl_keyboard_channel.h new file mode 100644 index 0000000000000..69c2b590f9fe5 --- /dev/null +++ b/shell/platform/linux/fl_keyboard_channel.h @@ -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_ diff --git a/shell/platform/linux/fl_keyboard_handler.cc b/shell/platform/linux/fl_keyboard_handler.cc index 1bf438be1f2b8..9a1478da7f3d5 100644 --- a/shell/platform/linux/fl_keyboard_handler.cc +++ b/shell/platform/linux/fl_keyboard_handler.cc @@ -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; } From 47871f8ab4af3f3d24a22a5788554811c161def4 Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 18 Nov 2024 17:59:42 +1300 Subject: [PATCH 6/8] Update licenses --- ci/licenses_golden/licenses_flutter | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 2b8ef0b31ba54..5b8f38980eeb3 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -45013,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 @@ -45042,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 @@ -45058,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 @@ -47915,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 @@ -47944,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 @@ -47960,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 From ac34e2f8cae19c2e7c00f75f2adfe708127b854b Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Mon, 18 Nov 2024 21:02:02 +1300 Subject: [PATCH 7/8] Allow standard enum style --- shell/platform/linux/fl_platform_channel.h | 4 ++++ shell/platform/linux/fl_settings_channel.h | 2 ++ shell/platform/linux/fl_text_input_channel.h | 2 ++ 3 files changed, 8 insertions(+) diff --git a/shell/platform/linux/fl_platform_channel.h b/shell/platform/linux/fl_platform_channel.h index 3dd5e424b44f7..b1cdf9328a653 100644 --- a/shell/platform/linux/fl_platform_channel.h +++ b/shell/platform/linux/fl_platform_channel.h @@ -11,13 +11,17 @@ 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, diff --git a/shell/platform/linux/fl_settings_channel.h b/shell/platform/linux/fl_settings_channel.h index 3c3409a2f90e2..6f1945942e76a 100644 --- a/shell/platform/linux/fl_settings_channel.h +++ b/shell/platform/linux/fl_settings_channel.h @@ -10,8 +10,10 @@ 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, diff --git a/shell/platform/linux/fl_text_input_channel.h b/shell/platform/linux/fl_text_input_channel.h index 566cd8a06549e..f2e54ee4d9fbb 100644 --- a/shell/platform/linux/fl_text_input_channel.h +++ b/shell/platform/linux/fl_text_input_channel.h @@ -12,11 +12,13 @@ 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 { From 876a2fa461b8593c2991ee3dead1220caf9d064d Mon Sep 17 00:00:00 2001 From: Robert Ancell Date: Tue, 19 Nov 2024 10:44:28 +1300 Subject: [PATCH 8/8] One more allow standard enum style --- shell/platform/linux/fl_text_input_channel.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/shell/platform/linux/fl_text_input_channel.h b/shell/platform/linux/fl_text_input_channel.h index f2e54ee4d9fbb..fad0cd5e9be03 100644 --- a/shell/platform/linux/fl_text_input_channel.h +++ b/shell/platform/linux/fl_text_input_channel.h @@ -22,8 +22,10 @@ typedef enum { } 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,