From 88e0acd31288982afe92370ee78e75565576f06c Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Wed, 30 Sep 2026 18:46:16 +0900 Subject: [PATCH 1/5] Follow tizen-core-imf event callback API rename tizen-core-imf 0.2.22 renamed the context event callback functions from *_event_callback to *_event_cb, keeping the same parameters (including user_data on the del variants). Update the tcore input method context to the new names so it builds against the current Tizen 11.0 packages. --- .../tizen/tizen_input_method_context_tcore.cc | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc index 5077725..36ca42b 100644 --- a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -390,7 +390,7 @@ void TizenInputMethodContext::RegisterEventCallbacks() { self->on_commit_(str); } }; - tizen_core_imf_context_add_event_callback( + tizen_core_imf_context_add_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_COMMIT, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_COMMIT], this); @@ -402,7 +402,7 @@ void TizenInputMethodContext::RegisterEventCallbacks() { self->on_preedit_start_(); } }; - tizen_core_imf_context_add_event_callback( + tizen_core_imf_context_add_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_START, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_START], this); @@ -414,7 +414,7 @@ void TizenInputMethodContext::RegisterEventCallbacks() { self->on_preedit_end_(); } }; - tizen_core_imf_context_add_event_callback( + tizen_core_imf_context_add_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_END, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_END], this); @@ -438,7 +438,7 @@ void TizenInputMethodContext::RegisterEventCallbacks() { } } }; - tizen_core_imf_context_add_event_callback( + tizen_core_imf_context_add_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED], this); } @@ -447,16 +447,16 @@ void TizenInputMethodContext::UnregisterEventCallbacks() { if (!imf_context_) { return; } - tizen_core_imf_context_del_event_callback( + tizen_core_imf_context_del_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_COMMIT, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_COMMIT], this); - tizen_core_imf_context_del_event_callback( + tizen_core_imf_context_del_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_CHANGED], this); - tizen_core_imf_context_del_event_callback( + tizen_core_imf_context_del_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_START, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_START], this); - tizen_core_imf_context_del_event_callback( + tizen_core_imf_context_del_event_cb( imf_context_, TIZEN_CORE_IMF_CALLBACK_PREEDIT_END, event_callbacks_[TIZEN_CORE_IMF_CALLBACK_PREEDIT_END], this); } @@ -513,7 +513,7 @@ void TizenInputMethodContext::RegisterInputPanelEventCallback() { return; } - tizen_core_imf_context_add_input_panel_event_callback( + tizen_core_imf_context_add_input_panel_event_cb( imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_EVENT_STATE, InputPanelStateChangedCallback, this); } @@ -523,7 +523,7 @@ void TizenInputMethodContext::UnregisterInputPanelEventCallback() { return; } - tizen_core_imf_context_del_input_panel_event_callback( + tizen_core_imf_context_del_input_panel_event_cb( imf_context_, TIZEN_CORE_IMF_INPUT_PANEL_EVENT_STATE, InputPanelStateChangedCallback, this); } From e81ed1baf3f3a142ed1c8400503389d06bb265ed Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 1 Oct 2026 13:15:28 +0900 Subject: [PATCH 2/5] Initialize tizen-core-imf once per process Unlike ecore_imf_init(), tizen_core_imf_init() is not reference counted and must not be called again before tizen_core_imf_shutdown(). Calling shutdown from each context unloads the IME module while other contexts in the same process are still alive. Share a single init among all TizenInputMethodContext instances and log when initialization fails. --- .../tizen/tizen_input_method_context_tcore.cc | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc index 36ca42b..c64e206 100644 --- a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -12,6 +12,8 @@ namespace { +int imf_init_count = 0; + tizen_core_imf_input_panel_layout_e TextInputTypeToImfInputPanelLayout( const std::string& text_input_type) { if (text_input_type == "TextInputType.text" || @@ -155,7 +157,11 @@ bool IsNavigationOrSystemKey(const char* key) { namespace flutter { TizenInputMethodContext::TizenInputMethodContext(uintptr_t window_id) { - tizen_core_imf_init(); + if (imf_init_count++ == 0 && + tizen_core_imf_init() != TIZEN_CORE_IMF_ERROR_NONE) { + FT_LOG(Error) << "Failed to initialize tizen_core_imf."; + return; + } if (tizen_core_imf_context_create(&imf_context_) != TIZEN_CORE_IMF_ERROR_NONE) { @@ -179,7 +185,9 @@ TizenInputMethodContext::~TizenInputMethodContext() { tizen_core_imf_context_destroy(imf_context_); } - tizen_core_imf_shutdown(); + if (--imf_init_count == 0) { + tizen_core_imf_shutdown(); + } } bool TizenInputMethodContext::HandleTcoreWlEventKey(void* event, bool is_down) { From f156aad8584f6a56f4f796b11c7811cf9e79abe9 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 1 Oct 2026 13:16:16 +0900 Subject: [PATCH 3/5] Set device class of tcore IMF key events The Ecore backend passes the device class and subclass of key events to the input method, but the tcore backend left them as NONE, so the IME could not tell remote control input from keyboard input. Look up the input device by its identifier in the default seat and set its class and subclass on the IMF key event. --- .../tizen/tizen_input_method_context_tcore.cc | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc index c64e206..a1abbf7 100644 --- a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -75,6 +75,44 @@ tizen_core_imf_keyboard_locks_e ModifiersToImfLocks(unsigned int modifiers) { return static_cast(locks); } +void SetImfKeyDeviceClass(tizen_core_imf_event_key_h imf_key, + tizen_core_wl_event_input_base_h ev, + const char* dev_identifier) { + tizen_core_wl_window_h window = nullptr; + tizen_core_wl_display_h display = nullptr; + tizen_core_wl_seat_h seat = nullptr; + GList* devices = nullptr; + if (tizen_core_wl_event_input_base_get_window(ev, &window) != + TIZEN_CORE_WL_ERROR_NONE || + tizen_core_wl_window_get_display(window, &display) != + TIZEN_CORE_WL_ERROR_NONE || + tizen_core_wl_display_get_default_seat(display, &seat) != + TIZEN_CORE_WL_ERROR_NONE || + tizen_core_wl_seat_get_input_device_list(seat, &devices) != + TIZEN_CORE_WL_ERROR_NONE) { + return; + } + + for (GList* node = devices; node; node = node->next) { + auto device = static_cast(node->data); + const char* identifier = nullptr; + tizen_core_wl_input_device_get_identifier(device, &identifier); + if (identifier && strcmp(identifier, dev_identifier) == 0) { + tizen_core_wl_device_class_e dev_class = TIZEN_CORE_WL_DEVICE_CLASS_NONE; + tizen_core_wl_device_subclass_e dev_subclass = + TIZEN_CORE_WL_DEVICE_SUBCLASS_NONE; + tizen_core_wl_input_device_get_class(device, &dev_class); + tizen_core_wl_input_device_get_subclass(device, &dev_subclass); + tizen_core_imf_event_key_set_device_class( + imf_key, static_cast(dev_class)); + tizen_core_imf_event_key_set_device_subclass( + imf_key, static_cast(dev_subclass)); + break; + } + } + g_list_free(devices); +} + tizen_core_imf_event_key_h CreateImfKeyEventFromTcoreWlEvent(void* event) { auto* ev = static_cast(event); @@ -125,6 +163,7 @@ tizen_core_imf_event_key_h CreateImfKeyEventFromTcoreWlEvent(void* event) { 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); + SetImfKeyDeviceClass(imf_key, ev, dev_identifier); free(dev_identifier); } From ddad9102afe92acb7fa0ce33b4d56bb3d38498f4 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Thu, 1 Oct 2026 13:16:45 +0900 Subject: [PATCH 4/5] Initialize input panel state before querying it IsInputPanelShown() read the state even when tizen_core_imf_context_get_input_panel_state() failed and left it unset. Default it to HIDE. --- .../shell/platform/tizen/tizen_input_method_context_tcore.cc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc index a1abbf7..9163674 100644 --- a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -340,7 +340,8 @@ bool TizenInputMethodContext::IsInputPanelShown() { if (!imf_context_) { return false; } - tizen_core_imf_input_panel_state_e state; + tizen_core_imf_input_panel_state_e state = + TIZEN_CORE_IMF_INPUT_PANEL_STATE_HIDE; tizen_core_imf_context_get_input_panel_state(imf_context_, &state); return state == TIZEN_CORE_IMF_INPUT_PANEL_STATE_SHOW || state == TIZEN_CORE_IMF_INPUT_PANEL_STATE_WILL_SHOW; From 8ec3c0d5e21cf1f248bff50befa19c1806ba8488 Mon Sep 17 00:00:00 2001 From: JunsuChoi Date: Fri, 2 Oct 2026 17:28:10 +0900 Subject: [PATCH 5/5] Count only live tizen-core-imf contexts The init count was incremented even when tizen_core_imf_init() failed, so the destructor called tizen_core_imf_shutdown() without a matching init and later contexts skipped the initialization. Increment the count only after the context is created and decrement it only when a context is destroyed, so that an instance which failed to initialize never affects the shutdown of the others. If the first context cannot be created, shut down right away to keep the init and shutdown calls paired. --- .../tizen/tizen_input_method_context_tcore.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc index 9163674..1c9eb02 100644 --- a/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc +++ b/flutter/shell/platform/tizen/tizen_input_method_context_tcore.cc @@ -196,7 +196,7 @@ bool IsNavigationOrSystemKey(const char* key) { namespace flutter { TizenInputMethodContext::TizenInputMethodContext(uintptr_t window_id) { - if (imf_init_count++ == 0 && + if (imf_init_count == 0 && tizen_core_imf_init() != TIZEN_CORE_IMF_ERROR_NONE) { FT_LOG(Error) << "Failed to initialize tizen_core_imf."; return; @@ -205,8 +205,13 @@ TizenInputMethodContext::TizenInputMethodContext(uintptr_t window_id) { if (tizen_core_imf_context_create(&imf_context_) != TIZEN_CORE_IMF_ERROR_NONE) { FT_LOG(Error) << "Failed to create tizen_core_imf_context."; + imf_context_ = nullptr; + if (imf_init_count == 0) { + tizen_core_imf_shutdown(); + } return; } + imf_init_count++; tizen_core_imf_context_set_client_window(imf_context_, reinterpret_cast(window_id)); @@ -222,10 +227,9 @@ TizenInputMethodContext::~TizenInputMethodContext() { if (imf_context_) { tizen_core_imf_context_destroy(imf_context_); - } - - if (--imf_init_count == 0) { - tizen_core_imf_shutdown(); + if (--imf_init_count == 0) { + tizen_core_imf_shutdown(); + } } }