2 * Copyright (c) 2018 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali/internal/input/ubuntu-x11/imf-manager-impl-x.h>
22 #include <dali/public-api/events/key-event.h>
23 #include <dali/public-api/object/type-registry.h>
24 #include <dali/integration-api/debug.h>
27 #include <dali/integration-api/adaptor.h>
28 #include <dali/internal/window-system/ubuntu-x11/window-render-surface-x.h>
29 #include <dali/internal/adaptor/common/adaptor-impl.h>
30 #include <dali/internal/system/common/locale-utils.h>
31 #include <dali/internal/system/common/singleton-service-impl.h>
32 #include <dali/internal/input/common/virtual-keyboard-impl.h>
33 // Ecore is littered with C style cast
34 #pragma GCC diagnostic push
35 #pragma GCC diagnostic ignored "-Wold-style-cast"
36 #include <dali/internal/input/tizen-wayland/ecore-virtual-keyboard.h>
49 #if defined(DEBUG_ENABLED)
50 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMF_MANAGER");
53 // Currently this code is internal to dali/dali/internal/event/text/utf8.h but should be made Public and used from there instead.
54 size_t Utf8SequenceLength(const unsigned char leadByte)
58 if ((leadByte & 0x80) == 0 ) //ASCII character (lead bit zero)
62 else if (( leadByte & 0xe0 ) == 0xc0 ) //110x xxxx
66 else if (( leadByte & 0xf0 ) == 0xe0 ) //1110 xxxx
70 else if (( leadByte & 0xf8 ) == 0xf0 ) //1111 0xxx
78 // Static function calls used by ecore 'c' style callback registration
79 void Commit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
83 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
84 imfManager->CommitReceived( data, imfContext, event_info );
88 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
92 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
93 imfManager->PreEditChanged( data, imfContext, event_info );
97 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
101 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
102 return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
111 * Called when an IMF delete surrounding event is received.
112 * Here we tell the application that it should delete a certain range.
114 void ImfDeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
118 ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
119 imfManager->DeleteSurrounding( data, imfContext, event_info );
123 } // unnamed namespace
125 bool ImfManagerX::IsAvailable()
127 bool available( false );
129 Dali::SingletonService service( SingletonService::Get() );
132 available = service.GetSingleton( typeid( Dali::ImfManager ) );
138 Dali::ImfManager ImfManagerX::Get()
140 Dali::ImfManager manager;
142 Dali::SingletonService service( SingletonService::Get() );
145 // Check whether the singleton is already created
146 Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
149 // If so, downcast the handle
150 manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
152 else if ( Adaptor::IsAvailable() )
154 // Create instance and register singleton only if the adaptor is available
156 Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
157 Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
159 // The Ecore_X_Window needs to use the ImfManager.
160 // Only when the render surface is window, we can get the Ecore_X_Window.
161 Ecore_X_Window ecoreXwin( AnyCast<Ecore_X_Window>(nativeWindow) );
164 // If we fail to get Ecore_X_Window, we can't use the ImfManager correctly.
165 // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
166 // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
168 manager = Dali::ImfManager( new ImfManagerX( ecoreXwin ) );
169 service.Register( typeid( manager ), manager );
173 DALI_LOG_ERROR("Failed to get native window handle\n");
181 ImfManagerX::ImfManagerX( Ecore_X_Window ecoreXwin )
183 mIMFCursorPosition( 0 ),
185 mRestoreAfterFocusLost( false ),
186 mIdleCallbackConnected( false )
189 CreateContext( ecoreXwin );
192 VirtualKeyboard::ConnectCallbacks( mIMFContext );
195 ImfManagerX::~ImfManagerX()
197 VirtualKeyboard::DisconnectCallbacks( mIMFContext );
199 ecore_imf_shutdown();
202 void ImfManagerX::Finalize()
204 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Finalize\n" );
205 DisconnectCallbacks();
210 void ImfManagerX::CreateContext( Ecore_X_Window ecoreXwin )
212 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
214 const char *contextId = ecore_imf_context_default_id_get();
217 mIMFContext = ecore_imf_context_add( contextId );
223 ecore_imf_context_client_window_set( mIMFContext, reinterpret_cast<void*>( ecoreXwin ) );
228 DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
233 DALI_LOG_INFO( gLogFilter, Debug::General, "IMF Unable to get IMF Context\n");
237 void ImfManagerX::DeleteContext()
239 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
243 ecore_imf_context_del( mIMFContext );
248 // Callbacks for predicitive text support.
249 void ImfManagerX::ConnectCallbacks()
253 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
255 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit, this );
256 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit, this );
257 ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
259 ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
263 void ImfManagerX::DisconnectCallbacks()
267 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
269 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED, PreEdit );
270 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT, Commit );
271 ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
273 // We do not need to unset the retrieve surrounding callback.
277 void ImfManagerX::Activate()
279 // Reset mIdleCallbackConnected
280 mIdleCallbackConnected = false;
284 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
286 ecore_imf_context_focus_in( mIMFContext );
288 // emit keyboard activated signal
289 Dali::ImfManager handle( this );
290 mActivatedSignal.Emit( handle );
294 void ImfManagerX::Deactivate()
298 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
301 ecore_imf_context_focus_out( mIMFContext );
304 // Reset mIdleCallbackConnected
305 mIdleCallbackConnected = false;
308 void ImfManagerX::Reset()
310 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
314 ecore_imf_context_reset( mIMFContext );
318 ImfContext* ImfManagerX::GetContext()
320 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
325 bool ImfManagerX::RestoreAfterFocusLost() const
327 return mRestoreAfterFocusLost;
330 void ImfManagerX::SetRestoreAfterFocusLost( bool toggle )
332 mRestoreAfterFocusLost = toggle;
336 * Called when an IMF Pre-Edit changed event is received.
337 * We are still predicting what the user is typing. The latest string is what the IMF module thinks
338 * the user wants to type.
340 void ImfManagerX::PreEditChanged( void*, ImfContext * context, void* event_info )
342 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
344 auto imfContext = reinterpret_cast<Ecore_IMF_Context*>(context);
346 char* preEditString( NULL );
347 int cursorPosition( 0 );
348 Eina_List* attrs = NULL;
351 Ecore_IMF_Preedit_Attr* attr;
353 // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
354 // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
355 ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
359 // iterate through the list of attributes getting the type, start and end position.
360 for ( l = attrs, (attr = static_cast<Ecore_IMF_Preedit_Attr*>( eina_list_data_get(l) ) ); l; l = eina_list_next(l), ( attr = static_cast<Ecore_IMF_Preedit_Attr*>( eina_list_data_get(l) ) ))
362 #ifdef DALI_PROFILE_UBUNTU
363 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
364 #else // DALI_PROFILE_UBUNTU
365 if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
366 #endif // DALI_PROFILE_UBUNTU
368 // check first byte so know how many bytes a character is represented by as keyboard returns cursor position in bytes. Which is different for some languages.
370 size_t visualCharacterIndex = 0;
371 size_t byteIndex = 0;
373 // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
374 const char leadByte = preEditString[byteIndex];
375 while( leadByte != '\0' )
377 // attr->end_index is provided as a byte position not character and we need to know the character position.
378 const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
379 if ( byteIndex == attr->end_index )
381 cursorPosition = visualCharacterIndex;
383 // end loop as found cursor position that matches byte position
387 byteIndex += currentSequenceLength; // jump to next character
388 visualCharacterIndex++; // increment character count so we know our position for when we get a match
391 DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
397 if ( Dali::Adaptor::IsAvailable() )
399 Dali::ImfManager handle( this );
400 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
401 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
403 if( callbackData.update )
405 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
407 NotifyCursorPosition();
410 if( callbackData.preeditResetRequired )
415 free( preEditString );
418 void ImfManagerX::CommitReceived( void*, ImfContext* context, void* event_info )
420 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
422 if ( Dali::Adaptor::IsAvailable() )
424 const std::string keyString( static_cast<char*>( event_info ) );
426 Dali::ImfManager handle( this );
427 Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
428 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
430 if( callbackData.update )
432 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
434 NotifyCursorPosition();
440 * Called when an IMF retrieve surround event is received.
441 * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
442 * We need to signal the application to tell us this information.
444 bool ImfManagerX::RetrieveSurrounding( void* data, ImfContext* context, char** text, int* cursorPosition )
446 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
448 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
449 Dali::ImfManager handle( this );
450 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
452 if( callbackData.update )
456 *text = strdup( callbackData.currentText.c_str() );
461 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
462 *cursorPosition = mIMFCursorPosition;
470 * Called when an IMF delete surrounding event is received.
471 * Here we tell the application that it should delete a certain range.
473 void ImfManagerX::DeleteSurrounding( void* data, ImfContext* context, void* event_info )
475 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
477 if( Dali::Adaptor::IsAvailable() )
479 Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
481 Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
482 Dali::ImfManager handle( this );
483 Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfData );
485 if( callbackData.update )
487 mIMFCursorPosition = static_cast<int>( callbackData.cursorPosition );
489 NotifyCursorPosition();
494 void ImfManagerX::NotifyCursorPosition()
496 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
500 ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
504 void ImfManagerX::SetCursorPosition( unsigned int cursorPosition )
506 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
508 mIMFCursorPosition = static_cast<int>( cursorPosition );
511 unsigned int ImfManagerX::GetCursorPosition() const
513 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
515 return static_cast<unsigned int>( mIMFCursorPosition );
518 void ImfManagerX::SetSurroundingText( const std::string& text )
520 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
522 mSurroundingText = text;
525 const std::string& ImfManagerX::GetSurroundingText() const
527 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
529 return mSurroundingText;
532 void ImfManagerX::NotifyTextInputMultiLine( bool multiLine )
536 Dali::ImfManager::TextDirection ImfManagerX::GetTextDirection()
538 Dali::ImfManager::TextDirection direction ( Dali::ImfManager::LeftToRight );
540 if ( ImfManager::IsAvailable() /* We do not want to create an instance of ImfManager */ )
544 char* locale( NULL );
545 ecore_imf_context_input_panel_language_locale_get( mIMFContext, &locale );
549 direction = static_cast< Dali::ImfManager::TextDirection >( Locale::GetDirection( std::string( locale ) ) );
557 Rect<int> ImfManagerX::GetInputMethodArea()
559 int xPos, yPos, width, height;
561 width = height = xPos = yPos = 0;
565 ecore_imf_context_input_panel_geometry_get( mIMFContext, &xPos, &yPos, &width, &height );
569 DALI_LOG_WARNING("VKB Unable to get IMF Context so GetSize unavailable\n");
572 return Rect<int>(xPos,yPos,width,height);
575 void ImfManagerX::ApplyOptions( const InputMethodOptions& options )
577 using namespace Dali::InputMethod::Category;
581 if (mIMFContext == NULL)
583 DALI_LOG_WARNING("VKB Unable to excute ApplyOptions with Null ImfContext\n");
587 if ( mOptions.CompareAndSet(PANEL_LAYOUT, options, index) )
590 if ( mOptions.CompareAndSet(AUTO_CAPITALISE, options, index) )
593 if ( mOptions.CompareAndSet(ACTION_BUTTON_TITLE, options, index) )
596 if ( mOptions.CompareAndSet(VARIATION, options, index) )
601 void ImfManagerX::SetInputPanelData( const std::string& data )
603 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetInputPanelData\n" );
607 int length = data.length();
608 ecore_imf_context_input_panel_imdata_set( mIMFContext, data.c_str(), length );
612 void ImfManagerX::GetInputPanelData( std::string& data )
614 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelData\n" );
618 int length = 4096; // The max length is 4096 bytes
619 Dali::Vector< char > buffer;
620 buffer.Resize( length );
621 ecore_imf_context_input_panel_imdata_get( mIMFContext, &buffer[0], &length );
622 data = std::string( buffer.Begin(), buffer.End() );
626 Dali::ImfManager::State ImfManagerX::GetInputPanelState()
628 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelState\n" );
633 value = ecore_imf_context_input_panel_state_get( mIMFContext );
637 case ECORE_IMF_INPUT_PANEL_STATE_SHOW:
639 return Dali::ImfManager::SHOW;
643 case ECORE_IMF_INPUT_PANEL_STATE_HIDE:
645 return Dali::ImfManager::HIDE;
649 case ECORE_IMF_INPUT_PANEL_STATE_WILL_SHOW:
651 return Dali::ImfManager::WILL_SHOW;
657 return Dali::ImfManager::DEFAULT;
661 return Dali::ImfManager::DEFAULT;
664 void ImfManagerX::SetReturnKeyState( bool visible )
666 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetReturnKeyState\n" );
670 ecore_imf_context_input_panel_return_key_disabled_set( mIMFContext, !visible );
674 void ImfManagerX::AutoEnableInputPanel( bool enabled )
676 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::AutoEnableInputPanel\n" );
680 ecore_imf_context_input_panel_enabled_set( mIMFContext, enabled );
684 void ImfManagerX::ShowInputPanel()
686 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ShowInputPanel\n" );
690 ecore_imf_context_input_panel_show( mIMFContext );
694 void ImfManagerX::HideInputPanel()
696 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::HideInputPanel\n" );
700 ecore_imf_context_input_panel_hide( mIMFContext );
704 Dali::ImfManager::KeyboardType ImfManagerX::GetKeyboardType()
706 return Dali::ImfManager::KeyboardType::SOFTWARE_KEYBOARD;
709 std::string ImfManagerX::GetInputPanelLocale()
711 DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetInputPanelLocale\n" );
713 std::string locale = "";
718 ecore_imf_context_input_panel_language_locale_get( mIMFContext, &value );
722 std::string valueCopy( value );
725 // The locale string retrieved must be freed with free().
738 #pragma GCC diagnostic pop