Merge "Add native render surface for wayland" into devel/master
[platform/core/uifw/dali-adaptor.git] / adaptors / ecore / wayland / imf-manager-impl-ecore-wl.cpp
1 /*
2  * Copyright (c) 2014 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <imf-manager-impl.h>
20
21 // EXTERNAL INCLUDES
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>
25
26 // INTERNAL INCLUDES
27 #include <adaptor.h>
28 #include <window-render-surface.h>
29 #include <adaptor-impl.h>
30 #include <singleton-service-impl.h>
31 #include <virtual-keyboard-impl.h>
32 #include "ecore-virtual-keyboard.h"
33
34 namespace Dali
35 {
36
37 namespace Internal
38 {
39
40 namespace Adaptor
41 {
42
43 namespace
44 {
45 #if defined(DEBUG_ENABLED)
46 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_IMF_MANAGER");
47 #endif
48
49 // Currently this code is internal to dali/dali/internal/event/text/utf8.h but should be made Public and used from there instead.
50 size_t Utf8SequenceLength(const unsigned char leadByte)
51 {
52   size_t length = 0;
53
54   if ((leadByte & 0x80) == 0 )          //ASCII character (lead bit zero)
55   {
56     length = 1;
57   }
58   else if (( leadByte & 0xe0 ) == 0xc0 ) //110x xxxx
59   {
60     length = 2;
61   }
62   else if (( leadByte & 0xf0 ) == 0xe0 ) //1110 xxxx
63   {
64     length = 3;
65   }
66   else if (( leadByte & 0xf8 ) == 0xf0 ) //1111 0xxx
67   {
68     length = 4;
69   }
70
71   return length;
72 }
73
74 // Static function calls used by ecore 'c' style callback registration
75 void Commit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
76 {
77   if ( data )
78   {
79     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
80     imfManager->CommitReceived( data, imfContext, event_info );
81   }
82 }
83
84 void PreEdit( void *data, Ecore_IMF_Context *imfContext, void *event_info )
85 {
86   if ( data )
87   {
88     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
89     imfManager->PreEditChanged( data, imfContext, event_info );
90   }
91 }
92
93 Eina_Bool ImfRetrieveSurrounding(void *data, Ecore_IMF_Context *imfContext, char** text, int* cursorPosition )
94 {
95   if ( data )
96   {
97     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
98     return imfManager->RetrieveSurrounding( data, imfContext, text, cursorPosition );
99   }
100   else
101   {
102     return false;
103   }
104 }
105
106 /**
107  * Called when an IMF delete surrounding event is received.
108  * Here we tell the application that it should delete a certain range.
109  */
110 void ImfDeleteSurrounding( void *data, Ecore_IMF_Context *imfContext, void *event_info )
111 {
112   if ( data )
113   {
114     ImfManager* imfManager = reinterpret_cast< ImfManager* > ( data );
115     imfManager->DeleteSurrounding( data, imfContext, event_info );
116   }
117 }
118
119 BaseHandle Create()
120 {
121   return ImfManager::Get();
122 }
123
124 TypeRegistration IMF_MANAGER_TYPE( typeid(Dali::ImfManager), typeid(Dali::BaseHandle), Create );
125
126 } // unnamed namespace
127
128 bool ImfManager::IsAvailable()
129 {
130   bool available( false );
131
132   Dali::SingletonService service( SingletonService::Get() );
133   if ( service )
134   {
135     available = service.GetSingleton( typeid( Dali::ImfManager ) );
136   }
137
138   return available;
139 }
140
141 Dali::ImfManager ImfManager::Get()
142 {
143   Dali::ImfManager manager;
144
145   Dali::SingletonService service( SingletonService::Get() );
146   if ( service )
147   {
148     // Check whether the singleton is already created
149     Dali::BaseHandle handle = service.GetSingleton( typeid( Dali::ImfManager ) );
150     if( handle )
151     {
152       // If so, downcast the handle
153       manager = Dali::ImfManager( dynamic_cast< ImfManager* >( handle.GetObjectPtr() ) );
154     }
155     else if ( Adaptor::IsAvailable() )
156     {
157       // Create instance and register singleton only if the adaptor is available
158
159       Adaptor& adaptorImpl( Adaptor::GetImplementation( Adaptor::Get() ) );
160       Any nativeWindow = adaptorImpl.GetNativeWindowHandle();
161
162       // The Ecore_Wl_Window needs to use the ImfManager.
163       // Only when the render surface is window, we can get the Ecore_Wl_Window.
164       Ecore_Wl_Window *ecoreWwin( AnyCast< Ecore_Wl_Window* >( nativeWindow ) );
165       if (ecoreWwin)
166       {
167         // If we fail to get Ecore_Wl_Window, we can't use the ImfManager correctly.
168         // Thus you have to call "ecore_imf_context_client_window_set" somewhere.
169         // In EvasPlugIn, this function is called in EvasPlugin::ConnectEcoreEvent().
170
171         manager = Dali::ImfManager( new ImfManager( ecoreWwin ) );
172         service.Register( typeid( manager ), manager );
173       }
174       else
175       {
176         DALI_LOG_ERROR("Failed to get native window handle\n");
177       }
178     }
179   }
180
181   return manager;
182 }
183
184 ImfManager::ImfManager( Ecore_Wl_Window *ecoreWlwin )
185 : mIMFContext(),
186   mIMFCursorPosition( 0 ),
187   mSurroundingText(),
188   mRestoreAfterFocusLost( false ),
189   mIdleCallbackConnected( false )
190 {
191   ecore_imf_init();
192   CreateContext( ecoreWlwin );
193
194   ConnectCallbacks();
195   VirtualKeyboard::ConnectCallbacks( mIMFContext );
196 }
197
198 ImfManager::~ImfManager()
199 {
200   VirtualKeyboard::DisconnectCallbacks( mIMFContext );
201   DisconnectCallbacks();
202
203   DeleteContext();
204   ecore_imf_shutdown();
205 }
206
207
208 void ImfManager::CreateContext( Ecore_Wl_Window *ecoreWlwin )
209 {
210   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CreateContext\n" );
211
212   const char *contextId = ecore_imf_context_default_id_get();
213   if( contextId )
214   {
215     mIMFContext = ecore_imf_context_add( contextId );
216
217     if( mIMFContext )
218     {
219       if( ecoreWlwin )
220       {
221           ecore_imf_context_client_window_set( mIMFContext,
222             reinterpret_cast<void*>( ecore_wl_window_id_get(ecoreWlwin)) );
223       }
224     }
225     else
226     {
227       DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
228     }
229   }
230   else
231   {
232     DALI_LOG_WARNING("IMF Unable to get IMF Context\n");
233   }
234 }
235
236 void ImfManager::DeleteContext()
237 {
238   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteContext\n" );
239
240   if ( mIMFContext )
241   {
242     mIMFContext = NULL;
243   }
244 }
245
246 // Callbacks for predicitive text support.
247 void ImfManager::ConnectCallbacks()
248 {
249   if ( mIMFContext )
250   {
251     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::ConnectCallbacks\n" );
252
253     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit,    this );
254     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit,     this );
255     ecore_imf_context_event_callback_add( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding, this );
256
257     ecore_imf_context_retrieve_surrounding_callback_set( mIMFContext, ImfRetrieveSurrounding, this);
258   }
259 }
260
261 void ImfManager::DisconnectCallbacks()
262 {
263   if ( mIMFContext )
264   {
265     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DisconnectCallbacks\n" );
266
267     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_PREEDIT_CHANGED,    PreEdit );
268     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_COMMIT,             Commit );
269     ecore_imf_context_event_callback_del( mIMFContext, ECORE_IMF_CALLBACK_DELETE_SURROUNDING, ImfDeleteSurrounding );
270
271     // We do not need to unset the retrieve surrounding callback.
272   }
273 }
274
275 void ImfManager::Activate()
276 {
277   // Reset mIdleCallbackConnected
278   mIdleCallbackConnected = false;
279
280   if ( mIMFContext )
281   {
282     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Activate\n" );
283
284     ecore_imf_context_focus_in( mIMFContext );
285
286     // emit keyboard activated signal
287     Dali::ImfManager handle( this );
288     mActivatedSignal.Emit( handle );
289   }
290 }
291
292 void ImfManager::Deactivate()
293 {
294   if( mIMFContext )
295   {
296     DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Deactivate\n" );
297
298     Reset();
299     ecore_imf_context_focus_out( mIMFContext );
300   }
301
302   // Reset mIdleCallbackConnected
303   mIdleCallbackConnected = false;
304 }
305
306 void ImfManager::Reset()
307 {
308   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::Reset\n" );
309
310   if ( mIMFContext )
311   {
312     ecore_imf_context_reset( mIMFContext );
313   }
314 }
315
316 Ecore_IMF_Context* ImfManager::GetContext()
317 {
318   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetContext\n" );
319
320   return mIMFContext;
321 }
322
323 bool ImfManager::RestoreAfterFocusLost() const
324 {
325   return mRestoreAfterFocusLost;
326 }
327
328 void ImfManager::SetRestoreAfterFocusLost( bool toggle )
329 {
330   mRestoreAfterFocusLost = toggle;
331 }
332
333 /**
334  * Called when an IMF Pre-Edit changed event is received.
335  * We are still predicting what the user is typing.  The latest string is what the IMF module thinks
336  * the user wants to type.
337  */
338 void ImfManager::PreEditChanged( void*, Ecore_IMF_Context* imfContext, void* event_info )
339 {
340   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::PreEditChanged\n" );
341
342   char* preEditString( NULL );
343   int cursorPosition( 0 );
344   Eina_List* attrs = NULL;
345   Eina_List* l = NULL;
346
347   Ecore_IMF_Preedit_Attr* attr;
348
349   // Retrieves attributes as well as the string the cursor position offset from start of pre-edit string.
350   // the attributes (attrs) is used in languages that use the soft arrows keys to insert characters into a current pre-edit string.
351   ecore_imf_context_preedit_string_with_attributes_get( imfContext, &preEditString, &attrs, &cursorPosition );
352
353   if ( attrs )
354   {
355     // iterate through the list of attributes getting the type, start and end position.
356     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) ) ))
357     {
358 #ifdef DALI_PROFILE_UBUNTU
359       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB3 ) // (Ecore_IMF)
360 #else // DALI_PROFILE_UBUNTU
361       if ( attr->preedit_type == ECORE_IMF_PREEDIT_TYPE_SUB4 ) // (Ecore_IMF)
362 #endif // DALI_PROFILE_UBUNTU
363       {
364         // 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.
365
366         size_t visualCharacterIndex = 0;
367         size_t byteIndex = 0;
368
369         // iterate through null terminated string checking each character's position against the given byte position ( attr->end_index ).
370         const char leadByte = preEditString[byteIndex];
371         while( leadByte != '\0' )
372         {
373           // attr->end_index is provided as a byte position not character and we need to know the character position.
374           const size_t currentSequenceLength = Utf8SequenceLength( leadByte ); // returns number of bytes used to represent character.
375           if ( byteIndex == attr->end_index )
376           {
377             cursorPosition = visualCharacterIndex;
378             break;
379             // end loop as found cursor position that matches byte position
380           }
381           else
382           {
383             byteIndex += currentSequenceLength; // jump to next character
384             visualCharacterIndex++;  // increment character count so we know our position for when we get a match
385           }
386
387           DALI_ASSERT_DEBUG( visualCharacterIndex < strlen( preEditString ));
388         }
389       }
390     }
391   }
392
393   if ( Dali::Adaptor::IsAvailable() )
394   {
395     Dali::ImfManager handle( this );
396     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::PREEDIT, preEditString, cursorPosition, 0 );
397     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
398
399     if ( callbackData.update )
400     {
401       SetCursorPosition( callbackData.cursorPosition );
402       SetSurroundingText( callbackData.currentText );
403
404       NotifyCursorPosition();
405     }
406
407     if ( callbackData.preeditResetRequired )
408     {
409       Reset();
410     }
411   }
412   free( preEditString );
413 }
414
415 void ImfManager::CommitReceived( void*, Ecore_IMF_Context* imfContext, void* event_info )
416 {
417   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::CommitReceived\n" );
418
419   if ( Dali::Adaptor::IsAvailable() )
420   {
421     const std::string keyString( static_cast<char*>( event_info ) );
422
423     Dali::ImfManager handle( this );
424     Dali::ImfManager::ImfEventData imfEventData( Dali::ImfManager::COMMIT, keyString, 0, 0 );
425     Dali::ImfManager::ImfCallbackData callbackData = mEventSignal.Emit( handle, imfEventData );
426
427     if( callbackData.update )
428     {
429       SetCursorPosition( callbackData.cursorPosition );
430       SetSurroundingText( callbackData.currentText );
431
432       NotifyCursorPosition();
433     }
434   }
435 }
436
437 /**
438  * Called when an IMF retrieve surround event is received.
439  * Here the IMF module wishes to know the string we are working with and where within the string the cursor is
440  * We need to signal the application to tell us this information.
441  */
442 Eina_Bool ImfManager::RetrieveSurrounding( void* data, Ecore_IMF_Context* imfContext, char** text, int* cursorPosition )
443 {
444   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::RetrieveSurrounding\n" );
445
446   Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::GETSURROUNDING, std::string(), 0, 0 );
447   Dali::ImfManager handle( this );
448   mEventSignal.Emit( handle, imfData );
449
450   if( text )
451   {
452     *text = strdup( mSurroundingText.c_str() );
453   }
454
455   if( cursorPosition )
456   {
457     *cursorPosition = mIMFCursorPosition;
458   }
459
460   return EINA_TRUE;
461 }
462
463 /**
464  * Called when an IMF delete surrounding event is received.
465  * Here we tell the application that it should delete a certain range.
466  */
467 void ImfManager::DeleteSurrounding( void* data, Ecore_IMF_Context* imfContext, void* event_info )
468 {
469   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::DeleteSurrounding\n" );
470
471   if( Dali::Adaptor::IsAvailable() )
472   {
473     Ecore_IMF_Event_Delete_Surrounding* deleteSurroundingEvent = static_cast<Ecore_IMF_Event_Delete_Surrounding*>( event_info );
474
475     Dali::ImfManager::ImfEventData imfData( Dali::ImfManager::DELETESURROUNDING, std::string(), deleteSurroundingEvent->offset, deleteSurroundingEvent->n_chars );
476     Dali::ImfManager handle( this );
477     mEventSignal.Emit( handle, imfData );
478   }
479 }
480
481 void ImfManager::NotifyCursorPosition()
482 {
483   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::NotifyCursorPosition\n" );
484
485   if( mIMFContext )
486   {
487     ecore_imf_context_cursor_position_set( mIMFContext, mIMFCursorPosition );
488   }
489 }
490
491 void ImfManager::SetCursorPosition( unsigned int cursorPosition )
492 {
493   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetCursorPosition\n" );
494
495   mIMFCursorPosition = static_cast<int>( cursorPosition );
496 }
497
498 unsigned int ImfManager::GetCursorPosition() const
499 {
500   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetCursorPosition\n" );
501
502   return static_cast<unsigned int>( mIMFCursorPosition );
503 }
504
505 void ImfManager::SetSurroundingText( const std::string& text )
506 {
507   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::SetSurroundingText\n" );
508
509   mSurroundingText = text;
510 }
511
512 const std::string& ImfManager::GetSurroundingText() const
513 {
514   DALI_LOG_INFO( gLogFilter, Debug::General, "ImfManager::GetSurroundingText\n" );
515
516   return mSurroundingText;
517 }
518
519 } // Adaptor
520
521 } // Internal
522
523 } // Dali