diff --git a/build/config/BUILDCONFIG.gn b/build/config/BUILDCONFIG.gn index 65876b36..baea25a7 100644 --- a/build/config/BUILDCONFIG.gn +++ b/build/config/BUILDCONFIG.gn @@ -31,6 +31,9 @@ declare_args() { # Use the system libstdc++ without building third_party/libcxx from source. use_system_cxx = false + + # Use tizen-core-wl API instead of ecore. + use_tcore = false } _default_configs = [ diff --git a/flutter/shell/platform/tizen/BUILD.gn b/flutter/shell/platform/tizen/BUILD.gn index 3d9dfd0b..e902416e 100644 --- a/flutter/shell/platform/tizen/BUILD.gn +++ b/flutter/shell/platform/tizen/BUILD.gn @@ -17,15 +17,6 @@ config("flutter_tizen_config") { "${sysroot_path}/usr/include/dali-adaptor", "${sysroot_path}/usr/include/dali-toolkit", "${sysroot_path}/usr/include/dlog", - "${sysroot_path}/usr/include/ecore-1", - "${sysroot_path}/usr/include/ecore-imf-1", - "${sysroot_path}/usr/include/ecore-input-1", - "${sysroot_path}/usr/include/ecore-wayland-1", - "${sysroot_path}/usr/include/ecore-wl2-1", - "${sysroot_path}/usr/include/efl-1", - "${sysroot_path}/usr/include/eina-1", - "${sysroot_path}/usr/include/eina-1/eina", - "${sysroot_path}/usr/include/eo-1", "${sysroot_path}/usr/include/feedback", "${sysroot_path}/usr/include/system", "${sysroot_path}/usr/include/tzsh", @@ -35,6 +26,28 @@ config("flutter_tizen_config") { "${sysroot_path}/usr/lib/glib-2.0/include", ] + # tcore-specific includes + if (use_tcore) { + include_dirs += [ + "${sysroot_path}/usr/include/tizen-core", + "${sysroot_path}/usr/include/tizen-core-imf", + "${sysroot_path}/usr/include/tizen-core-wl", + ] + } else { + # ecore-specific includes + include_dirs += [ + "${sysroot_path}/usr/include/ecore-1", + "${sysroot_path}/usr/include/ecore-imf-1", + "${sysroot_path}/usr/include/ecore-input-1", + "${sysroot_path}/usr/include/ecore-wayland-1", + "${sysroot_path}/usr/include/ecore-wl2-1", + "${sysroot_path}/usr/include/efl-1", + "${sysroot_path}/usr/include/eina-1", + "${sysroot_path}/usr/include/eina-1/eina", + "${sysroot_path}/usr/include/eo-1", + ] + } + cflags_cc = [ "-Wno-newline-eof", "-Wno-macro-redefined", @@ -93,14 +106,25 @@ template("embedder") { "logger.cc", "system_utils.cc", "tizen_event_loop.cc", - "tizen_input_method_context.cc", "tizen_renderer.cc", "tizen_renderer_egl.cc", "tizen_renderer_gl.cc", "tizen_vsync_waiter.cc", - "tizen_window_ecore_wl2.cc", ] + # Select window implementation based on use_tcore + if (use_tcore) { + sources += [ + "tizen_input_method_context_tcore.cc", + "tizen_window_tcore_wl.cc", + ] + } else { + sources += [ + "tizen_input_method_context.cc", + "tizen_window_ecore_wl2.cc", + ] + } + lib_dirs = [ "//engine/${target_cpu}" ] libs = [ @@ -113,11 +137,7 @@ template("embedder") { "capi-system-info", "capi-system-system-settings", "dlog", - "ecore", - "ecore_imf", - "ecore_input", - "ecore_wl2", - "eina", + "glib-2.0", "gio-2.0", "feedback", "flutter_engine", @@ -130,6 +150,23 @@ template("embedder") { "GLESv2", ] + # tcore-specific or ecore-specific libraries + if (use_tcore) { + libs += [ + "tizen-core", + "tizen-core-imf", + "tizen-core-wl", + ] + } else { + libs += [ + "ecore", + "ecore_imf", + "ecore_input", + "ecore_wl2", + "eina", + ] + } + if (target_name == "flutter_tizen_common" || target_name == "flutter_tizen_common_experimental") { sources += [ "channels/tizen_shell.cc" ] @@ -143,6 +180,11 @@ template("embedder") { defines += invoker.defines defines += [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] + # Add USE_TCORE_WL define when using tcore + if (use_tcore) { + defines += [ "USE_TCORE_WL" ] + } + if (target_name == "flutter_tizen_mobile_experimental" || target_name == "flutter_tizen_tv_experimental" || target_name == "flutter_tizen_common_experimental") { @@ -255,6 +297,10 @@ executable("flutter_tizen_unittests") { ldflags = [ "-Wl,--unresolved-symbols=ignore-in-shared-libs" ] + if (use_tcore) { + defines = [ "USE_TCORE_WL" ] + } + configs += [ ":flutter_tizen_config" ] deps += [ diff --git a/flutter/shell/platform/tizen/channels/input_panel_channel.cc b/flutter/shell/platform/tizen/channels/input_panel_channel.cc index c2af019c..d6c6150b 100644 --- a/flutter/shell/platform/tizen/channels/input_panel_channel.cc +++ b/flutter/shell/platform/tizen/channels/input_panel_channel.cc @@ -7,7 +7,11 @@ #include "flutter/shell/platform/common/client_wrapper/include/flutter/event_stream_handler_functions.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/standard_method_codec.h" #include "flutter/shell/platform/tizen/logger.h" +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_input_method_context_tcore.h" +#else #include "flutter/shell/platform/tizen/tizen_input_method_context.h" +#endif namespace { diff --git a/flutter/shell/platform/tizen/channels/key_mapping.cc b/flutter/shell/platform/tizen/channels/key_mapping.cc index 35c0f368..54ea04de 100644 --- a/flutter/shell/platform/tizen/channels/key_mapping.cc +++ b/flutter/shell/platform/tizen/channels/key_mapping.cc @@ -177,7 +177,7 @@ std::map kScanCodeToGtkKeyCode = { {0x0000024d, 269025069}, // launchScreenSaver }; -std::map kEcoreModifierToGtkModifier = { +std::map kModifierToGtkModifier = { {0x0001, 1 << 0}, // SHIFT -> modifierShift {0x0002, 1 << 2}, // CTRL -> modifierControl {0x0004, 1 << 3}, // ALT -> modifierMod1 diff --git a/flutter/shell/platform/tizen/channels/key_mapping.h b/flutter/shell/platform/tizen/channels/key_mapping.h index d2ec300b..e07e1fb6 100644 --- a/flutter/shell/platform/tizen/channels/key_mapping.h +++ b/flutter/shell/platform/tizen/channels/key_mapping.h @@ -19,15 +19,15 @@ // legacy RawKeyboard API is removed from the framework in the future. extern std::map kScanCodeToGtkKeyCode; -// Maps Ecore modifiers to GTK modifiers. +// Maps Tizen modifier bitmask values to GTK modifiers. // -// The values are originally defined in: -// - efl/Ecore_Input.h -// - flutter/raw_keyboard_linux.dart (GtkKeyHelper) +// The modifier bitmask values match tizen_core_wl_modifier_e. +// The GTK modifier values are from flutter/raw_keyboard_linux.dart +// (GtkKeyHelper). // // Provided only for backward compatibility. This will be removed after the // legacy RawKeyboard API is removed from the framework in the future. -extern std::map kEcoreModifierToGtkModifier; +extern std::map kModifierToGtkModifier; // Maps XKB scan codes to Flutter's physical key codes. // diff --git a/flutter/shell/platform/tizen/channels/keyboard_channel.cc b/flutter/shell/platform/tizen/channels/keyboard_channel.cc index d48caecb..8b6e32cb 100644 --- a/flutter/shell/platform/tizen/channels/keyboard_channel.cc +++ b/flutter/shell/platform/tizen/channels/keyboard_channel.cc @@ -164,7 +164,7 @@ void KeyboardChannel::SendChannelEvent(const char* key, } int gtk_modifiers = 0; - for (auto element : kEcoreModifierToGtkModifier) { + for (auto element : kModifierToGtkModifier) { if (element.first & modifiers) { gtk_modifiers |= element.second; } diff --git a/flutter/shell/platform/tizen/channels/text_input_channel.cc b/flutter/shell/platform/tizen/channels/text_input_channel.cc index 7e2b3435..b986f57e 100644 --- a/flutter/shell/platform/tizen/channels/text_input_channel.cc +++ b/flutter/shell/platform/tizen/channels/text_input_channel.cc @@ -335,7 +335,6 @@ bool TextInputChannel::HandleKey(const char* key, return true; #endif } else { - FT_LOG(Info) << "Key[" << key << "] is unhandled."; return false; } diff --git a/flutter/shell/platform/tizen/channels/text_input_channel.h b/flutter/shell/platform/tizen/channels/text_input_channel.h index 11d3aa35..91c2a68a 100644 --- a/flutter/shell/platform/tizen/channels/text_input_channel.h +++ b/flutter/shell/platform/tizen/channels/text_input_channel.h @@ -11,7 +11,11 @@ #include "flutter/shell/platform/common/client_wrapper/include/flutter/binary_messenger.h" #include "flutter/shell/platform/common/client_wrapper/include/flutter/method_channel.h" #include "flutter/shell/platform/common/text_input_model.h" +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_input_method_context_tcore.h" +#else #include "flutter/shell/platform/tizen/tizen_input_method_context.h" +#endif #include "rapidjson/document.h" namespace flutter { diff --git a/flutter/shell/platform/tizen/channels/window_channel.cc b/flutter/shell/platform/tizen/channels/window_channel.cc index 5e618e55..4f4d825f 100644 --- a/flutter/shell/platform/tizen/channels/window_channel.cc +++ b/flutter/shell/platform/tizen/channels/window_channel.cc @@ -8,7 +8,11 @@ #include "flutter/shell/platform/tizen/channels/encodable_value_holder.h" #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/tizen_window.h" +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_window_tcore_wl.h" +#else #include "flutter/shell/platform/tizen/tizen_window_ecore_wl2.h" +#endif namespace flutter { diff --git a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer_dma.cc b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer_dma.cc index d422d522..511bc03f 100644 --- a/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer_dma.cc +++ b/flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer_dma.cc @@ -3,6 +3,9 @@ // found in the LICENSE file. #include "flutter/shell/platform/tizen/external_texture_surface_vulkan_buffer_dma.h" + +#include + #include "flutter/shell/platform/tizen/logger.h" namespace flutter { diff --git a/flutter/shell/platform/tizen/flutter_tizen.cc b/flutter/shell/platform/tizen/flutter_tizen.cc index 9615b111..c87e3732 100644 --- a/flutter/shell/platform/tizen/flutter_tizen.cc +++ b/flutter/shell/platform/tizen/flutter_tizen.cc @@ -20,7 +20,11 @@ #include "flutter/shell/platform/tizen/tizen_view_nui.h" #endif #include "flutter/shell/platform/tizen/tizen_window.h" +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_window_tcore_wl.h" +#else #include "flutter/shell/platform/tizen/tizen_window_ecore_wl2.h" +#endif namespace { @@ -201,12 +205,21 @@ FlutterDesktopViewRef FlutterDesktopViewCreateFromNewWindow( std::unique_ptr window; +#ifdef USE_TCORE_WL + window = std::make_unique( + window_geometry, window_properties.transparent, + window_properties.focusable, window_properties.top_level, + window_properties.pointing_device_support, + window_properties.floating_menu_support, window_properties.window_handle, + window_properties.renderer_type == kEVulkan); +#else window = std::make_unique( window_geometry, window_properties.transparent, window_properties.focusable, window_properties.top_level, window_properties.pointing_device_support, window_properties.floating_menu_support, window_properties.window_handle, window_properties.renderer_type == kEVulkan); +#endif auto view = std::make_unique( flutter::kImplicitViewId, std::move(window), diff --git a/flutter/shell/platform/tizen/flutter_tizen_display_monitor.cc b/flutter/shell/platform/tizen/flutter_tizen_display_monitor.cc index 0a5bdf61..7cf67d42 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_display_monitor.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_display_monitor.cc @@ -3,7 +3,6 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -#include #include #include "flutter/shell/platform/tizen/flutter_tizen_display_monitor.h" @@ -29,12 +28,11 @@ void FlutterTizenDisplayMonitor::UpdateDisplays() { display.display_id = 0; display.single_display = true; - double fps = ecore_animator_frametime_get(); - if (fps <= 0.0) { - display.refresh_rate = 0.0; - } else { - display.refresh_rate = 1 / fps; - } + // TODO(jsuya): Default to 60Hz refresh rate. The previous implementation + // used ecore_animator_frametime_get(), which is no longer available after + // dropping the Ecore dependency. Replace this once tizen-core (or another + // public Tizen API) exposes a way to query the display refresh rate. + display.refresh_rate = 60.0; int32_t width = 0, height = 0, dpi = 0; FlutterTizenView* view = engine_->view(); diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine.cc b/flutter/shell/platform/tizen/flutter_tizen_engine.cc index acfb334d..05cc468a 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine.cc @@ -15,7 +15,11 @@ #include "flutter/shell/platform/tizen/flutter_tizen_view.h" #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/system_utils.h" +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_input_method_context_tcore.h" +#else #include "flutter/shell/platform/tizen/tizen_input_method_context.h" +#endif #include "flutter/shell/platform/tizen/tizen_renderer_egl.h" #ifdef FLUTTER_TIZEN_EXPERIMENTAL diff --git a/flutter/shell/platform/tizen/flutter_tizen_engine_unittest.cc b/flutter/shell/platform/tizen/flutter_tizen_engine_unittest.cc index 55978b69..bd441be7 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_engine_unittest.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_engine_unittest.cc @@ -4,7 +4,11 @@ #include "flutter/shell/platform/tizen/flutter_tizen_engine.h" +#ifdef USE_TCORE_WL +#include +#else #include +#endif #include "flutter/shell/platform/embedder/test_utils/proc_table_replacement.h" #include "flutter/shell/platform/tizen/testing/engine_modifier.h" @@ -15,7 +19,13 @@ namespace testing { class FlutterTizenEngineTest : public ::testing::Test { public: - FlutterTizenEngineTest() { ecore_init(); } + FlutterTizenEngineTest() { +#ifdef USE_TCORE_WL + tizen_core_wl_init(); +#else + ecore_init(); +#endif + } protected: void SetUp() { diff --git a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar_unittests.cc b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar_unittests.cc index 535bf476..3432e714 100644 --- a/flutter/shell/platform/tizen/flutter_tizen_texture_registrar_unittests.cc +++ b/flutter/shell/platform/tizen/flutter_tizen_texture_registrar_unittests.cc @@ -4,7 +4,11 @@ #include "flutter/shell/platform/tizen/flutter_tizen_texture_registrar.h" +#ifdef USE_TCORE_WL +#include +#else #include +#endif #include @@ -18,7 +22,13 @@ namespace testing { class FlutterTizenTextureRegistrarTest : public ::testing::Test { public: - FlutterTizenTextureRegistrarTest() { ecore_init(); } + FlutterTizenTextureRegistrarTest() { +#ifdef USE_TCORE_WL + tizen_core_wl_init(); +#else + ecore_init(); +#endif + } protected: void SetUp() { diff --git a/flutter/shell/platform/tizen/public/flutter_tizen.h b/flutter/shell/platform/tizen/public/flutter_tizen.h index 6e9c27b4..466356cd 100644 --- a/flutter/shell/platform/tizen/public/flutter_tizen.h +++ b/flutter/shell/platform/tizen/public/flutter_tizen.h @@ -206,7 +206,7 @@ FLUTTER_EXPORT void FlutterDesktopViewDestroy(FlutterDesktopViewRef view); // Returns a native UI toolkit handle for manipulation in host application. // // Cast the returned void* -// - window ecore wl2 : to Ecore_Wl2_Window* +// - window ecore wl2 : to tizen_core_wl_window_h // @warning This API is a work-in-progress and may change. FLUTTER_EXPORT void* FlutterDesktopViewGetNativeHandle( FlutterDesktopViewRef view); diff --git a/flutter/shell/platform/tizen/tizen_clipboard.cc b/flutter/shell/platform/tizen/tizen_clipboard.cc index c8393aae..8b85c067 100644 --- a/flutter/shell/platform/tizen/tizen_clipboard.cc +++ b/flutter/shell/platform/tizen/tizen_clipboard.cc @@ -4,9 +4,19 @@ #include "tizen_clipboard.h" +#include + +#include +#include + #include "flutter/shell/platform/tizen/logger.h" #include "flutter/shell/platform/tizen/tizen_window.h" + +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_window_tcore_wl.h" +#else #include "flutter/shell/platform/tizen/tizen_window_ecore_wl2.h" +#endif namespace flutter { @@ -16,6 +26,272 @@ constexpr char kMimeTypeTextPlain[] = "text/plain;charset=utf-8"; } // namespace +#ifdef USE_TCORE_WL + +TizenClipboard::TizenClipboard(TizenViewBase* view) { + if (auto* window = dynamic_cast(view)) { + tizen_core_wl_window_h tcore_window = + static_cast(window->GetNativeHandle()); + tizen_core_wl_window_get_display(tcore_window, &display_); + } else { + if (tizen_core_wl_init() != TIZEN_CORE_WL_ERROR_NONE || + tizen_core_wl_display_create(&display_) != TIZEN_CORE_WL_ERROR_NONE || + tizen_core_wl_display_connect(display_, nullptr) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Failed to connect display for clipboard."; + display_ = nullptr; + return; + } + owns_display_ = true; + } + + if (tizen_core_wl_display_get_event(display_, &tcore_wl_event_) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Failed to get event handle for clipboard."; + return; + } + + tizen_core_wl_event_add_listener( + tcore_wl_event_, TIZEN_CORE_WL_EVENT_DATA_SOURCE_SEND, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + self->SendData(event); + }, + this, &send_listener_); + + tizen_core_wl_event_add_listener( + tcore_wl_event_, TIZEN_CORE_WL_EVENT_DATA_READY, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + self->ReceiveData(event); + }, + this, &receive_listener_); +} + +TizenClipboard::~TizenClipboard() { + on_data_callback_ = nullptr; + + if (send_listener_) { + tizen_core_wl_event_remove_listener(tcore_wl_event_, send_listener_); + } + if (receive_listener_) { + tizen_core_wl_event_remove_listener(tcore_wl_event_, receive_listener_); + } + + if (owns_display_ && display_) { + tizen_core_wl_display_disconnect(display_); + tizen_core_wl_display_destroy(display_); + tizen_core_wl_shutdown(); + } +} + +void TizenClipboard::SendData(void* event) { + if (!event) { + return; + } + tizen_core_wl_event_data_source_send_h send_event = + static_cast(event); + + char* mime_type = nullptr; + tizen_core_wl_event_data_source_send_get_type(send_event, &mime_type); + int fd = -1; + tizen_core_wl_event_data_source_send_get_fd(send_event, &fd); + + // Get serial from base event. + tizen_core_wl_event_data_source_base_h base_event = + static_cast(event); + unsigned int serial = 0; + tizen_core_wl_event_data_source_base_get_serial(base_event, &serial); + + if (!mime_type || + (strlen(mime_type) != 0 && strcmp(mime_type, kMimeTypeTextPlain))) { + FT_LOG(Error) << "Invaild mime type(" << (mime_type ? mime_type : "null") + << ")."; + if (fd >= 0) { + close(fd); + } + return; + } + + if (serial != selection_serial_) { + FT_LOG(Error) << "The serial doesn't match."; + if (fd >= 0) { + close(fd); + } + return; + } + + const char* buffer = data_.c_str(); + size_t remaining = data_.length(); + while (remaining > 0) { + ssize_t written = write(fd, buffer, remaining); + if (written < 0) { + if (errno == EINTR) { + continue; + } + FT_LOG(Error) << "Failed to write clipboard data: " << strerror(errno); + break; + } + buffer += written; + remaining -= written; + } + close(fd); +} + +void TizenClipboard::ReceiveData(void* event) { + if (!event) { + return; + } + tizen_core_wl_event_data_ready_h ready_event = + static_cast(event); + + void* raw_data = nullptr; + tizen_core_wl_event_data_ready_get_data(ready_event, &raw_data); + const char* data = static_cast(raw_data); + int len = 0; + tizen_core_wl_event_data_ready_get_len(ready_event, &len); + + if (data == nullptr || len < 1) { + FT_LOG(Info) << "No data available."; + if (on_data_callback_) { + on_data_callback_(""); + on_data_callback_ = nullptr; + } + return; + } + + tizen_core_wl_data_offer_h offer = nullptr; + tizen_core_wl_event_data_ready_get_offer(ready_event, &offer); + + if (offer != selection_offer_) { + FT_LOG(Error) << "The offer doesn't match."; + if (on_data_callback_) { + on_data_callback_(std::nullopt); + on_data_callback_ = nullptr; + } + return; + } + + size_t data_length = strlen(data); + size_t buffer_size = len; + std::string content; + + if (data_length < buffer_size) { + content.append(data, data_length); + } else { + content.append(data, buffer_size); + } + + if (on_data_callback_) { + on_data_callback_(content); + on_data_callback_ = nullptr; + } +} + +void TizenClipboard::SetData(const std::string& data) { + data_ = data; + + const char* mime_types[3]; + mime_types[0] = kMimeTypeTextPlain; + // TODO(jsuya): There is an issue where ECORE_WL2_EVENT_DATA_SOURCE_SEND event + // does not work properly even if ecore_wl2_dnd_selection_set() is called in + // Tizen 6.5 or lower. Therefore, add empty mimetype for event call from the + // cbhm module. Since it works normally from Tizen 8.0, this part may be + // modified in the future. + mime_types[1] = ""; + mime_types[2] = nullptr; + + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(display_, &default_seat); + if (default_seat) { + tizen_core_wl_data_h data_handle = nullptr; + tizen_core_wl_seat_get_data(default_seat, &data_handle); + if (data_handle) { + tizen_core_wl_data_set_selection(data_handle, mime_types, + &selection_serial_); + } + } + tizen_core_wl_display_flush(display_); +} + +bool TizenClipboard::GetData(ClipboardCallback on_data_callback) { + on_data_callback_ = std::move(on_data_callback); + + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(display_, &default_seat); + if (!default_seat) { + if (on_data_callback_) { + on_data_callback_ = nullptr; + } + return false; + } + + tizen_core_wl_data_h data_handle = nullptr; + tizen_core_wl_seat_get_data(default_seat, &data_handle); + if (!data_handle) { + if (on_data_callback_) { + on_data_callback_ = nullptr; + } + return false; + } + + tizen_core_wl_data_offer_h offer = nullptr; + tizen_core_wl_data_get_selection(data_handle, &offer); + selection_offer_ = offer; + if (!selection_offer_) { + FT_LOG(Error) << "tizen_core_wl_data_get_selection() failed."; + if (on_data_callback_) { + on_data_callback_ = nullptr; + } + return false; + } + + tizen_core_wl_data_receive(selection_offer_, + const_cast(kMimeTypeTextPlain)); + return true; +} + +bool TizenClipboard::HasStrings() { + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(display_, &default_seat); + if (!default_seat) { + return false; + } + + tizen_core_wl_data_h data_handle = nullptr; + tizen_core_wl_seat_get_data(default_seat, &data_handle); + if (!data_handle) { + return false; + } + + tizen_core_wl_data_offer_h offer = nullptr; + tizen_core_wl_data_get_selection(data_handle, &offer); + selection_offer_ = offer; + if (!selection_offer_) { + return false; + } + + char** mimes = nullptr; + int mime_count = 0; + tizen_core_wl_data_get_mimes(selection_offer_, &mimes, &mime_count); + + if (!mimes) { + return false; + } + + bool found = false; + for (int i = 0; i < mime_count; ++i) { + if (mimes[i] && !strcmp(kMimeTypeTextPlain, mimes[i])) { + found = true; + break; + } + } + free(mimes); + return found; +} + +#else // USE_TCORE_WL + TizenClipboard::TizenClipboard(TizenViewBase* view) { if (auto* window = dynamic_cast(view)) { auto* ecore_wl2_window = @@ -179,4 +455,6 @@ bool TizenClipboard::HasStrings() { return false; } +#endif // USE_TCORE_WL + } // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_clipboard.h b/flutter/shell/platform/tizen/tizen_clipboard.h index 244eafe8..df8d70c9 100644 --- a/flutter/shell/platform/tizen/tizen_clipboard.h +++ b/flutter/shell/platform/tizen/tizen_clipboard.h @@ -5,8 +5,12 @@ #ifndef EMBEDDER_TIZEN_CLIPBOARD_H_ #define EMBEDDER_TIZEN_CLIPBOARD_H_ +#ifdef USE_TCORE_WL +#include +#else #define EFL_BETA_API_SUPPORT #include +#endif #include #include @@ -35,10 +39,19 @@ class TizenClipboard { std::string data_; ClipboardCallback on_data_callback_; uint32_t selection_serial_ = 0; +#ifdef USE_TCORE_WL + tizen_core_wl_data_offer_h selection_offer_ = nullptr; + tizen_core_wl_display_h display_ = nullptr; + bool owns_display_ = false; + tizen_core_event_h tcore_wl_event_ = nullptr; + tizen_core_wl_event_listener_h send_listener_ = nullptr; + tizen_core_wl_event_listener_h receive_listener_ = nullptr; +#else Ecore_Wl2_Offer* selection_offer_ = nullptr; Ecore_Wl2_Display* display_ = nullptr; Ecore_Event_Handler* send_handler = nullptr; Ecore_Event_Handler* receive_handler = nullptr; +#endif }; } // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_event_loop.cc b/flutter/shell/platform/tizen/tizen_event_loop.cc index ce528e3b..edf010f7 100644 --- a/flutter/shell/platform/tizen/tizen_event_loop.cc +++ b/flutter/shell/platform/tizen/tizen_event_loop.cc @@ -5,6 +5,8 @@ #include "tizen_event_loop.h" +#include + #include namespace flutter { @@ -15,17 +17,34 @@ TizenEventLoop::TizenEventLoop(std::thread::id main_thread_id, : main_thread_id_(main_thread_id), get_current_time_(get_current_time), on_task_expired_(std::move(on_task_expired)) { - ecore_pipe_ = ecore_pipe_add( - [](void* data, void* buffer, unsigned int nbyte) -> void { - auto* self = static_cast(data); - self->ExecuteTaskEvents(); - }, - this); + if (pipe(pipe_fds_) == 0) { + pipe_channel_ = g_io_channel_unix_new(pipe_fds_[0]); + pipe_watch_id_ = g_io_add_watch( + pipe_channel_, G_IO_IN, + [](GIOChannel* source, GIOCondition condition, + gpointer data) -> gboolean { + auto* self = static_cast(data); + char buf[64]; + read(self->pipe_fds_[0], buf, sizeof(buf)); + self->ExecuteTaskEvents(); + return G_SOURCE_CONTINUE; + }, + this); + } } TizenEventLoop::~TizenEventLoop() { - if (ecore_pipe_) { - ecore_pipe_del(ecore_pipe_); + if (pipe_watch_id_ > 0) { + g_source_remove(pipe_watch_id_); + } + if (pipe_channel_) { + g_io_channel_unref(pipe_channel_); + } + if (pipe_fds_[0] >= 0) { + close(pipe_fds_[0]); + } + if (pipe_fds_[1] >= 0) { + close(pipe_fds_[1]); } } @@ -74,19 +93,23 @@ void TizenEventLoop::PostTask(FlutterTask flutter_task, const double flutter_duration = static_cast(flutter_target_time_nanos) - get_current_time_(); if (flutter_duration > 0) { - ecore_timer_add( - flutter_duration / 1000000000.0, - [](void* data) -> Eina_Bool { - auto* self = static_cast(data); - if (self->ecore_pipe_) { - ecore_pipe_write(self->ecore_pipe_, nullptr, 0); + int* pipe_write_fd = new int(pipe_fds_[1]); + g_timeout_add( + static_cast(flutter_duration / 1000000.0), + [](gpointer data) -> gboolean { + int* fd = static_cast(data); + if (*fd >= 0) { + char c = 1; + write(*fd, &c, 1); } - return ECORE_CALLBACK_CANCEL; + delete fd; + return G_SOURCE_REMOVE; }, - this); + pipe_write_fd); } else { - if (ecore_pipe_) { - ecore_pipe_write(ecore_pipe_, nullptr, 0); + if (pipe_fds_[1] >= 0) { + char c = 1; + write(pipe_fds_[1], &c, 1); } } } diff --git a/flutter/shell/platform/tizen/tizen_event_loop.h b/flutter/shell/platform/tizen/tizen_event_loop.h index bf7817e6..83911975 100644 --- a/flutter/shell/platform/tizen/tizen_event_loop.h +++ b/flutter/shell/platform/tizen/tizen_event_loop.h @@ -6,7 +6,7 @@ #ifndef EMBEDDER_TIZEN_EVENT_LOOP_H_ #define EMBEDDER_TIZEN_EVENT_LOOP_H_ -#include +#include #include #include @@ -73,7 +73,9 @@ class TizenEventLoop { std::atomic task_order_ = 0; private: - Ecore_Pipe* ecore_pipe_ = nullptr; + int pipe_fds_[2] = {-1, -1}; + GIOChannel* pipe_channel_ = nullptr; + guint pipe_watch_id_ = 0; // Returns a TaskTimePoint computed from the given target time from Flutter. TaskTimePoint TimePointFromFlutterTime(uint64_t flutter_target_time_nanos); diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc new file mode 100644 index 00000000..aa25a520 --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -0,0 +1,435 @@ +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tizen_input_method_context_tcore.h" + +#include + +#include "flutter/shell/platform/tizen/logger.h" + +namespace { + +tizen_core_imf_input_panel_layout_e TextInputTypeToImfInputPanelLayout( + const std::string& text_input_type) { + if (text_input_type == "TextInputType.text" || + text_input_type == "TextInputType.multiline") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_NORMAL; + } else if (text_input_type == "TextInputType.number") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_NUMBERONLY; + } else if (text_input_type == "TextInputType.phone") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_PHONENUMBER; + } else if (text_input_type == "TextInputType.datetime") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_DATETIME; + } else if (text_input_type == "TextInputType.emailAddress" || + text_input_type == "TextInputType.twitter") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_EMAIL; + } else if (text_input_type == "TextInputType.url" || + text_input_type == "TextInputType.webSearch") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_URL; + } else if (text_input_type == "TextInputType.visiblePassword") { + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_PASSWORD; + } else { + FT_LOG(Warn) << "The requested input type " << text_input_type + << " is not supported."; + return TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_NORMAL; + } +} + +tizen_core_imf_keyboard_modifiers_e ModifiersToImfModifiers( + unsigned int modifiers) { + unsigned int imf_modifiers = TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_NONE; + if (modifiers & TIZEN_CORE_WL_MODIFIER_SHIFT) { + imf_modifiers |= TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_SHIFT; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_ALT) { + imf_modifiers |= TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_ALT; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_CTRL) { + imf_modifiers |= TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_CTRL; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_WIN) { + imf_modifiers |= TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_WIN; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_ALTGR) { + imf_modifiers |= TIZEN_CORE_IMF_KEYBOARD_MODIFIERS_ALTGR; + } + return static_cast(imf_modifiers); +} + +tizen_core_imf_keyboard_locks_e ModifiersToImfLocks(unsigned int modifiers) { + unsigned int locks = TIZEN_CORE_IMF_KEYBOARD_LOCKS_NONE; + if (modifiers & TIZEN_CORE_WL_MODIFIER_NUM) { + locks |= TIZEN_CORE_IMF_KEYBOARD_LOCKS_NUM; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_CAPS) { + locks |= TIZEN_CORE_IMF_KEYBOARD_LOCKS_CAPS; + } + if (modifiers & TIZEN_CORE_WL_MODIFIER_SCROLL) { + locks |= TIZEN_CORE_IMF_KEYBOARD_LOCKS_SCROLL; + } + return static_cast(locks); +} + +tizen_core_imf_event_key_h CreateImfKeyEventFromTcoreWlEvent(void* event) { + auto* ev = static_cast(event); + + tizen_core_imf_event_key_h imf_key = nullptr; + tizen_core_imf_event_key_create(&imf_key); + if (!imf_key) { + return nullptr; + } + + char* keyname = nullptr; + tizen_core_wl_event_key_get_keyname(ev, &keyname); + if (keyname) { + tizen_core_imf_event_key_set_keyname(imf_key, keyname); + tizen_core_imf_event_key_set_key(imf_key, keyname); + free(keyname); + } + + char* keysymbol = nullptr; + tizen_core_wl_event_key_get_keysymbol(ev, &keysymbol); + if (keysymbol) { + tizen_core_imf_event_key_set_string(imf_key, keysymbol); + free(keysymbol); + } + + char* compose = nullptr; + tizen_core_wl_event_key_get_compose(ev, &compose); + if (compose) { + tizen_core_imf_event_key_set_compose(imf_key, compose); + free(compose); + } + + unsigned int keycode = 0; + tizen_core_wl_event_key_get_keycode(ev, &keycode); + tizen_core_imf_event_key_set_keycode(imf_key, keycode); + + unsigned int modifiers = 0; + tizen_core_wl_event_key_get_modifiers(ev, &modifiers); + tizen_core_imf_event_key_set_modifiers(imf_key, + ModifiersToImfModifiers(modifiers)); + tizen_core_imf_event_key_set_locks(imf_key, ModifiersToImfLocks(modifiers)); + + uint32_t timestamp = 0; + tizen_core_wl_event_input_base_get_timestamp(ev, ×tamp); + tizen_core_imf_event_key_set_timestamp(imf_key, timestamp); + + char* dev_identifier = nullptr; + tizen_core_wl_event_input_base_get_device_identifier(ev, &dev_identifier); + if (dev_identifier) { + tizen_core_imf_event_key_set_device_name(imf_key, dev_identifier); + free(dev_identifier); + } + + return imf_key; +} + +} // namespace + +namespace flutter { + +TizenInputMethodContext::TizenInputMethodContext(uintptr_t window_id) { + tizen_core_imf_init(); + + if (tizen_core_imf_context_create(&imf_context_) != + TIZEN_CORE_IMF_ERROR_NONE) { + FT_LOG(Error) << "Failed to create tizen_core_imf_context."; + return; + } + + tizen_core_imf_context_set_client_window(imf_context_, + reinterpret_cast(window_id)); + SetContextOptions(); + SetInputPanelOptions(); + RegisterEventCallbacks(); + RegisterInputPanelEventCallback(); +} + +TizenInputMethodContext::~TizenInputMethodContext() { + UnregisterInputPanelEventCallback(); + UnregisterEventCallbacks(); + + if (imf_context_) { + tizen_core_imf_context_destroy(imf_context_); + } + + tizen_core_imf_shutdown(); +} + +bool TizenInputMethodContext::HandleTcoreWlEventKey(void* event, bool is_down) { + FT_ASSERT(imf_context_); + FT_ASSERT(event); + + tizen_core_imf_event_key_h imf_key = CreateImfKeyEventFromTcoreWlEvent(event); + if (!imf_key) { + return false; + } + + bool filter_result = false; + tizen_core_imf_context_filter_event(imf_context_, + is_down + ? TIZEN_CORE_IMF_EVENT_TYPE_KEY_DOWN + : TIZEN_CORE_IMF_EVENT_TYPE_KEY_UP, + imf_key, &filter_result); + tizen_core_imf_event_key_destroy(imf_key); + return filter_result; +} + +#ifdef NUI_SUPPORT +bool TizenInputMethodContext::HandleNuiKeyEvent(const char* device_name, + uint32_t device_class, + uint32_t device_subclass, + const char* key, + const char* string, + uint32_t modifiers, + uint32_t scan_code, + size_t timestamp, + bool is_down) { + tizen_core_imf_event_key_h imf_key = nullptr; + tizen_core_imf_event_key_create(&imf_key); + if (!imf_key) { + return false; + } + + if (key) { + tizen_core_imf_event_key_set_keyname(imf_key, key); + tizen_core_imf_event_key_set_key(imf_key, key); + } + if (string) { + tizen_core_imf_event_key_set_string(imf_key, string); + } + + tizen_core_imf_event_key_set_modifiers(imf_key, + ModifiersToImfModifiers(modifiers)); + tizen_core_imf_event_key_set_locks(imf_key, ModifiersToImfLocks(modifiers)); + tizen_core_imf_event_key_set_keycode(imf_key, scan_code); + tizen_core_imf_event_key_set_timestamp(imf_key, timestamp); + + if (device_name) { + tizen_core_imf_event_key_set_device_name(imf_key, device_name); + } + tizen_core_imf_event_key_set_device_class( + imf_key, static_cast(device_class)); + tizen_core_imf_event_key_set_device_subclass( + imf_key, static_cast(device_subclass)); + + bool filter_result = false; + tizen_core_imf_context_filter_event(imf_context_, + is_down + ? TIZEN_CORE_IMF_EVENT_TYPE_KEY_DOWN + : TIZEN_CORE_IMF_EVENT_TYPE_KEY_UP, + imf_key, &filter_result); + tizen_core_imf_event_key_destroy(imf_key); + return filter_result; +} +#endif + +InputPanelGeometry TizenInputMethodContext::GetInputPanelGeometry() { + FT_ASSERT(imf_context_); + InputPanelGeometry geometry; + tizen_core_imf_context_get_input_panel_geometry( + imf_context_, &geometry.x, &geometry.y, &geometry.w, &geometry.h); + return geometry; +} + +void TizenInputMethodContext::ResetInputMethodContext() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_reset(imf_context_); +} + +void TizenInputMethodContext::ShowInputPanel() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_input_panel_show(imf_context_); + tizen_core_imf_context_focus_in(imf_context_); +} + +void TizenInputMethodContext::HideInputPanel() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_focus_out(imf_context_); + tizen_core_imf_context_input_panel_hide(imf_context_); +} + +bool TizenInputMethodContext::IsInputPanelShown() { + tizen_core_imf_input_panel_state_e state; + tizen_core_imf_context_get_input_panel_state(imf_context_, &state); + return state == TIZEN_CORE_IMF_INPUT_PANEL_STATE_SHOW; +} + +void TizenInputMethodContext::SetInputPanelLayout( + const std::string& input_type) { + FT_ASSERT(imf_context_); + tizen_core_imf_input_panel_layout_e panel_layout = + TextInputTypeToImfInputPanelLayout(input_type); + tizen_core_imf_context_set_input_panel_layout(imf_context_, panel_layout); +} + +void TizenInputMethodContext::SetInputPanelLayoutVariation(bool is_signed, + bool is_decimal) { + tizen_core_imf_layout_numberonly_variation_e variation; + if (is_signed && is_decimal) { + variation = TIZEN_CORE_IMF_LAYOUT_NUMBERONLY_VARIATION_SIGNED_AND_DECIMAL; + } else if (is_signed) { + variation = TIZEN_CORE_IMF_LAYOUT_NUMBERONLY_VARIATION_SIGNED; + } else if (is_decimal) { + variation = TIZEN_CORE_IMF_LAYOUT_NUMBERONLY_VARIATION_DECIMAL; + } else { + variation = TIZEN_CORE_IMF_LAYOUT_NUMBERONLY_VARIATION_NORMAL; + } + tizen_core_imf_context_set_input_panel_layout_variation(imf_context_, + variation); +} + +void TizenInputMethodContext::SetAutocapitalType(const std::string& type) { + tizen_core_imf_autocapital_type_e autocapital_type = + TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_NONE; + + if (type == "TextCapitalization.characters") { + autocapital_type = TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_ALLCHARACTER; + } else if (type == "TextCapitalization.words") { + autocapital_type = TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_WORD; + } else if (type == "TextCapitalization.sentences") { + autocapital_type = TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_SENTENCE; + } else if (type == "TextCapitalization.none") { + autocapital_type = TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_NONE; + } + tizen_core_imf_context_set_autocapital_type(imf_context_, autocapital_type); +} + +void TizenInputMethodContext::RegisterEventCallbacks() { + FT_ASSERT(imf_context_); + + // commit callback + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_COMMIT] = + [](tizen_core_imf_context_h ctx, void* event_info, void* data) { + auto* self = static_cast(data); + char* str = static_cast(event_info); + if (self->on_commit_) { + self->on_commit_(str); + } + }; + tizen_core_imf_context_add_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_COMMIT, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_COMMIT], this); + + // pre-edit start callback + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_START] = + [](tizen_core_imf_context_h ctx, void* event_info, void* data) { + auto* self = static_cast(data); + if (self->on_preedit_start_) { + self->on_preedit_start_(); + } + }; + tizen_core_imf_context_add_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_START, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_START], this); + + // pre-edit end callback + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_END] = + [](tizen_core_imf_context_h ctx, void* event_info, void* data) { + auto* self = static_cast(data); + if (self->on_preedit_end_) { + self->on_preedit_end_(); + } + }; + tizen_core_imf_context_add_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_END, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_END], this); + + // pre-edit changed callback + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED] = + [](tizen_core_imf_context_h ctx, void* event_info, void* data) { + auto* self = static_cast(data); + if (self->on_preedit_changed_) { + char* str = nullptr; + int cursor_pos = 0; + tizen_core_imf_context_get_preedit_string(ctx, &str, nullptr, nullptr, + &cursor_pos); + if (str) { + self->on_preedit_changed_(str, cursor_pos); + free(str); + } + } + }; + tizen_core_imf_context_add_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED], this); +} + +void TizenInputMethodContext::UnregisterEventCallbacks() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_del_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_COMMIT, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_COMMIT]); + tizen_core_imf_context_del_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED]); + tizen_core_imf_context_del_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_START, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_START]); + tizen_core_imf_context_del_event_callback( + imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_END, + event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_END]); +} + +void TizenInputMethodContext::SetContextOptions() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_set_autocapital_type( + imf_context_, TIZEN_CORE_IMF_AUTOCAPITAL_TYPE_NONE); +} + +void TizenInputMethodContext::SetInputPanelOptions() { + FT_ASSERT(imf_context_); + tizen_core_imf_context_set_input_panel_layout( + imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_LAYOUT_NORMAL); + tizen_core_imf_context_set_input_panel_return_key_type( + imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_RETURN_KEY_TYPE_DEFAULT); +} + +void TizenInputMethodContext::InputPanelStateChangedCallback( + tizen_core_imf_context_h ctx, + int value, + void* data) { + auto* self = static_cast(data); + tizen_core_imf_input_panel_state_e state = + static_cast(value); + + std::string state_str; + switch (state) { + case TIZEN_CORE_IMF_INPUT_PANEL_STATE_SHOW: + state_str = "show"; + break; + case TIZEN_CORE_IMF_INPUT_PANEL_STATE_HIDE: + state_str = "hide"; + break; + case TIZEN_CORE_IMF_INPUT_PANEL_STATE_WILL_SHOW: + state_str = "will_show"; + break; + default: + state_str = "unknown"; + break; + } + + if (self->on_input_panel_state_changed_) { + self->on_input_panel_state_changed_(state_str); + } +} + +void TizenInputMethodContext::RegisterInputPanelEventCallback() { + FT_ASSERT(imf_context_); + + tizen_core_imf_context_add_input_panel_event_callback( + imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_EVENT_STATE, + InputPanelStateChangedCallback, this); +} + +void TizenInputMethodContext::UnregisterInputPanelEventCallback() { + FT_ASSERT(imf_context_); + + tizen_core_imf_context_del_input_panel_event_callback( + imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_EVENT_STATE, + InputPanelStateChangedCallback); +} + +} // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.h b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.h new file mode 100644 index 00000000..c62dd20a --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.h @@ -0,0 +1,103 @@ +// Copyright 2021 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EMBEDDER_TIZEN_INPUT_METHOD_CONTEXT_H_ +#define EMBEDDER_TIZEN_INPUT_METHOD_CONTEXT_H_ + +#include + +#include +#include +#include + +namespace flutter { + +using OnCommit = std::function; +using OnPreeditChanged = std::function; +using OnPreeditStart = std::function; +using OnPreeditEnd = std::function; +using OnInputPanelStateChanged = std::function; + +struct InputPanelGeometry { + int32_t x = 0, y = 0, w = 0, h = 0; +}; + +class TizenInputMethodContext { + public: + TizenInputMethodContext(uintptr_t window_id); + ~TizenInputMethodContext(); + + bool HandleTcoreWlEventKey(void* event, bool is_down); + +#ifdef NUI_SUPPORT + bool HandleNuiKeyEvent(const char* device_name, + uint32_t device_class, + uint32_t device_subclass, + const char* key, + const char* string, + uint32_t modifiers, + uint32_t scan_code, + size_t timestamp, + bool is_down); +#endif + + InputPanelGeometry GetInputPanelGeometry(); + + void ResetInputMethodContext(); + + void ShowInputPanel(); + + void HideInputPanel(); + + bool IsInputPanelShown(); + + void SetInputPanelLayout(const std::string& layout); + + void SetInputPanelLayoutVariation(bool is_signed, bool is_decimal); + + void SetAutocapitalType(const std::string& type); + + void SetOnCommit(OnCommit callback) { on_commit_ = callback; } + + void SetOnPreeditChanged(OnPreeditChanged callback) { + on_preedit_changed_ = callback; + } + + void SetOnPreeditStart(OnPreeditStart callback) { + on_preedit_start_ = callback; + } + + void SetOnPreeditEnd(OnPreeditEnd callback) { on_preedit_end_ = callback; } + + void SetOnInputPanelStateChanged(OnInputPanelStateChanged callback) { + on_input_panel_state_changed_ = callback; + } + + void RegisterInputPanelEventCallback(); + void UnregisterInputPanelEventCallback(); + + private: + static void InputPanelStateChangedCallback(tizen_core_imf_context_h ctx, + int value, + void* data); + + void RegisterEventCallbacks(); + void UnregisterEventCallbacks(); + + void SetContextOptions(); + void SetInputPanelOptions(); + + tizen_core_imf_context_h imf_context_ = nullptr; + OnCommit on_commit_; + OnPreeditChanged on_preedit_changed_; + OnPreeditStart on_preedit_start_; + OnPreeditEnd on_preedit_end_; + OnInputPanelStateChanged on_input_panel_state_changed_; + std::unordered_map + event_callbacks_; +}; + +} // namespace flutter + +#endif // EMBEDDER_TIZEN_INPUT_METHOD_CONTEXT_H_ diff --git a/flutter/shell/platform/tizen/tizen_renderer_egl.cc b/flutter/shell/platform/tizen/tizen_renderer_egl.cc index d618943c..53a622cd 100644 --- a/flutter/shell/platform/tizen/tizen_renderer_egl.cc +++ b/flutter/shell/platform/tizen/tizen_renderer_egl.cc @@ -4,8 +4,12 @@ #include "flutter/shell/platform/tizen/tizen_renderer_egl.h" +#ifdef USE_TCORE_WL +#include +#else #define EFL_BETA_API_SUPPORT #include +#endif #include #include #ifdef NUI_SUPPORT @@ -94,8 +98,13 @@ bool TizenRendererEgl::CreateSurface(void* render_target, const EGLint attribs[] = {EGL_NONE}; if (render_target_display) { +#ifdef USE_TCORE_WL + void* egl_window = tizen_core_wl_egl_window_native_get( + static_cast(render_target)); +#else const auto egl_window = ecore_wl2_egl_window_native_get( static_cast(render_target)); +#endif egl_surface_ = eglCreateWindowSurface( egl_display_, egl_config_, reinterpret_cast(egl_window), attribs); diff --git a/flutter/shell/platform/tizen/tizen_view_base.h b/flutter/shell/platform/tizen/tizen_view_base.h index 40a1b8f9..9354dd4a 100644 --- a/flutter/shell/platform/tizen/tizen_view_base.h +++ b/flutter/shell/platform/tizen/tizen_view_base.h @@ -10,7 +10,11 @@ #include #include +#ifdef USE_TCORE_WL +#include "flutter/shell/platform/tizen/tizen_input_method_context_tcore.h" +#else #include "flutter/shell/platform/tizen/tizen_input_method_context.h" +#endif #include "flutter/shell/platform/tizen/tizen_view_event_handler_delegate.h" namespace flutter { diff --git a/flutter/shell/platform/tizen/tizen_window_tcore_wl.cc b/flutter/shell/platform/tizen/tizen_window_tcore_wl.cc new file mode 100644 index 00000000..b2744e13 --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_window_tcore_wl.cc @@ -0,0 +1,968 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "tizen_window_tcore_wl.h" + +#ifdef TV_PROFILE +#include +#include +#include +#include +#include +#include +#endif + +#include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/tizen/logger.h" +#include "flutter/shell/platform/tizen/tizen_view_event_handler_delegate.h" + +// Forward declarations for rotation APIs that exist in the library +// but are not yet declared in the public header. +extern "C" { +tizen_core_wl_error_e tizen_core_wl_window_set_rotation_angle( + tizen_core_wl_window_h window, + tizen_core_wl_window_angle_e angle); +tizen_core_wl_error_e tizen_core_wl_window_get_rotation_angle( + tizen_core_wl_window_h window, + tizen_core_wl_window_angle_e* angle); +tizen_core_wl_error_e tizen_core_wl_window_set_available_rotation_angle_list( + tizen_core_wl_window_h window, + tizen_core_wl_window_angle_e* available_angles, + size_t available_angle_count); +} + +namespace flutter { + +namespace { + +constexpr int kScrollDirectionVertical = 0; +constexpr int kScrollDirectionHorizontal = 1; + +#ifdef TV_PROFILE +constexpr char kSysMouseCursorPointerSizeVConfKey[] = + "db/menu/system/mouse-pointer-size"; +constexpr char kSysPointingDeviceSupportToastSharedPreferenceKey[] = + "flutter-tizen/preference/pointing-device-support-toast"; +constexpr char kTcoreWlInputCursorThemeName[] = "vd-cursors"; +#endif + +FlutterPointerMouseButtons ToFlutterPointerButton(int32_t button) { + if (button == 2) { + return kFlutterPointerButtonMouseMiddle; + } else if (button == 3) { + return kFlutterPointerButtonMouseSecondary; + } else { + return kFlutterPointerButtonMousePrimary; + } +} + +// FlutterPointerDeviceKind GetDeviceKindFromEventType(int event_type) { +// if (event_type == TIZEN_CORE_WL_EVENT_MOUSE_BUTTON_DOWN || +// event_type == TIZEN_CORE_WL_EVENT_MOUSE_BUTTON_UP || +// event_type == TIZEN_CORE_WL_EVENT_MOUSE_MOVE || +// event_type == TIZEN_CORE_WL_EVENT_MOUSE_WHEEL) { +// // The event system doesn't distinguish mouse vs touch at event level. +// // Default to touch; mouse events can be differentiated by touch_id == 0. +// return kFlutterPointerDeviceKindTouch; +// } +// return kFlutterPointerDeviceKindTouch; +// } + +#ifdef TV_PROFILE +time_t GetBootTimeEpoch() { + struct timespec now, boot_time; + if (clock_gettime(CLOCK_REALTIME, &now) != 0) { + FT_LOG(Error) << "Fail to get clock_gettime(CLOCK_REALTIME)."; + return -1; + } + if (clock_gettime(CLOCK_BOOTTIME, &boot_time) != 0) { + FT_LOG(Error) << "Fail to get clock_gettime(CLOCK_BOOTTIME)."; + return -1; + } + time_t boot_time_epoch = now.tv_sec - boot_time.tv_sec; + return (boot_time_epoch / 10) * 10; +} + +bool PreferenceItemCallback(const char* key, void* user_data) { + char* app_id = (char*)user_data; + if (!app_id || !key) { + return true; + } + + std::string preference_key = + std::string(kSysPointingDeviceSupportToastSharedPreferenceKey) + "/" + + app_id; + if (!strncmp(key, preference_key.c_str(), preference_key.length())) { + preference_remove(key); + } + return true; +} + +std::string GetPreferenceKey(bool clear_exist_key) { + time_t boot_time = GetBootTimeEpoch(); + if (boot_time == -1) { + return ""; + } + + char* id = nullptr; + int ret = app_get_id(&id); + if (ret != APP_CONTROL_ERROR_NONE || !id) { + FT_LOG(Error) << "Fail to get app id."; + return std::string(); + } + + std::string app_id = id; + free(id); + + std::ostringstream boot_time_buffer; + boot_time_buffer << boot_time; + + std::string preference_key = + std::string(kSysPointingDeviceSupportToastSharedPreferenceKey) + "/" + + app_id + "/" + boot_time_buffer.str(); + + if (clear_exist_key) { + preference_foreach_item(PreferenceItemCallback, (void*)app_id.c_str()); + } + return preference_key; +} + +bool GetPointingDeviceToastPreference() { + bool show_unsupported_toast = false; + std::string preference_key = GetPreferenceKey(false); + if (preference_key.empty()) { + return false; + } + + int ret = + preference_get_boolean(preference_key.c_str(), &show_unsupported_toast); + if (ret != PREFERENCE_ERROR_NONE) { + return false; + } + return show_unsupported_toast; +} + +void SetPointingDevicePreference() { + std::string preference_key = GetPreferenceKey(true); + if (preference_key.empty()) { + return; + } + + int ret = preference_set_boolean(preference_key.c_str(), true); + if (ret != PREFERENCE_ERROR_NONE) { + FT_LOG(Error) << "Fail to set toasted preference."; + } +} +#endif + +} // namespace + +TizenWindowTcoreWl::TizenWindowTcoreWl(TizenGeometry geometry, + bool transparent, + bool focusable, + bool top_level, + bool pointing_device_support, + bool floating_menu_support, + void* window_handle = nullptr, + bool is_vulkan = false) + : TizenWindow(geometry, transparent, focusable, top_level) +#ifdef TV_PROFILE + , + pointing_device_support_(pointing_device_support), + floating_menu_support_(floating_menu_support) +#endif + , + is_vulkan_(is_vulkan) { + if (!CreateWindow(window_handle)) { + FT_LOG(Error) << "Failed to create a platform window."; + return; + } + + SetWindowOptions(); + RegisterEventHandlers(); + PrepareInputMethod(); + Show(); +} + +TizenWindowTcoreWl::~TizenWindowTcoreWl() { + UnregisterEventHandlers(); + DestroyWindow(); +} + +bool TizenWindowTcoreWl::CreateWindow(void* window_handle) { + if (tizen_core_wl_init() != TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Could not initialize tizen core wl."; + return false; + } + + if (tizen_core_wl_display_create(&tcore_wl_display_) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Could not create tizen core wl display."; + return false; + } + + if (tizen_core_wl_display_connect(tcore_wl_display_, nullptr) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Tizen core wl display not found."; + return false; + } + + tizen_core_wl_display_private_get_wl_display(tcore_wl_display_, + &wl2_display_); + + if (tizen_core_wl_display_sync(tcore_wl_display_) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Failed to sync tizen core wl display."; + return false; + } + + if (tizen_core_wl_display_get_event(tcore_wl_display_, &tcore_wl_event_) != + TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Could not get tizen core wl event handle."; + return false; + } + + int32_t width = 0, height = 0; + GList* output_list = nullptr; + tizen_core_wl_display_get_output_device_list(tcore_wl_display_, &output_list); + if (output_list) { + tizen_core_wl_output_h output = + static_cast(output_list->data); + tizen_core_wl_output_device_get_geometry(output, &width, &height); + g_list_free(output_list); + } + if (width == 0 || height == 0) { + FT_LOG(Error) << "Invalid screen size: " << width << " x " << height; + return false; + } + + if (initial_geometry_.width == 0) { + initial_geometry_.width = width; + } + if (initial_geometry_.height == 0) { + initial_geometry_.height = height; + } + + if (window_handle == nullptr) { + if (tizen_core_wl_create_window( + tcore_wl_display_, nullptr, initial_geometry_.left, + initial_geometry_.top, initial_geometry_.width, + initial_geometry_.height, + &tcore_wl_window_) != TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Could not create tizen core wl window."; + return false; + } + } else { + tcore_wl_window_ = static_cast(window_handle); + } + + if (is_vulkan_) { + tizen_core_wl_window_private_get_wl_surface(tcore_wl_window_, + &wl2_surface_); + return wl2_surface_ && wl2_display_; + } else { + if (tizen_core_wl_create_egl_window( + tcore_wl_window_, initial_geometry_.width, initial_geometry_.height, + &tcore_wl_egl_window_) != TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Could not create tizen core wl egl window."; + return false; + } + return tcore_wl_egl_window_ && wl2_display_; + } +} + +void TizenWindowTcoreWl::SetWindowOptions() { + tizen_core_wl_window_set_type( + tcore_wl_window_, top_level_ ? TIZEN_CORE_WL_WINDOW_TYPE_NOTIFICATION + : TIZEN_CORE_WL_WINDOW_TYPE_TOPLEVEL); + if (top_level_) { + SetNotificationLevel(TIZEN_CORE_WL_NOTIFICATION_LEVEL_TOP); + } + + tizen_core_wl_window_set_position(tcore_wl_window_, initial_geometry_.left, + initial_geometry_.top); + tizen_core_wl_window_set_aux_hint(tcore_wl_window_, + "wm.policy.win.user.geometry", "1"); + + tizen_core_wl_window_set_alpha(tcore_wl_window_, transparent_); + + if (!focusable_) { + tizen_core_wl_window_set_focus_skip(tcore_wl_window_, true); + } + +#ifdef TV_PROFILE + tizen_core_wl_window_angle_e rotations[1] = {TIZEN_CORE_WL_WINDOW_ANGLE_0}; +#else + tizen_core_wl_window_angle_e rotations[4] = { + TIZEN_CORE_WL_WINDOW_ANGLE_0, TIZEN_CORE_WL_WINDOW_ANGLE_90, + TIZEN_CORE_WL_WINDOW_ANGLE_180, TIZEN_CORE_WL_WINDOW_ANGLE_270}; +#endif + tizen_core_wl_window_set_available_rotation_angle_list( + tcore_wl_window_, rotations, sizeof(rotations) / sizeof(rotations[0])); + EnableCursor(); +} + +// NOTE: The TV profile cursor / mouse-pointer / floating-menu helpers below +// depend on libvd-win-util.so, which is an internal Samsung TV library and +// not part of the public Tizen API. Symbols are looked up via dlopen/dlsym +// so the embedder still builds on devices where the library is missing, but +// these symbols may be renamed or removed in future TV platform releases. +// All call sites must handle the "library or symbol not available" case +// gracefully (by bailing out and leaving the corresponding feature disabled). +void TizenWindowTcoreWl::EnableCursor() { +#ifdef TV_PROFILE + void* handle = dlopen("libvd-win-util.so", RTLD_LAZY); + if (!handle) { + FT_LOG(Error) << "Could not open a shared library libvd-win-util.so."; + return; + } + + int (*CursorModule_Initialize)(wl_display* display, wl_registry* registry, + wl_seat* seat, unsigned int id); + int (*Cursor_Set_Config)(wl_surface* surface, uint32_t config_type, + void* data); + void (*CursorModule_Finalize)(void); + *(void**)(&CursorModule_Initialize) = + dlsym(handle, "CursorModule_Initialize"); + *(void**)(&Cursor_Set_Config) = dlsym(handle, "Cursor_Set_Config"); + *(void**)(&CursorModule_Finalize) = dlsym(handle, "CursorModule_Finalize"); + + if (!CursorModule_Initialize || !Cursor_Set_Config || + !CursorModule_Finalize) { + FT_LOG(Error) << "Could not load symbols from the library."; + dlclose(handle); + return; + } + + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(tcore_wl_display_, &default_seat); + if (!default_seat) { + FT_LOG(Error) << "Could not get default seat."; + dlclose(handle); + return; + } + struct wl_seat* seat = nullptr; + tizen_core_wl_seat_private_get_wl_seat(default_seat, &seat); + if (!seat) { + FT_LOG(Error) << "Could not get wl_seat from the default seat."; + dlclose(handle); + return; + } + + int cursor_global_id = 0; + if (tizen_core_wl_display_private_get_global_id( + tcore_wl_display_, "tizen_cursor", &cursor_global_id) == + TIZEN_CORE_WL_ERROR_NONE && + cursor_global_id > 0) { + struct wl_registry* registry = wl_display_get_registry(wl2_display_); + if (registry) { + if (!CursorModule_Initialize(wl2_display_, registry, seat, + cursor_global_id)) { + FT_LOG(Error) << "Failed to initialize the cursor module."; + } + wl_registry_destroy(registry); + } + } + + tizen_core_wl_display_sync(tcore_wl_display_); + + struct wl_surface* surface = nullptr; + tizen_core_wl_window_private_get_wl_surface(tcore_wl_window_, &surface); + if (surface && !Cursor_Set_Config(surface, 1, nullptr)) { + FT_LOG(Error) << "Failed to set a cursor config value."; + } + + CursorModule_Finalize(); + dlclose(handle); +#endif +} + +#ifdef TV_PROFILE +typedef enum _MouseSupport { DISABLE = 0, ENABLE } MouseSupport; +typedef enum _Device_Type { MOUSE_DEVICE = 3, TOUCH_DEVICE } Device_Type; + +void TizenWindowTcoreWl::SetPointingDeviceSupport() { + void* handle = dlopen("libvd-win-util.so", RTLD_LAZY); + if (!handle) { + FT_LOG(Error) << "Could not open a shared library libvd-win-util.so."; + return; + } + + int (*Mouse_Pointer_Support)(MouseSupport type, void* win); + *(void**)(&Mouse_Pointer_Support) = dlsym(handle, "Mouse_Pointer_Support"); + + if (!Mouse_Pointer_Support) { + FT_LOG(Error) << "Could not load symbols from the library."; + dlclose(handle); + return; + } + + Mouse_Pointer_Support(pointing_device_support_ ? ENABLE : DISABLE, + tcore_wl_window_); + dlclose(handle); +} + +void TizenWindowTcoreWl::SetFloatingMenuSupport() { + void* handle = dlopen("libvd-win-util.so", RTLD_LAZY); + if (!handle) { + FT_LOG(Error) << "Could not open a shared library libvd-win-util.so."; + return; + } + + int (*Mouse_Pointer_Not_Allow)(int enable, void* win); + *(void**)(&Mouse_Pointer_Not_Allow) = + dlsym(handle, "Mouse_Pointer_Not_Allow"); + + if (!Mouse_Pointer_Not_Allow) { + FT_LOG(Error) << "Could not load symbols from the library."; + dlclose(handle); + return; + } + + Mouse_Pointer_Not_Allow(!floating_menu_support_, tcore_wl_window_); + dlclose(handle); +} + +void TizenWindowTcoreWl::ShowUnsupportedToast() { + void* handle = dlopen("libvd-win-util.so", RTLD_LAZY); + if (!handle) { + FT_LOG(Error) << "Could not open a shared library libvd-win-util.so."; + return; + } + + void (*Unsupported_Toast_Launch)(Device_Type type, int show, int enable, + void* win); + *(void**)(&Unsupported_Toast_Launch) = + dlsym(handle, "Unsupported_Toast_Launch"); + + if (!Unsupported_Toast_Launch) { + FT_LOG(Error) << "Could not load symbols from the library."; + dlclose(handle); + return; + } + + Unsupported_Toast_Launch(MOUSE_DEVICE, 1, 1, tcore_wl_window_); + dlclose(handle); +} +#endif + +void TizenWindowTcoreWl::AddEventListener(tizen_core_wl_event_type_e type, + tizen_core_wl_event_cb callback) { + tizen_core_wl_event_listener_h listener = nullptr; + if (tizen_core_wl_event_add_listener(tcore_wl_event_, type, callback, this, + &listener) != TIZEN_CORE_WL_ERROR_NONE) { + FT_LOG(Error) << "Failed to add tizen core wl event listener."; + return; + } + tcore_event_listeners_.push_back(listener); +} + +void TizenWindowTcoreWl::RegisterEventHandlers() { + // Window rotation event. + AddEventListener( + TIZEN_CORE_WL_EVENT_WINDOW_ROTATION, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* base_event = + static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_window_base_get_window(base_event, &event_window); + if (event_window == self->tcore_wl_window_) { + tizen_core_wl_event_window_rotation_h rot_event = nullptr; + tizen_core_wl_event_window_base_to_window_rotation(base_event, + &rot_event); + tizen_core_wl_window_angle_e angle = TIZEN_CORE_WL_WINDOW_ANGLE_0; + tizen_core_wl_event_window_rotation_get_angle(rot_event, &angle); + int degree = static_cast(angle); + self->view_delegate_->OnRotate(degree); + tizen_core_wl_window_set_rotation_angle( + self->tcore_wl_window_, + static_cast(degree)); + tizen_core_wl_window_send_rotation_change_done( + self->tcore_wl_window_, degree); + } + } + }); + + // Window configure event. + if (!is_vulkan_) { + AddEventListener( + TIZEN_CORE_WL_EVENT_WINDOW_CONFIGURE_COMPLETE, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* base_event = + static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_window_base_get_window(base_event, + &event_window); + if (event_window == self->tcore_wl_window_) { + int x = 0, y = 0, w = 0, h = 0; + tizen_core_wl_window_get_geometry(self->tcore_wl_window_, &x, &y, + &w, &h); + int32_t rotation = self->GetRotation(); + tizen_core_wl_egl_window_resize(self->tcore_wl_egl_window_, w, h); + tizen_core_wl_egl_window_set_window_transform( + self->tcore_wl_egl_window_, rotation / 90); + self->view_delegate_->OnResize(x, y, w, h); + } + } + }); + } + + // Mouse button down. + AddEventListener( + TIZEN_CORE_WL_EVENT_MOUSE_BUTTON_DOWN, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); +#ifdef TV_PROFILE + if ((!self->pointing_device_support_ || + !self->floating_menu_support_) && + !self->show_unsupported_toast_) { + bool shown = GetPointingDeviceToastPreference(); + if (!self->floating_menu_support_) { + self->SetFloatingMenuSupport(); + } + if (!shown) { + self->ShowUnsupportedToast(); + SetPointingDevicePreference(); + } + self->show_unsupported_toast_ = true; + if (self->floating_menu_support_ && !self->pointing_device_support_) { + self->SetPointingDeviceSupport(); + } + return; + } +#endif + if (self->view_delegate_) { + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window == self->tcore_wl_window_) { + int x = 0, y = 0; + tizen_core_wl_event_mouse_button_get_position(ev, &x, &y); + unsigned int buttons = 0; + tizen_core_wl_event_mouse_button_get_buttons(ev, &buttons); + uint32_t timestamp = 0; + tizen_core_wl_event_input_base_get_timestamp(ev, ×tamp); + unsigned int touch_id = 0; + tizen_core_wl_event_mouse_button_get_touch_id(ev, &touch_id); + self->view_delegate_->OnPointerDown( + x, y, ToFlutterPointerButton(buttons), timestamp, + touch_id == 0 ? kFlutterPointerDeviceKindMouse + : kFlutterPointerDeviceKindTouch, + touch_id); + } + } + }); + + // Mouse button up. + AddEventListener( + TIZEN_CORE_WL_EVENT_MOUSE_BUTTON_UP, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window == self->tcore_wl_window_) { + int x = 0, y = 0; + tizen_core_wl_event_mouse_button_get_position(ev, &x, &y); + unsigned int buttons = 0; + tizen_core_wl_event_mouse_button_get_buttons(ev, &buttons); + uint32_t timestamp = 0; + tizen_core_wl_event_input_base_get_timestamp(ev, ×tamp); + unsigned int touch_id = 0; + tizen_core_wl_event_mouse_button_get_touch_id(ev, &touch_id); + self->view_delegate_->OnPointerUp( + x, y, ToFlutterPointerButton(buttons), timestamp, + touch_id == 0 ? kFlutterPointerDeviceKindMouse + : kFlutterPointerDeviceKindTouch, + touch_id); + } + } + }); + + // Mouse move. + AddEventListener( + TIZEN_CORE_WL_EVENT_MOUSE_MOVE, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window == self->tcore_wl_window_) { + int x = 0, y = 0; + tizen_core_wl_event_mouse_move_get_position(ev, &x, &y); + uint32_t timestamp = 0; + tizen_core_wl_event_input_base_get_timestamp(ev, ×tamp); + unsigned int touch_id = 0; + tizen_core_wl_event_mouse_move_get_touch_id(ev, &touch_id); + self->view_delegate_->OnPointerMove( + x, y, timestamp, + touch_id == 0 ? kFlutterPointerDeviceKindMouse + : kFlutterPointerDeviceKindTouch, + touch_id); + } + } + }); + + // Mouse wheel. + AddEventListener( + TIZEN_CORE_WL_EVENT_MOUSE_WHEEL, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (self->view_delegate_) { + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window == self->tcore_wl_window_) { + int x = 0, y = 0; + tizen_core_wl_event_mouse_wheel_get_position(ev, &x, &y); + int direction = 0; + tizen_core_wl_event_mouse_wheel_get_direction(ev, &direction); + int z = 0; + tizen_core_wl_event_mouse_wheel_get_z(ev, &z); + uint32_t timestamp = 0; + tizen_core_wl_event_input_base_get_timestamp(ev, ×tamp); + + double delta_x = 0.0; + double delta_y = 0.0; + if (direction == kScrollDirectionVertical) { + delta_y += z; + } else if (direction == kScrollDirectionHorizontal) { + delta_x += z; + } + + self->view_delegate_->OnScroll(x, y, delta_x, delta_y, timestamp, + kFlutterPointerDeviceKindMouse, 0); + } + } + }); + + // Key down. + AddEventListener( + TIZEN_CORE_WL_EVENT_KEY_DOWN, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (!self->view_delegate_) { + return; + } + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window != self->tcore_wl_window_) { + return; + } + + char* keyname = nullptr; + tizen_core_wl_event_key_get_keyname(ev, &keyname); + char* keysymbol = nullptr; + tizen_core_wl_event_key_get_keysymbol(ev, &keysymbol); + char* compose = nullptr; + tizen_core_wl_event_key_get_compose(ev, &compose); + unsigned int modifiers = 0; + tizen_core_wl_event_key_get_modifiers(ev, &modifiers); + unsigned int keycode = 0; + tizen_core_wl_event_key_get_keycode(ev, &keycode); + char* dev_identifier = nullptr; + tizen_core_wl_event_input_base_get_device_identifier(ev, + &dev_identifier); + + bool handled = false; + if (self->input_method_context_ && + self->input_method_context_->IsInputPanelShown()) { + handled = + self->input_method_context_->HandleTcoreWlEventKey(event, true); + } + if (!handled) { + // Match TizenWindowEcoreWl2 OnKey arg semantics: + // param 1 (key) = XKB key symbol name (e.g. "period") + // param 2 (string) = composed printable string (e.g. ".") + // tcore's keysymbol corresponds to ecore's key_event->key, and + // tcore's compose corresponds to ecore's key_event->string. + // Passing keysymbol as `string` breaks the printable-key fallback + // in TextInputChannel::HandleKey() for keys whose symbol name is + // multi-char ("period", "minus", "equal", ...). + self->view_delegate_->OnKey(keysymbol, compose, compose, modifiers, + keycode, dev_identifier, true); + } + free(keyname); + free(keysymbol); + free(compose); + free(dev_identifier); + }); + + // Key up. + AddEventListener( + TIZEN_CORE_WL_EVENT_KEY_UP, + [](void* event, tizen_core_wl_event_type_e type, void* data) { + auto* self = static_cast(data); + if (!self->view_delegate_) { + return; + } + auto* ev = static_cast(event); + tizen_core_wl_window_h event_window = nullptr; + tizen_core_wl_event_input_base_get_window(ev, &event_window); + if (event_window != self->tcore_wl_window_) { + return; + } + + char* keyname = nullptr; + tizen_core_wl_event_key_get_keyname(ev, &keyname); + char* keysymbol = nullptr; + tizen_core_wl_event_key_get_keysymbol(ev, &keysymbol); + char* compose = nullptr; + tizen_core_wl_event_key_get_compose(ev, &compose); + unsigned int modifiers = 0; + tizen_core_wl_event_key_get_modifiers(ev, &modifiers); + unsigned int keycode = 0; + tizen_core_wl_event_key_get_keycode(ev, &keycode); + + bool handled = false; + if (self->input_method_context_ && + self->input_method_context_->IsInputPanelShown()) { + handled = + self->input_method_context_->HandleTcoreWlEventKey(event, false); + } + if (!handled) { + // See key-down handler for the keysymbol vs compose rationale. + self->view_delegate_->OnKey(keysymbol, compose, compose, modifiers, + keycode, nullptr, false); + } + free(keyname); + free(keysymbol); + free(compose); + }); +} + +void TizenWindowTcoreWl::UnregisterEventHandlers() { + for (tizen_core_wl_event_listener_h listener : tcore_event_listeners_) { + tizen_core_wl_event_remove_listener(tcore_wl_event_, listener); + } + tcore_event_listeners_.clear(); +} + +void TizenWindowTcoreWl::DestroyWindow() { + if (tcore_wl_egl_window_) { + tizen_core_wl_egl_window_destroy(tcore_wl_egl_window_); + tcore_wl_egl_window_ = nullptr; + } + + if (tcore_wl_window_) { + tizen_core_wl_window_destroy(tcore_wl_window_); + tcore_wl_window_ = nullptr; + } + + if (tcore_wl_display_) { + tizen_core_wl_display_disconnect(tcore_wl_display_); + tizen_core_wl_display_destroy(tcore_wl_display_); + tcore_wl_display_ = nullptr; + } + tizen_core_wl_shutdown(); +} + +TizenGeometry TizenWindowTcoreWl::GetGeometry() { + TizenGeometry result; + tizen_core_wl_window_get_geometry(tcore_wl_window_, &result.left, &result.top, + &result.width, &result.height); + return result; +} + +bool TizenWindowTcoreWl::SetGeometry(TizenGeometry geometry) { + tizen_core_wl_window_set_geometry(tcore_wl_window_, geometry.left, + geometry.top, geometry.width, + geometry.height); + tizen_core_wl_window_set_position(tcore_wl_window_, geometry.left, + geometry.top); + return true; +} + +TizenGeometry TizenWindowTcoreWl::GetScreenGeometry() { + TizenGeometry result = {}; + GList* output_list = nullptr; + tizen_core_wl_display_get_output_device_list(tcore_wl_display_, &output_list); + if (output_list) { + tizen_core_wl_output_h output = + static_cast(output_list->data); + tizen_core_wl_output_device_get_geometry(output, &result.width, + &result.height); + g_list_free(output_list); + } + return result; +} + +int32_t TizenWindowTcoreWl::GetRotation() { + tizen_core_wl_window_angle_e angle = TIZEN_CORE_WL_WINDOW_ANGLE_0; + tizen_core_wl_window_get_rotation_angle(tcore_wl_window_, &angle); + return static_cast(angle); +} + +int32_t TizenWindowTcoreWl::GetDpi() { + GList* output_list = nullptr; + tizen_core_wl_display_get_output_device_list(tcore_wl_display_, &output_list); + if (!output_list) { + FT_LOG(Error) << "Could not find an output device."; + return 0; + } + tizen_core_wl_output_h output = + static_cast(output_list->data); + g_list_free(output_list); + int dpi = 0; + tizen_core_wl_output_device_get_dpi(output, &dpi); + return dpi; +} + +uintptr_t TizenWindowTcoreWl::GetWindowId() { + return reinterpret_cast(tcore_wl_window_); +} + +uint32_t TizenWindowTcoreWl::GetResourceId() { + if (resource_id_ > 0) { + return resource_id_; + } + + unsigned int res_id = 0; + if (tizen_core_wl_window_private_get_resource_id(tcore_wl_window_, &res_id) == + TIZEN_CORE_WL_ERROR_NONE) { + resource_id_ = res_id; + } + return resource_id_; +} + +void TizenWindowTcoreWl::SetPreferredOrientations( + const std::vector& rotations) { + std::vector angles; + for (int rot : rotations) { + angles.push_back(static_cast(rot)); + } + tizen_core_wl_window_set_available_rotation_angle_list( + tcore_wl_window_, angles.data(), angles.size()); +} + +void TizenWindowTcoreWl::BindKeys(const std::vector& keys) { + for (const std::string& key : keys) { + tizen_core_wl_keygrab_info_h info = nullptr; + tizen_core_wl_keygrab_info_create(key.c_str(), + TIZEN_CORE_WL_KEYGRAB_TOPMOST, &info); + if (info) { + GList* list = g_list_append(nullptr, info); + tizen_core_wl_window_set_keygrab_list(tcore_wl_window_, list); + g_list_free(list); + tizen_core_wl_keygrab_info_destroy(info); + } + } +} + +void TizenWindowTcoreWl::Show() { + tizen_core_wl_window_show(tcore_wl_window_); +} + +void TizenWindowTcoreWl::UpdateFlutterCursor(const std::string& kind) { +#ifdef TV_PROFILE + int pointer_size = -1; + if (vconf_get_int(kSysMouseCursorPointerSizeVConfKey, &pointer_size) < 0) { + FT_LOG(Info) << "Failed to load cursor size."; + } + + std::string cursor_name = "normal_default"; + if (kind == "basic") { + if (pointer_size == 0) { + cursor_name = "large_normal"; + } else if (pointer_size == 1) { + cursor_name = "medium_normal"; + } else if (pointer_size == 2) { + cursor_name = "small_normal"; + } else { + cursor_name = "normal_default"; + } + } else if (kind == "click") { + if (pointer_size == 0) { + cursor_name = "large_normal_pnh"; + } else if (pointer_size == 1) { + cursor_name = "medium_normal_pnh"; + } else if (pointer_size == 2) { + cursor_name = "small_normal_pnh"; + } else { + cursor_name = "normal_pnh"; + } + } else if (kind == "text") { + if (pointer_size == 0) { + cursor_name = "large_normal_input_field"; + } else if (pointer_size == 1) { + cursor_name = "medium_normal_input_field"; + } else if (pointer_size == 2) { + cursor_name = "small_normal_input_field"; + } else { + cursor_name = "normal_input_field"; + } + } else if (kind == "none") { + cursor_name = "normal_transparent"; + } else { + FT_LOG(Info) << kind << " cursor is not supported."; + } + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(tcore_wl_display_, &default_seat); + if (default_seat) { + tizen_core_wl_seat_set_cursor_theme(default_seat, + kTcoreWlInputCursorThemeName); + tizen_core_wl_seat_set_cursor_name(default_seat, cursor_name.c_str()); + } else { + FT_LOG(Error) << "Failed to get default seat; cannot update cursor."; + } +#else + tizen_core_wl_seat_h default_seat = nullptr; + tizen_core_wl_display_get_default_seat(tcore_wl_display_, &default_seat); + if (default_seat) { + tizen_core_wl_seat_set_cursor_theme(default_seat, "default"); + tizen_core_wl_seat_set_cursor_name(default_seat, "left_ptr"); + } + FT_LOG(Info) << "UpdateFlutterCursor is not supported."; +#endif +} + +void TizenWindowTcoreWl::SetNotificationLevel(int level) { + tizen_core_wl_notification_set_level( + tcore_wl_window_, static_cast(level)); +} + +void TizenWindowTcoreWl::PrepareInputMethod() { + input_method_context_ = + std::make_unique(GetWindowId()); + + input_method_context_->SetOnPreeditStart( + [this]() { view_delegate_->OnComposeBegin(); }); + input_method_context_->SetOnPreeditChanged( + [this](std::string str, int cursor_pos) { + view_delegate_->OnComposeChange(str, cursor_pos); + }); + input_method_context_->SetOnPreeditEnd( + [this]() { view_delegate_->OnComposeEnd(); }); + input_method_context_->SetOnCommit( + [this](std::string str) { view_delegate_->OnCommit(str); }); +} + +void* TizenWindowTcoreWl::GetRenderTarget() { + if (is_vulkan_) { + return wl2_surface_; + } else { + return tcore_wl_egl_window_; + } +} + +void TizenWindowTcoreWl::ActivateWindow() { + tizen_core_wl_window_activate(tcore_wl_window_); +} + +void TizenWindowTcoreWl::RaiseWindow() { + tizen_core_wl_window_raise(tcore_wl_window_); +} + +void TizenWindowTcoreWl::LowerWindow() { + tizen_core_wl_window_lower(tcore_wl_window_); +} + +} // namespace flutter diff --git a/flutter/shell/platform/tizen/tizen_window_tcore_wl.h b/flutter/shell/platform/tizen/tizen_window_tcore_wl.h new file mode 100644 index 00000000..2ced4ddc --- /dev/null +++ b/flutter/shell/platform/tizen/tizen_window_tcore_wl.h @@ -0,0 +1,114 @@ +// Copyright 2022 Samsung Electronics Co., Ltd. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef EMBEDDER_TIZEN_WINDOW_TCORE_WL_H_ +#define EMBEDDER_TIZEN_WINDOW_TCORE_WL_H_ + +#include +#include +#include + +#include +#include +#include + +#include "flutter/shell/platform/tizen/tizen_window.h" + +namespace flutter { + +class TizenWindowTcoreWl : public TizenWindow { + public: + TizenWindowTcoreWl(TizenGeometry geometry, + bool transparent, + bool focusable, + bool top_level, + bool pointing_device_support, + bool floating_menu_support, + void* window_handle, + bool is_vulkan); + + ~TizenWindowTcoreWl(); + + TizenGeometry GetGeometry() override; + + bool SetGeometry(TizenGeometry geometry) override; + + TizenGeometry GetScreenGeometry() override; + + void* GetRenderTarget() override; + + void* GetRenderTargetDisplay() override { return wl2_display_; } + + void* GetNativeHandle() override { return tcore_wl_window_; } + + int32_t GetRotation() override; + + int32_t GetDpi() override; + + uintptr_t GetWindowId() override; + + uint32_t GetResourceId() override; + + void SetPreferredOrientations(const std::vector& rotations) override; + + void BindKeys(const std::vector& keys) override; + + void Show() override; + + void UpdateFlutterCursor(const std::string& kind) override; + + void ActivateWindow() override; + + void RaiseWindow() override; + + void LowerWindow() override; + + private: + bool CreateWindow(void* window_handle); + + void DestroyWindow(); + + void SetWindowOptions(); + + void EnableCursor(); + +#ifdef TV_PROFILE + void SetPointingDeviceSupport(); + + void SetFloatingMenuSupport(); + + void ShowUnsupportedToast(); +#endif + + void RegisterEventHandlers(); + + void AddEventListener(tizen_core_wl_event_type_e type, + tizen_core_wl_event_cb callback); + + void UnregisterEventHandlers(); + + void SetNotificationLevel(int level); + + void PrepareInputMethod(); + + tizen_core_wl_display_h tcore_wl_display_ = nullptr; + tizen_core_wl_window_h tcore_wl_window_ = nullptr; + tizen_core_wl_egl_window_h tcore_wl_egl_window_ = nullptr; + tizen_core_event_h tcore_wl_event_ = nullptr; + wl_display* wl2_display_ = nullptr; + wl_surface* wl2_surface_ = nullptr; + std::vector tcore_event_listeners_; + uint32_t resource_id_ = 0; + +#ifdef TV_PROFILE + bool pointing_device_support_ = true; + bool floating_menu_support_ = true; + bool show_unsupported_toast_ = false; +#endif + bool is_vulkan_ = false; +}; + +} // namespace flutter + +#endif // EMBEDDER_TIZEN_WINDOW_TCORE_WL_H_ diff --git a/flutter/third_party/accessibility/ax/ax_active_popup.h b/flutter/third_party/accessibility/ax/ax_active_popup.h index 39b8beda..94f15c66 100644 --- a/flutter/third_party/accessibility/ax/ax_active_popup.h +++ b/flutter/third_party/accessibility/ax/ax_active_popup.h @@ -5,6 +5,7 @@ #ifndef UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_ #define UI_ACCESSIBILITY_AX_ACTIVE_POPUP_H_ +#include #include #include "ax/ax_export.h" diff --git a/flutter/third_party/accessibility/ax/ax_event_generator.h b/flutter/third_party/accessibility/ax/ax_event_generator.h index 688cffcd..f66e5f0c 100644 --- a/flutter/third_party/accessibility/ax/ax_event_generator.h +++ b/flutter/third_party/accessibility/ax/ax_event_generator.h @@ -125,9 +125,13 @@ class AX_EXPORT AXEventGenerator : public AXTreeObserver { const EventParams& event_params; }; - class AX_EXPORT Iterator - : public std::iterator { + class AX_EXPORT Iterator { public: + using iterator_category = std::input_iterator_tag; + using value_type = TargetedEvent; + using difference_type = std::ptrdiff_t; + using pointer = TargetedEvent*; + using reference = TargetedEvent&; Iterator( const std::map>& map, const std::map>::const_iterator& head); diff --git a/tools/generate_sysroot.py b/tools/generate_sysroot.py index 8b036cd2..097c9fae 100755 --- a/tools/generate_sysroot.py +++ b/tools/generate_sysroot.py @@ -109,6 +109,14 @@ 'dali2-integration-devel', 'dali2-adaptor-integration-devel', 'dali2-toolkit-integration-devel', + 'tizen-core', + 'tizen-core-devel', + 'tizen-core-imf', + 'tizen-core-imf-devel', + 'tizen-core-wl', + 'tizen-core-wl-devel', + 'libwayland-egl-tizen', + 'libwayland-egl-tizen-devel', ] def generate_sysroot(sysroot: Path, api_version: float, arch: str, quiet=False): diff --git a/tools/gn b/tools/gn index 7038677f..aba985c7 100755 --- a/tools/gn +++ b/tools/gn @@ -14,6 +14,9 @@ from pathlib import Path def get_out_dir(args): target_dir = ['tizen', args.target_cpu] + if args.use_tcore: + target_dir.append('tcore') + if args.unoptimized: target_dir.append('unopt') @@ -78,6 +81,7 @@ def to_gn_args(args): gn_args['clang_version'] = get_clang_version(args.target_toolchain) gn_args['api_version'] = args.api_version gn_args['use_system_cxx'] = not args.no_system_cxx + gn_args['use_tcore'] = args.use_tcore for key, val in gn_args.items(): if isinstance(val, bool): @@ -104,6 +108,8 @@ def parse_args(args): parser.add_argument('--api-version', default='6.0', type=str) parser.add_argument('--no-system-cxx', default=False, action='store_true') + parser.add_argument('--use-tcore', default=False, action='store_true', + help='Use tizen-core-wl API instead of ecore') parser.add_argument('--target-dir', type=str) parser.add_argument('--verbose', default=False, action='store_true') @@ -114,6 +120,16 @@ def parse_args(args): def main(): args = parse_args(sys.argv) + if args.use_tcore: + try: + api_value = float(args.api_version) + except ValueError: + api_value = 0.0 + if api_value < 10.0: + sys.exit('--use-tcore requires --api-version 10.0 or higher ' + '(tizen-core packages are only published for Tizen 11+). ' + 'Got --api-version {}.'.format(args.api_version)) + root_dir = Path(__file__).parent.parent.resolve() gn = root_dir / 'third_party' / 'gn' / 'gn' command = [gn, 'gen', '--check', '--export-compile-commands']