1 /***************************************************************************
2 **
3 ** Copyright (C) 2013 BlackBerry Limited. All rights reserved.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "qqnxinputcontext_imf.h"
41 #include "qqnxabstractvirtualkeyboard.h"
42 #include "qqnxintegration.h"
43 #include "qqnxscreen.h"
44 #include "qqnxscreeneventhandler.h"
45 
46 #include <QtGui/QGuiApplication>
47 #include <QtGui/QInputMethodEvent>
48 #include <QtGui/QTextCharFormat>
49 
50 #include <QtCore/QDebug>
51 #include <QtCore/QMutex>
52 #include <QtCore/QVariant>
53 #include <QtCore/QVariantHash>
54 #include <QtCore/QWaitCondition>
55 #include <QtCore/QQueue>
56 #include <QtCore/QGlobalStatic>
57 
58 #include <dlfcn.h>
59 #include "imf/imf_client.h"
60 #include "imf/input_control.h"
61 #include <process.h>
62 #include <sys/keycodes.h>
63 
64 #if defined(QQNXINPUTCONTEXT_IMF_EVENT_DEBUG)
65 #define qInputContextIMFRequestDebug qDebug
66 #else
67 #define qInputContextIMFRequestDebug QT_NO_QDEBUG_MACRO
68 #endif
69 
70 #if defined(QQNXINPUTCONTEXT_DEBUG)
71 #define qInputContextDebug qDebug
72 #else
73 #define qInputContextDebug QT_NO_QDEBUG_MACRO
74 #endif
75 
76 static QQnxInputContext *sInputContextInstance;
77 static QColor sSelectedColor(0,0xb8,0,85);
78 
79 static const input_session_t *sSpellCheckSession = 0;
80 static const input_session_t *sInputSession = 0;
isSessionOkay(input_session_t * ic)81 static bool isSessionOkay(input_session_t *ic)
82 {
83     return ic !=0 && sInputSession != 0 && ic->component_id == sInputSession->component_id;
84 }
85 
86 enum ImfEventType
87 {
88     ImfCommitText,
89     ImfDeleteSurroundingText,
90     ImfFinishComposingText,
91     ImfGetCursorPosition,
92     ImfGetTextAfterCursor,
93     ImfGetTextBeforeCursor,
94     ImfSendEvent,
95     ImfSetComposingRegion,
96     ImfSetComposingText,
97     ImfIsTextSelected,
98     ImfIsAllTextSelected,
99 };
100 
101 struct SpellCheckInfo {
SpellCheckInfoSpellCheckInfo102     SpellCheckInfo(void *_context, void (*_spellCheckDone)(void *, const QString &, const QList<int> &))
103         : context(_context), spellCheckDone(_spellCheckDone) {}
104     void *context;
105     void (*spellCheckDone)(void *, const QString &, const QList<int> &);
106 };
107 Q_GLOBAL_STATIC(QQueue<SpellCheckInfo>, sSpellCheckQueue)
108 
109 // IMF requests all arrive on IMF's own thread and have to be posted to the main thread to be processed.
110 class QQnxImfRequest
111 {
112 public:
QQnxImfRequest(input_session_t * _session,ImfEventType _type)113     QQnxImfRequest(input_session_t *_session, ImfEventType _type)
114         : session(_session), type(_type)
115         { }
~QQnxImfRequest()116     ~QQnxImfRequest() { }
117 
118     input_session_t *session;
119     ImfEventType type;
120     union {
121         struct {
122             int32_t n;
123             int32_t flags;
124             bool before;
125             spannable_string_t *result;
126         } gtac; // ic_get_text_before_cursor/ic_get_text_after_cursor
127         struct {
128             int32_t result;
129         } gcp; // ic_get_cursor_position
130         struct {
131             int32_t start;
132             int32_t end;
133             int32_t result;
134         } scr; // ic_set_composing_region
135         struct {
136             spannable_string_t* text;
137             int32_t new_cursor_position;
138             int32_t result;
139         } sct; // ic_set_composing_text
140         struct {
141             spannable_string_t* text;
142             int32_t new_cursor_position;
143             int32_t result;
144         } ct; // ic_commit_text
145         struct {
146             int32_t result;
147         } fct; // ic_finish_composing_text
148         struct {
149             int32_t left_length;
150             int32_t right_length;
151             int32_t result;
152         } dst; // ic_delete_surrounding_text
153         struct {
154             event_t *event;
155             int32_t result;
156         } sae;  // ic_send_async_event/ic_send_event
157         struct {
158             int32_t *pIsSelected;
159             int32_t result;
160         } its;  // ic_is_text_selected/ic_is_all_text_selected
161     };
162 };
163 
164 // Invoke an IMF initiated request synchronously on Qt's main thread.  As describe below all
165 // IMF requests are made from another thread but need to be executed on the main thread.
executeIMFRequest(QQnxImfRequest * event)166 static void executeIMFRequest(QQnxImfRequest *event)
167 {
168     QMetaObject::invokeMethod(sInputContextInstance,
169                               "processImfEvent",
170                               Qt::BlockingQueuedConnection,
171                               Q_ARG(QQnxImfRequest*, event));
172 }
173 
174 // The following functions (ic_*) are callback functions called by the input system to query information
175 // about the text object that currently has focus or to make changes to it.  All calls are made from the
176 // input system's own thread.  The pattern for each callback function is to copy its parameters into
177 // a QQnxImfRequest structure and call executeIMFRequest to have it passed synchronously to Qt's main thread.
178 // Any return values should be pre-initialised with suitable default values as in some cases
179 // (e.g. a stale session) the call will return without having executed any request specific code.
180 //
181 // To make the correspondence more obvious, the names of these functions match those defined in the headers.
182 // They're in an anonymous namespace to avoid compiler conflicts with external functions defined with the
183 // same names.
184 namespace
185 {
186 
187 // See comment at beginning of namespace declaration for general information
ic_begin_batch_edit(input_session_t * ic)188 static int32_t ic_begin_batch_edit(input_session_t *ic)
189 {
190     Q_UNUSED(ic);
191 
192     // Ignore silently.
193     return 0;
194 }
195 
196 // End composition, committing the supplied text.
197 // See comment at beginning of namespace declaration for general information
ic_commit_text(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)198 static int32_t ic_commit_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
199 {
200     qInputContextIMFRequestDebug();
201 
202     QQnxImfRequest event(ic, ImfCommitText);
203     event.ct.text = text;
204     event.ct.new_cursor_position = new_cursor_position;
205     event.ct.result = -1;
206     executeIMFRequest(&event);
207 
208     return event.ct.result;
209 }
210 
211 // Delete left_length characters before and right_length characters after the cursor.
212 // See comment at beginning of namespace declaration for general information
ic_delete_surrounding_text(input_session_t * ic,int32_t left_length,int32_t right_length)213 static int32_t ic_delete_surrounding_text(input_session_t *ic, int32_t left_length, int32_t right_length)
214 {
215     qInputContextIMFRequestDebug();
216 
217     QQnxImfRequest event(ic, ImfDeleteSurroundingText);
218     event.dst.left_length = left_length;
219     event.dst.right_length = right_length;
220     event.dst.result = -1;
221     executeIMFRequest(&event);
222 
223     return event.dst.result;
224 }
225 
226 // See comment at beginning of namespace declaration for general information
ic_end_batch_edit(input_session_t * ic)227 static int32_t ic_end_batch_edit(input_session_t *ic)
228 {
229     Q_UNUSED(ic);
230 
231     // Ignore silently.
232     return 0;
233 }
234 
235 // End composition, committing what's there.
236 // See comment at beginning of namespace declaration for general information
ic_finish_composing_text(input_session_t * ic)237 static int32_t ic_finish_composing_text(input_session_t *ic)
238 {
239     qInputContextIMFRequestDebug();
240 
241     QQnxImfRequest event(ic, ImfFinishComposingText);
242     event.fct.result = -1;
243     executeIMFRequest(&event);
244 
245     return event.fct.result;
246 }
247 
248 // Return the position of the cursor.
249 // See comment at beginning of namespace declaration for general information
ic_get_cursor_position(input_session_t * ic)250 static int32_t ic_get_cursor_position(input_session_t *ic)
251 {
252     qInputContextIMFRequestDebug();
253 
254     QQnxImfRequest event(ic, ImfGetCursorPosition);
255     event.gcp.result = -1;
256     executeIMFRequest(&event);
257 
258     return event.gcp.result;
259 }
260 
261 // Return the n characters after the cursor.
262 // See comment at beginning of namespace declaration for general information
ic_get_text_after_cursor(input_session_t * ic,int32_t n,int32_t flags)263 static spannable_string_t *ic_get_text_after_cursor(input_session_t *ic, int32_t n, int32_t flags)
264 {
265     qInputContextIMFRequestDebug();
266 
267     QQnxImfRequest event(ic, ImfGetTextAfterCursor);
268     event.gtac.n = n;
269     event.gtac.flags = flags;
270     event.gtac.result = 0;
271     executeIMFRequest(&event);
272 
273     return event.gtac.result;
274 }
275 
276 // Return the n characters before the cursor.
277 // See comment at beginning of namespace declaration for general information
ic_get_text_before_cursor(input_session_t * ic,int32_t n,int32_t flags)278 static spannable_string_t *ic_get_text_before_cursor(input_session_t *ic, int32_t n, int32_t flags)
279 {
280     qInputContextIMFRequestDebug();
281 
282     QQnxImfRequest event(ic, ImfGetTextBeforeCursor);
283     event.gtac.n = n;
284     event.gtac.flags = flags;
285     event.gtac.result = 0;
286     executeIMFRequest(&event);
287 
288     return event.gtac.result;
289 }
290 
291 // Process an event from IMF.  Primarily used for reflecting back keyboard events.
292 // See comment at beginning of namespace declaration for general information
ic_send_event(input_session_t * ic,event_t * event)293 static int32_t ic_send_event(input_session_t *ic, event_t *event)
294 {
295     qInputContextIMFRequestDebug();
296 
297     QQnxImfRequest imfEvent(ic, ImfSendEvent);
298     imfEvent.sae.event = event;
299     imfEvent.sae.result = -1;
300     executeIMFRequest(&imfEvent);
301 
302     return imfEvent.sae.result;
303 }
304 
305 // Same as ic_send_event.
306 // See comment at beginning of namespace declaration for general information
ic_send_async_event(input_session_t * ic,event_t * event)307 static int32_t ic_send_async_event(input_session_t *ic, event_t *event)
308 {
309     qInputContextIMFRequestDebug();
310 
311     // There's no difference from our point of view between ic_send_event & ic_send_async_event
312     QQnxImfRequest imfEvent(ic, ImfSendEvent);
313     imfEvent.sae.event = event;
314     imfEvent.sae.result = -1;
315     executeIMFRequest(&imfEvent);
316 
317     return imfEvent.sae.result;
318 }
319 
320 // Set the range of text between start and end as the composition range.
321 // See comment at beginning of namespace declaration for general information
ic_set_composing_region(input_session_t * ic,int32_t start,int32_t end)322 static int32_t ic_set_composing_region(input_session_t *ic, int32_t start, int32_t end)
323 {
324     qInputContextIMFRequestDebug();
325 
326     QQnxImfRequest event(ic, ImfSetComposingRegion);
327     event.scr.start = start;
328     event.scr.end = end;
329     event.scr.result = -1;
330     executeIMFRequest(&event);
331 
332     return event.scr.result;
333 }
334 
335 // Update the composition range with the supplied text.  This can be called when no composition
336 // range is in effect in which case one is started at the current cursor position.
337 // See comment at beginning of namespace declaration for general information
ic_set_composing_text(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)338 static int32_t ic_set_composing_text(input_session_t *ic, spannable_string_t *text, int32_t new_cursor_position)
339 {
340     qInputContextIMFRequestDebug();
341 
342     QQnxImfRequest event(ic, ImfSetComposingText);
343     event.sct.text = text;
344     event.sct.new_cursor_position = new_cursor_position;
345     event.sct.result = -1;
346     executeIMFRequest(&event);
347 
348     return event.sct.result;
349 }
350 
351 // Indicate if any text is selected
352 // See comment at beginning of namespace declaration for general information
ic_is_text_selected(input_session_t * ic,int32_t * pIsSelected)353 static int32_t ic_is_text_selected(input_session_t* ic, int32_t* pIsSelected)
354 {
355     qInputContextIMFRequestDebug();
356 
357     QQnxImfRequest event(ic, ImfIsTextSelected);
358     event.its.pIsSelected = pIsSelected;
359     event.its.result = -1;
360     executeIMFRequest(&event);
361 
362     return event.its.result;
363 }
364 
365 // Indicate if all text is selected
366 // See comment at beginning of namespace declaration for general information
ic_is_all_text_selected(input_session_t * ic,int32_t * pIsSelected)367 static int32_t ic_is_all_text_selected(input_session_t* ic, int32_t* pIsSelected)
368 {
369     qInputContextIMFRequestDebug();
370 
371     QQnxImfRequest event(ic, ImfIsAllTextSelected);
372     event.its.pIsSelected = pIsSelected;
373     event.its.result = -1;
374     executeIMFRequest(&event);
375 
376     return event.its.result;
377 }
378 
379 // LCOV_EXCL_START - exclude from code coverage analysis
380 // The following functions are defined in the IMF headers but are not currently called.
381 
382 // Not currently used
ic_perform_editor_action(input_session_t * ic,int32_t editor_action)383 static int32_t ic_perform_editor_action(input_session_t *ic, int32_t editor_action)
384 {
385     Q_UNUSED(ic);
386     Q_UNUSED(editor_action);
387 
388     qCritical("ic_perform_editor_action not implemented");
389     return 0;
390 }
391 
392 // Not currently used
ic_report_fullscreen_mode(input_session_t * ic,int32_t enabled)393 static int32_t ic_report_fullscreen_mode(input_session_t *ic, int32_t enabled)
394 {
395     Q_UNUSED(ic);
396     Q_UNUSED(enabled);
397 
398     qCritical("ic_report_fullscreen_mode not implemented");
399     return 0;
400 }
401 
402 // Not currently used
ic_get_extracted_text(input_session_t * ic,extracted_text_request_t * request,int32_t flags)403 static extracted_text_t *ic_get_extracted_text(input_session_t *ic, extracted_text_request_t *request, int32_t flags)
404 {
405     Q_UNUSED(ic);
406     Q_UNUSED(request);
407     Q_UNUSED(flags);
408 
409     qCritical("ic_get_extracted_text not implemented");
410     return 0;
411 }
412 
413 // Not currently used
ic_get_selected_text(input_session_t * ic,int32_t flags)414 static spannable_string_t *ic_get_selected_text(input_session_t *ic, int32_t flags)
415 {
416     Q_UNUSED(ic);
417     Q_UNUSED(flags);
418 
419     qCritical("ic_get_selected_text not implemented");
420     return 0;
421 }
422 
423 // Not currently used
ic_get_cursor_caps_mode(input_session_t * ic,int32_t req_modes)424 static int32_t ic_get_cursor_caps_mode(input_session_t *ic, int32_t req_modes)
425 {
426     Q_UNUSED(ic);
427     Q_UNUSED(req_modes);
428 
429     qCritical("ic_get_cursor_caps_mode not implemented");
430     return 0;
431 }
432 
433 // Not currently used
ic_clear_meta_key_states(input_session_t * ic,int32_t states)434 static int32_t ic_clear_meta_key_states(input_session_t *ic, int32_t states)
435 {
436     Q_UNUSED(ic);
437     Q_UNUSED(states);
438 
439     qCritical("ic_clear_meta_key_states not implemented");
440     return 0;
441 }
442 
443 // Not currently used
ic_set_selection(input_session_t * ic,int32_t start,int32_t end)444 static int32_t ic_set_selection(input_session_t *ic, int32_t start, int32_t end)
445 {
446     Q_UNUSED(ic);
447     Q_UNUSED(start);
448     Q_UNUSED(end);
449 
450     qCritical("ic_set_selection not implemented");
451     return 0;
452 }
453 
454 // End of un-hittable code
455 // LCOV_EXCL_STOP
456 
457 
458 static connection_interface_t ic_funcs = {
459     ic_begin_batch_edit,
460     ic_clear_meta_key_states,
461     ic_commit_text,
462     ic_delete_surrounding_text,
463     ic_end_batch_edit,
464     ic_finish_composing_text,
465     ic_get_cursor_caps_mode,
466     ic_get_cursor_position,
467     ic_get_extracted_text,
468     ic_get_selected_text,
469     ic_get_text_after_cursor,
470     ic_get_text_before_cursor,
471     ic_perform_editor_action,
472     ic_report_fullscreen_mode,
473     0, //ic_send_key_event
474     ic_send_event,
475     ic_send_async_event,
476     ic_set_composing_region,
477     ic_set_composing_text,
478     ic_set_selection,
479     0, //ic_set_candidates,
480     0, //ic_get_cursor_offset,
481     0, //ic_get_selection,
482     ic_is_text_selected,
483     ic_is_all_text_selected,
484     0, //ic_get_max_cursor_offset_t
485 };
486 
487 } // namespace
488 
489 static void
initEvent(event_t * pEvent,const input_session_t * pSession,EventType eventType,int eventId,int eventSize)490 initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId, int eventSize)
491 {
492     static int s_transactionId;
493 
494     // Make sure structure is squeaky clean since it's not clear just what is significant.
495     memset(pEvent, 0, eventSize);
496     pEvent->event_type = eventType;
497     pEvent->event_id = eventId;
498     pEvent->pid = getpid();
499     pEvent->component_id = pSession->component_id;
500     pEvent->transaction_id = ++s_transactionId;
501 }
502 
toSpannableString(const QString & text)503 static spannable_string_t *toSpannableString(const QString &text)
504 {
505     qInputContextDebug() << text;
506 
507     spannable_string_t *pString = static_cast<spannable_string_t *>(malloc(sizeof(spannable_string_t)));
508     pString->str =  static_cast<wchar_t *>(malloc(sizeof(wchar_t) * text.length() + 1));
509     pString->length = text.toWCharArray(pString->str);
510     pString->spans = 0;
511     pString->spans_count = 0;
512     pString->str[pString->length] = 0;
513 
514     return pString;
515 }
516 
517 
518 static const input_session_t *(*p_ictrl_open_session)(connection_interface_t *) = 0;
519 static void (*p_ictrl_close_session)(input_session_t *) = 0;
520 static int32_t (*p_ictrl_dispatch_event)(event_t*) = 0;
521 static int32_t (*p_imf_client_init)() = 0;
522 static void (*p_imf_client_disconnect)() = 0;
523 static int32_t (*p_vkb_init_selection_service)() = 0;
524 static int32_t (*p_ictrl_get_num_active_sessions)() = 0;
525 static bool s_imfInitFailed = false;
526 
imfAvailable()527 static bool imfAvailable()
528 {
529     static bool s_imfDisabled = getenv("DISABLE_IMF") != 0;
530     static bool s_imfReady = false;
531 
532     if ( s_imfInitFailed || s_imfDisabled)
533         return false;
534     else if ( s_imfReady )
535         return true;
536 
537     if ( p_imf_client_init == 0 ) {
538         void *handle = dlopen("libinput_client.so.1", 0);
539         if (Q_UNLIKELY(!handle)) {
540             qCritical("libinput_client.so.1 is not present - IMF services are disabled.");
541             s_imfDisabled = true;
542             return false;
543         }
544         p_imf_client_init = (int32_t (*)()) dlsym(handle, "imf_client_init");
545         p_imf_client_disconnect = (void (*)()) dlsym(handle, "imf_client_disconnect");
546         p_ictrl_open_session = (const input_session_t *(*)(connection_interface_t *))dlsym(handle, "ictrl_open_session");
547         p_ictrl_close_session = (void (*)(input_session_t *))dlsym(handle, "ictrl_close_session");
548         p_ictrl_dispatch_event = (int32_t (*)(event_t *))dlsym(handle, "ictrl_dispatch_event");
549         p_vkb_init_selection_service = (int32_t (*)())dlsym(handle, "vkb_init_selection_service");
550         p_ictrl_get_num_active_sessions = (int32_t (*)())dlsym(handle, "ictrl_get_num_active_sessions");
551 
552         if (Q_UNLIKELY(!p_imf_client_init || !p_ictrl_open_session || !p_ictrl_dispatch_event)) {
553             p_ictrl_open_session = 0;
554             p_ictrl_dispatch_event = 0;
555             s_imfDisabled = true;
556             qCritical("libinput_client.so.1 did not contain the correct symbols, library mismatch? IMF services are disabled.");
557             return false;
558         }
559 
560         s_imfReady = true;
561     }
562 
563     return s_imfReady;
564 }
565 
566 QT_BEGIN_NAMESPACE
567 
QQnxInputContext(QQnxIntegration * integration,QQnxAbstractVirtualKeyboard & keyboard)568 QQnxInputContext::QQnxInputContext(QQnxIntegration *integration, QQnxAbstractVirtualKeyboard &keyboard) :
569          QPlatformInputContext(),
570          m_caretPosition(0),
571          m_isComposing(false),
572          m_isUpdatingText(false),
573          m_inputPanelVisible(false),
574          m_inputPanelLocale(QLocale::c()),
575          m_focusObject(0),
576          m_integration(integration),
577          m_virtualKeyboard(keyboard)
578 {
579     qInputContextDebug();
580 
581     if (!imfAvailable())
582         return;
583 
584     // Save a pointer to ourselves so we can execute calls from IMF through executeIMFRequest
585     // In practice there will only ever be a single instance.
586     Q_ASSERT(sInputContextInstance == 0);
587     sInputContextInstance = this;
588 
589     if (Q_UNLIKELY(p_imf_client_init() != 0)) {
590         s_imfInitFailed = true;
591         qCritical("imf_client_init failed - IMF services will be unavailable");
592     }
593 
594     connect(&keyboard, SIGNAL(visibilityChanged(bool)), this, SLOT(keyboardVisibilityChanged(bool)));
595     connect(&keyboard, SIGNAL(localeChanged(QLocale)), this, SLOT(keyboardLocaleChanged(QLocale)));
596     keyboardVisibilityChanged(keyboard.isVisible());
597     keyboardLocaleChanged(keyboard.locale());
598 }
599 
~QQnxInputContext()600 QQnxInputContext::~QQnxInputContext()
601 {
602     qInputContextDebug();
603 
604     Q_ASSERT(sInputContextInstance == this);
605     sInputContextInstance = 0;
606 
607     if (!imfAvailable())
608         return;
609 
610     p_imf_client_disconnect();
611 }
612 
isValid() const613 bool QQnxInputContext::isValid() const
614 {
615     return imfAvailable();
616 }
617 
processImfEvent(QQnxImfRequest * imfEvent)618 void QQnxInputContext::processImfEvent(QQnxImfRequest *imfEvent)
619 {
620     // If input session is no longer current, just bail, imfEvent should already be set with the appropriate
621     // return value.  The only exception is spell check events since they're not associated with the
622     // object with focus.
623     if (imfEvent->type != ImfSendEvent || imfEvent->sae.event->event_type != EVENT_SPELL_CHECK) {
624         if (!isSessionOkay(imfEvent->session))
625             return;
626     }
627 
628     switch (imfEvent->type) {
629     case ImfCommitText:
630         imfEvent->ct.result = onCommitText(imfEvent->ct.text, imfEvent->ct.new_cursor_position);
631         break;
632 
633     case ImfDeleteSurroundingText:
634         imfEvent->dst.result = onDeleteSurroundingText(imfEvent->dst.left_length, imfEvent->dst.right_length);
635         break;
636 
637     case ImfFinishComposingText:
638         imfEvent->fct.result = onFinishComposingText();
639         break;
640 
641     case ImfGetCursorPosition:
642         imfEvent->gcp.result = onGetCursorPosition();
643         break;
644 
645     case ImfGetTextAfterCursor:
646         imfEvent->gtac.result = onGetTextAfterCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
647         break;
648 
649     case ImfGetTextBeforeCursor:
650         imfEvent->gtac.result = onGetTextBeforeCursor(imfEvent->gtac.n, imfEvent->gtac.flags);
651         break;
652 
653     case ImfSendEvent:
654         imfEvent->sae.result = onSendEvent(imfEvent->sae.event);
655         break;
656 
657     case ImfSetComposingRegion:
658         imfEvent->scr.result = onSetComposingRegion(imfEvent->scr.start, imfEvent->scr.end);
659         break;
660 
661     case ImfSetComposingText:
662         imfEvent->sct.result = onSetComposingText(imfEvent->sct.text, imfEvent->sct.new_cursor_position);
663         break;
664 
665     case ImfIsTextSelected:
666         imfEvent->its.result = onIsTextSelected(imfEvent->its.pIsSelected);
667         break;
668 
669     case ImfIsAllTextSelected:
670         imfEvent->its.result = onIsAllTextSelected(imfEvent->its.pIsSelected);
671         break;
672     }; //switch
673 }
674 
filterEvent(const QEvent * event)675 bool QQnxInputContext::filterEvent( const QEvent *event )
676 {
677     qInputContextDebug() << event;
678 
679     switch (event->type()) {
680     case QEvent::CloseSoftwareInputPanel:
681         return dispatchCloseSoftwareInputPanel();
682 
683     case QEvent::RequestSoftwareInputPanel:
684         return dispatchRequestSoftwareInputPanel();
685 
686     default:
687         return false;
688     }
689 }
690 
keyboardRect() const691 QRectF QQnxInputContext::keyboardRect() const
692 {
693     QRect screenGeometry = m_integration->primaryDisplay()->geometry();
694     return QRectF(screenGeometry.x(), screenGeometry.height() - m_virtualKeyboard.height(),
695                   screenGeometry.width(), m_virtualKeyboard.height());
696 }
697 
reset()698 void QQnxInputContext::reset()
699 {
700     qInputContextDebug();
701     endComposition();
702 }
703 
commit()704 void QQnxInputContext::commit()
705 {
706     qInputContextDebug();
707     endComposition();
708 }
709 
update(Qt::InputMethodQueries queries)710 void QQnxInputContext::update(Qt::InputMethodQueries queries)
711 {
712     qInputContextDebug() << queries;
713 
714     if (queries & Qt::ImCursorPosition) {
715         int lastCaret = m_caretPosition;
716         updateCursorPosition();
717         // If caret position has changed we need to inform IMF unless this is just due to our own action
718         // such as committing text.
719         if (hasSession() && !m_isUpdatingText && lastCaret != m_caretPosition) {
720             caret_event_t caretEvent;
721             initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED, sizeof(caretEvent));
722             caretEvent.old_pos = lastCaret;
723             caretEvent.new_pos = m_caretPosition;
724             qInputContextDebug("ictrl_dispatch_event caret changed %d %d", lastCaret, m_caretPosition);
725             p_ictrl_dispatch_event(&caretEvent.event);
726         }
727     }
728 }
729 
closeSession()730 void QQnxInputContext::closeSession()
731 {
732     qInputContextDebug();
733     if (!imfAvailable())
734         return;
735 
736     if (sInputSession) {
737         p_ictrl_close_session((input_session_t *)sInputSession);
738         sInputSession = 0;
739     }
740     // These are likely already in the right state but this depends on the text control
741     // having called reset or commit.  So, just in case, set them to proper values.
742     m_isComposing = false;
743     m_composingText.clear();
744 }
745 
openSession()746 bool QQnxInputContext::openSession()
747 {
748     if (!imfAvailable())
749         return false;
750 
751     closeSession();
752     sInputSession = p_ictrl_open_session(&ic_funcs);
753 
754     qInputContextDebug();
755 
756     return sInputSession != 0;
757 }
758 
hasSession()759 bool QQnxInputContext::hasSession()
760 {
761     return sInputSession != 0;
762 }
763 
hasSelectedText()764 bool QQnxInputContext::hasSelectedText()
765 {
766     QObject *input = qGuiApp->focusObject();
767     if (!input)
768         return false;
769 
770     QInputMethodQueryEvent query(Qt::ImCurrentSelection);
771     QCoreApplication::sendEvent(input, &query);
772 
773     return !query.value(Qt::ImCurrentSelection).toString().isEmpty();
774 }
775 
dispatchRequestSoftwareInputPanel()776 bool QQnxInputContext::dispatchRequestSoftwareInputPanel()
777 {
778     qInputContextDebug() << "requesting keyboard" << m_inputPanelVisible;
779     m_virtualKeyboard.showKeyboard();
780 
781     return true;
782 }
783 
dispatchCloseSoftwareInputPanel()784 bool QQnxInputContext::dispatchCloseSoftwareInputPanel()
785 {
786     qInputContextDebug() << "hiding keyboard" << m_inputPanelVisible;
787     m_virtualKeyboard.hideKeyboard();
788 
789     return true;
790 }
791 
792 /**
793  * IMF Event Dispatchers.
794  */
dispatchFocusGainEvent(int inputHints)795 bool QQnxInputContext::dispatchFocusGainEvent(int inputHints)
796 {
797     if (hasSession())
798         dispatchFocusLossEvent();
799 
800     QObject *input = qGuiApp->focusObject();
801 
802     if (!input || !openSession())
803         return false;
804 
805     // Set the last caret position to 0 since we don't really have one and we don't
806     // want to have the old one.
807     m_caretPosition = 0;
808 
809     QInputMethodQueryEvent query(Qt::ImHints);
810     QCoreApplication::sendEvent(input, &query);
811 
812     focus_event_t focusEvent;
813     initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_GAINED, sizeof(focusEvent));
814     focusEvent.style = DEFAULT_STYLE;
815 
816     if (inputHints & Qt::ImhNoPredictiveText)
817         focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION;
818     if (inputHints & Qt::ImhNoAutoUppercase)
819         focusEvent.style |= NO_AUTO_TEXT;
820 
821     // Following styles are mutually exclusive
822     if (inputHints & Qt::ImhHiddenText) {
823         focusEvent.style |= IMF_PASSWORD_TYPE;
824     } else if (inputHints & Qt::ImhDialableCharactersOnly) {
825         focusEvent.style |= IMF_PHONE_TYPE;
826     } else if (inputHints & Qt::ImhUrlCharactersOnly) {
827         focusEvent.style |= IMF_URL_TYPE;
828     } else if (inputHints & Qt::ImhEmailCharactersOnly) {
829         focusEvent.style |= IMF_EMAIL_TYPE;
830     }
831 
832     qInputContextDebug() << "ictrl_dispatch_event focus gain style:" << focusEvent.style;
833 
834     p_ictrl_dispatch_event((event_t *)&focusEvent);
835 
836     return true;
837 }
838 
dispatchFocusLossEvent()839 void QQnxInputContext::dispatchFocusLossEvent()
840 {
841     if (hasSession()) {
842         qInputContextDebug("ictrl_dispatch_event focus lost");
843 
844         focus_event_t focusEvent;
845         initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, FOCUS_LOST, sizeof(focusEvent));
846         p_ictrl_dispatch_event((event_t *)&focusEvent);
847         closeSession();
848     }
849 }
850 
handleKeyboardEvent(int flags,int sym,int mod,int scan,int cap,int sequenceId)851 bool QQnxInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap, int sequenceId)
852 {
853     Q_UNUSED(scan);
854 
855     if (!hasSession())
856         return false;
857 
858     int key = (flags & KEY_SYM_VALID) ? sym : cap;
859     bool navigationKey = false;
860     switch (key) {
861     case KEYCODE_RETURN:
862          /* In a single line edit we should end composition because enter might be used by something.
863             endComposition();
864             return false;*/
865         break;
866 
867     case KEYCODE_BACKSPACE:
868     case KEYCODE_DELETE:
869         // If there is a selection range, then we want a delete key to operate on that (by
870         // deleting the contents of the select range) rather than operating on the composition
871         // range.
872         if (hasSelectedText())
873             return false;
874         break;
875     case  KEYCODE_LEFT:
876         key = NAVIGATE_LEFT;
877         navigationKey = true;
878         break;
879     case  KEYCODE_RIGHT:
880         key = NAVIGATE_RIGHT;
881         navigationKey = true;
882         break;
883     case  KEYCODE_UP:
884         key = NAVIGATE_UP;
885         navigationKey = true;
886         break;
887     case  KEYCODE_DOWN:
888         key = NAVIGATE_DOWN;
889         navigationKey = true;
890         break;
891     case  KEYCODE_LEFT_CTRL:
892     case  KEYCODE_RIGHT_CTRL:
893     case  KEYCODE_MENU:
894     case  KEYCODE_LEFT_HYPER:
895     case  KEYCODE_RIGHT_HYPER:
896     case  KEYCODE_INSERT:
897     case  KEYCODE_HOME:
898     case  KEYCODE_PG_UP:
899     case  KEYCODE_END:
900     case  KEYCODE_PG_DOWN:
901         // Don't send these
902         key = 0;
903         break;
904     }
905 
906     // Pass the keys we don't know about on through
907     if ( key == 0 )
908         return false;
909 
910     if (navigationKey) {
911         // Even if we're forwarding up events, we can't do this for
912         // navigation keys.
913         if ( flags & KEY_DOWN ) {
914             navigation_event_t navEvent;
915             initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key, sizeof(navEvent));
916             navEvent.magnitude = 1;
917             qInputContextDebug("ictrl_dispatch_even navigation %d", key);
918             p_ictrl_dispatch_event(&navEvent.event);
919         }
920     } else {
921         key_event_t keyEvent;
922         initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP,
923                   sizeof(keyEvent));
924         keyEvent.key_code = cap;
925         keyEvent.character = sym;
926         keyEvent.meta_key_state = mod;
927         keyEvent.sequence_id = sequenceId;
928 
929         p_ictrl_dispatch_event(&keyEvent.event);
930         qInputContextDebug("ictrl_dispatch_even key %d", key);
931     }
932 
933     return true;
934 }
935 
updateCursorPosition()936 void QQnxInputContext::updateCursorPosition()
937 {
938     QObject *input = qGuiApp->focusObject();
939     if (!input)
940         return;
941 
942     QInputMethodQueryEvent query(Qt::ImCursorPosition);
943     QCoreApplication::sendEvent(input, &query);
944     m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
945 
946     qInputContextDebug("%d", m_caretPosition);
947 }
948 
endComposition()949 void QQnxInputContext::endComposition()
950 {
951     if (!m_isComposing)
952         return;
953 
954     finishComposingText();
955 
956     if (hasSession()) {
957         action_event_t actionEvent;
958         initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION, sizeof(actionEvent));
959         qInputContextDebug("ictrl_dispatch_even end composition");
960         p_ictrl_dispatch_event(&actionEvent.event);
961     }
962 }
963 
updateComposition(spannable_string_t * text,int32_t new_cursor_position)964 void QQnxInputContext::updateComposition(spannable_string_t *text, int32_t new_cursor_position)
965 {
966    QObject *input = qGuiApp->focusObject();
967    if (!input)
968        return;
969 
970    if (new_cursor_position > 0)
971        new_cursor_position += text->length - 1;
972 
973     m_composingText = QString::fromWCharArray(text->str, text->length);
974     m_isComposing = true;
975 
976     qInputContextDebug() << m_composingText << new_cursor_position;
977 
978     QList<QInputMethodEvent::Attribute> attributes;
979     attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,
980                                                    new_cursor_position,
981                                                    1,
982                                                    QVariant()));
983 
984     for (unsigned int i = 0; i < text->spans_count; ++i) {
985         QColor highlightColor;
986         bool underline = false;
987 
988         if ((text->spans[i].attributes_mask & COMPOSED_TEXT_ATTRIB) != 0)
989             underline = true;
990 
991         if ((text->spans[i].attributes_mask & ACTIVE_REGION_ATTRIB) != 0) {
992             underline = true;
993             highlightColor = m_highlightColor[ActiveRegion];
994         } else if ((text->spans[i].attributes_mask & AUTO_CORRECTION_ATTRIB) != 0) {
995             highlightColor = m_highlightColor[AutoCorrected];
996         } else if ((text->spans[i].attributes_mask & REVERT_CORRECTION_ATTRIB) != 0) {
997             highlightColor = m_highlightColor[Reverted];
998         }
999 
1000         if (underline || highlightColor.isValid()) {
1001             QTextCharFormat format;
1002             if (underline)
1003                 format.setFontUnderline(true);
1004             if (highlightColor.isValid())
1005                 format.setBackground(QBrush(highlightColor));
1006             qInputContextDebug() << "    attrib:  " << underline << highlightColor << text->spans[i].start << text->spans[i].end;
1007             attributes.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, text->spans[i].start,
1008                                                            text->spans[i].end - text->spans[i].start + 1, QVariant(format)));
1009 
1010         }
1011     }
1012     QInputMethodEvent event(m_composingText, attributes);
1013     m_isUpdatingText = true;
1014     QCoreApplication::sendEvent(input, &event);
1015     m_isUpdatingText = false;
1016 
1017     updateCursorPosition();
1018 }
1019 
finishComposingText()1020 void QQnxInputContext::finishComposingText()
1021 {
1022     QObject *input = qGuiApp->focusObject();
1023 
1024     if (input) {
1025         qInputContextDebug() << m_composingText;
1026 
1027         QInputMethodEvent event;
1028         event.setCommitString(m_composingText);
1029         m_isUpdatingText = true;
1030         QCoreApplication::sendEvent(input, &event);
1031         m_isUpdatingText = false;
1032     }
1033     m_composingText = QString();
1034     m_isComposing = false;
1035 
1036     updateCursorPosition();
1037 }
1038 
1039 // Return the index relative to a UTF-16 sequence of characters for a index that is relative to the
1040 // corresponding UTF-32 character string given a starting index in the UTF-16 string and a count
1041 // of the number of lead surrogates prior to that index.  Updates the highSurrogateCount to reflect the
1042 // new surrogate characters encountered.
adjustIndex(const QChar * text,int utf32Index,int utf16StartIndex,int * highSurrogateCount)1043 static int adjustIndex(const QChar *text, int utf32Index, int utf16StartIndex, int *highSurrogateCount)
1044 {
1045     int utf16Index = utf32Index + *highSurrogateCount;
1046     while (utf16StartIndex <  utf16Index) {
1047         if (text[utf16StartIndex].isHighSurrogate()) {
1048             ++utf16Index;
1049             ++*highSurrogateCount;
1050         }
1051         ++utf16StartIndex;
1052     }
1053     return utf16StartIndex;
1054 }
1055 
handleSpellCheck(spell_check_event_t * event)1056 int QQnxInputContext::handleSpellCheck(spell_check_event_t *event)
1057 {
1058     // These should never happen.
1059     if (sSpellCheckQueue->isEmpty() || event->event.event_id != NOTIFY_SP_CHECK_MISSPELLINGS)
1060         return -1;
1061 
1062     SpellCheckInfo callerInfo = sSpellCheckQueue->dequeue();
1063     spannable_string_t* spellCheckData = *event->data;
1064     QString text = QString::fromWCharArray(spellCheckData->str, spellCheckData->length);
1065     // Generate the list of indices indicating misspelled words in the text.  We use end + 1
1066     // since it's more conventional to have the end index point just past the string.  We also
1067     // can't use the indices directly since they are relative to UTF-32 encoded data and the
1068     // conversion to Qt's UTF-16 internal format might cause lengthening.
1069     QList<int> indices;
1070     int adjustment = 0;
1071     int index = 0;
1072     for (unsigned int i = 0; i < spellCheckData->spans_count; ++i) {
1073         if (spellCheckData->spans[i].attributes_mask & MISSPELLED_WORD_ATTRIB) {
1074             index = adjustIndex(text.data(), spellCheckData->spans[i].start, index, &adjustment);
1075             indices.push_back(index);
1076             index = adjustIndex(text.data(), spellCheckData->spans[i].end + 1, index, &adjustment);
1077             indices.push_back(index);
1078         }
1079     }
1080     callerInfo.spellCheckDone(callerInfo.context, text, indices);
1081 
1082     return 0;
1083 }
1084 
processEvent(event_t * event)1085 int32_t QQnxInputContext::processEvent(event_t *event)
1086 {
1087     int32_t result = -1;
1088     switch (event->event_type) {
1089     case EVENT_SPELL_CHECK: {
1090         qInputContextDebug("EVENT_SPELL_CHECK");
1091         result = handleSpellCheck(reinterpret_cast<spell_check_event_t *>(event));
1092         break;
1093     }
1094 
1095     case EVENT_NAVIGATION: {
1096         qInputContextDebug("EVENT_NAVIGATION");
1097 
1098         int key = event->event_id == NAVIGATE_UP ? KEYCODE_UP :
1099             event->event_id == NAVIGATE_DOWN ? KEYCODE_DOWN :
1100             event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT :
1101             event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0;
1102 
1103         QQnxScreenEventHandler::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
1104         QQnxScreenEventHandler::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
1105         result = 0;
1106         break;
1107     }
1108 
1109     case EVENT_KEY: {
1110         key_event_t *kevent = reinterpret_cast<key_event_t *>(event);
1111         int keySym = kevent->character != 0 ? kevent->character : kevent->key_code;
1112         int keyCap = kevent->key_code;
1113         int modifiers = 0;
1114         if (kevent->meta_key_state & META_SHIFT_ON)
1115             modifiers |= KEYMOD_SHIFT;
1116         int flags = KEY_SYM_VALID | KEY_CAP_VALID;
1117         if (event->event_id == IMF_KEY_DOWN)
1118             flags |= KEY_DOWN;
1119         qInputContextDebug("EVENT_KEY %d %d", flags, keySym);
1120         QQnxScreenEventHandler::injectKeyboardEvent(flags, keySym, modifiers, 0, keyCap);
1121         result = 0;
1122         break;
1123     }
1124 
1125     case EVENT_ACTION:
1126             // Don't care, indicates that IMF is done.
1127         break;
1128 
1129     case EVENT_CARET:
1130     case EVENT_NOTHING:
1131     case EVENT_FOCUS:
1132     case EVENT_USER_ACTION:
1133     case EVENT_STROKE:
1134     case EVENT_INVOKE_LATER:
1135         qCritical() << "Unsupported event type: " << event->event_type;
1136         break;
1137     default:
1138         qCritical() << "Unknown event type: " << event->event_type;
1139     }
1140     return result;
1141 }
1142 
1143 /**
1144  * IMF Event Handlers
1145  */
1146 
onCommitText(spannable_string_t * text,int32_t new_cursor_position)1147 int32_t QQnxInputContext::onCommitText(spannable_string_t *text, int32_t new_cursor_position)
1148 {
1149     Q_UNUSED(new_cursor_position);
1150 
1151     updateComposition(text, new_cursor_position);
1152     finishComposingText();
1153 
1154     return 0;
1155 }
1156 
onDeleteSurroundingText(int32_t left_length,int32_t right_length)1157 int32_t QQnxInputContext::onDeleteSurroundingText(int32_t left_length, int32_t right_length)
1158 {
1159     qInputContextDebug("L: %d R: %d", int(left_length), int(right_length));
1160 
1161     QObject *input = qGuiApp->focusObject();
1162     if (!input)
1163         return 0;
1164 
1165     int replacementLength = left_length + right_length;
1166     int replacementStart = -left_length;
1167 
1168     finishComposingText();
1169 
1170     QInputMethodEvent event;
1171     event.setCommitString(QString(), replacementStart, replacementLength);
1172     m_isUpdatingText = true;
1173     QCoreApplication::sendEvent(input, &event);
1174     m_isUpdatingText = false;
1175 
1176     updateCursorPosition();
1177 
1178     return 0;
1179 }
1180 
onFinishComposingText()1181 int32_t QQnxInputContext::onFinishComposingText()
1182 {
1183     finishComposingText();
1184 
1185     return 0;
1186 }
1187 
onGetCursorPosition()1188 int32_t QQnxInputContext::onGetCursorPosition()
1189 {
1190     qInputContextDebug();
1191 
1192     QObject *input = qGuiApp->focusObject();
1193     if (!input)
1194         return 0;
1195 
1196     updateCursorPosition();
1197 
1198     return m_caretPosition;
1199 }
1200 
onGetTextAfterCursor(int32_t n,int32_t flags)1201 spannable_string_t *QQnxInputContext::onGetTextAfterCursor(int32_t n, int32_t flags)
1202 {
1203     Q_UNUSED(flags);
1204     qInputContextDebug();
1205 
1206     QObject *input = qGuiApp->focusObject();
1207     if (!input)
1208         return toSpannableString("");
1209 
1210     QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
1211     QCoreApplication::sendEvent(input, &query);
1212     QString text = query.value(Qt::ImSurroundingText).toString();
1213     m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
1214 
1215     return toSpannableString(text.mid(m_caretPosition, n));
1216 }
1217 
onGetTextBeforeCursor(int32_t n,int32_t flags)1218 spannable_string_t *QQnxInputContext::onGetTextBeforeCursor(int32_t n, int32_t flags)
1219 {
1220     Q_UNUSED(flags);
1221     qInputContextDebug();
1222 
1223     QObject *input = qGuiApp->focusObject();
1224     if (!input)
1225         return toSpannableString("");
1226 
1227     QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
1228     QCoreApplication::sendEvent(input, &query);
1229     QString text = query.value(Qt::ImSurroundingText).toString();
1230     m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
1231 
1232     if (n < m_caretPosition)
1233         return toSpannableString(text.mid(m_caretPosition - n, n));
1234     else
1235         return toSpannableString(text.mid(0, m_caretPosition));
1236 }
1237 
onSendEvent(event_t * event)1238 int32_t QQnxInputContext::onSendEvent(event_t *event)
1239 {
1240     qInputContextDebug();
1241 
1242     return processEvent(event);
1243 }
1244 
onSetComposingRegion(int32_t start,int32_t end)1245 int32_t QQnxInputContext::onSetComposingRegion(int32_t start, int32_t end)
1246 {
1247     QObject *input = qGuiApp->focusObject();
1248     if (!input)
1249         return 0;
1250 
1251     QInputMethodQueryEvent query(Qt::ImCursorPosition | Qt::ImSurroundingText);
1252     QCoreApplication::sendEvent(input, &query);
1253     QString text = query.value(Qt::ImSurroundingText).toString();
1254     m_caretPosition = query.value(Qt::ImCursorPosition).toInt();
1255 
1256     qInputContextDebug() << text;
1257 
1258     m_isUpdatingText = true;
1259 
1260     // Delete the current text.
1261     QInputMethodEvent deleteEvent;
1262     deleteEvent.setCommitString(QString(), start - m_caretPosition, end - start);
1263     QCoreApplication::sendEvent(input, &deleteEvent);
1264 
1265     m_composingText = text.mid(start, end - start);
1266     m_isComposing = true;
1267 
1268     QList<QInputMethodEvent::Attribute> attributes;
1269     QTextCharFormat format;
1270     format.setFontUnderline(true);
1271     attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, m_composingText.length(), format));
1272 
1273     QInputMethodEvent setTextEvent(m_composingText, attributes);
1274     QCoreApplication::sendEvent(input, &setTextEvent);
1275 
1276     m_isUpdatingText = false;
1277 
1278     return 0;
1279 }
1280 
onSetComposingText(spannable_string_t * text,int32_t new_cursor_position)1281 int32_t QQnxInputContext::onSetComposingText(spannable_string_t *text, int32_t new_cursor_position)
1282 {
1283     if (text->length > 0) {
1284         updateComposition(text, new_cursor_position);
1285     } else {
1286         // If the composing text is empty we can simply end composition, the visual effect is the same.
1287         // However, sometimes one wants to display hint text in an empty text field and for this to work
1288         // QQuickTextEdit.inputMethodComposing has to be false if the composition string is empty.
1289         m_composingText.clear();
1290         finishComposingText();
1291     }
1292     return 0;
1293 }
1294 
onIsTextSelected(int32_t * pIsSelected)1295 int32_t QQnxInputContext::onIsTextSelected(int32_t* pIsSelected)
1296 {
1297     *pIsSelected = hasSelectedText();
1298 
1299     qInputContextDebug() << *pIsSelected;
1300 
1301     return 0;
1302 }
1303 
onIsAllTextSelected(int32_t * pIsSelected)1304 int32_t QQnxInputContext::onIsAllTextSelected(int32_t* pIsSelected)
1305 {
1306     QObject *input = qGuiApp->focusObject();
1307     if (!input)
1308         return -1;
1309 
1310     QInputMethodQueryEvent query(Qt::ImCurrentSelection | Qt::ImSurroundingText);
1311     QCoreApplication::sendEvent(input, &query);
1312 
1313     *pIsSelected = query.value(Qt::ImSurroundingText).toString().length() == query.value(Qt::ImCurrentSelection).toString().length();
1314 
1315     qInputContextDebug() << *pIsSelected;
1316 
1317     return 0;
1318 }
1319 
showInputPanel()1320 void QQnxInputContext::showInputPanel()
1321 {
1322     qInputContextDebug();
1323     dispatchRequestSoftwareInputPanel();
1324 }
1325 
hideInputPanel()1326 void QQnxInputContext::hideInputPanel()
1327 {
1328     qInputContextDebug();
1329     dispatchCloseSoftwareInputPanel();
1330 }
1331 
isInputPanelVisible() const1332 bool QQnxInputContext::isInputPanelVisible() const
1333 {
1334     return m_inputPanelVisible;
1335 }
1336 
locale() const1337 QLocale QQnxInputContext::locale() const
1338 {
1339     return m_inputPanelLocale;
1340 }
1341 
keyboardVisibilityChanged(bool visible)1342 void QQnxInputContext::keyboardVisibilityChanged(bool visible)
1343 {
1344     qInputContextDebug() << "visible=" << visible;
1345     if (m_inputPanelVisible != visible) {
1346         m_inputPanelVisible = visible;
1347         emitInputPanelVisibleChanged();
1348     }
1349 }
1350 
keyboardLocaleChanged(const QLocale & locale)1351 void QQnxInputContext::keyboardLocaleChanged(const QLocale &locale)
1352 {
1353     qInputContextDebug() << "locale=" << locale;
1354     if (m_inputPanelLocale != locale) {
1355         m_inputPanelLocale = locale;
1356         emitLocaleChanged();
1357     }
1358 }
1359 
setHighlightColor(int index,const QColor & color)1360 void QQnxInputContext::setHighlightColor(int index, const QColor &color)
1361 {
1362     qInputContextDebug() << "setHighlightColor" << index << color << qGuiApp->focusObject();
1363 
1364     if (!sInputContextInstance)
1365         return;
1366 
1367     // If the focus has changed, revert all colors to the default.
1368     if (sInputContextInstance->m_focusObject != qGuiApp->focusObject()) {
1369         QColor invalidColor;
1370         sInputContextInstance->m_highlightColor[ActiveRegion] = sSelectedColor;
1371         sInputContextInstance->m_highlightColor[AutoCorrected] = invalidColor;
1372         sInputContextInstance->m_highlightColor[Reverted] = invalidColor;
1373         sInputContextInstance->m_focusObject = qGuiApp->focusObject();
1374     }
1375     if (index >= 0 && index <= Reverted)
1376         sInputContextInstance->m_highlightColor[index] = color;
1377 }
1378 
setFocusObject(QObject * object)1379 void QQnxInputContext::setFocusObject(QObject *object)
1380 {
1381     qInputContextDebug() << "input item=" << object;
1382 
1383     // Ensure the colors are reset if we've a change in focus object
1384     setHighlightColor(-1, QColor());
1385 
1386     if (!inputMethodAccepted()) {
1387         if (m_inputPanelVisible)
1388             hideInputPanel();
1389         if (hasSession())
1390             dispatchFocusLossEvent();
1391     } else {
1392         QInputMethodQueryEvent query(Qt::ImHints | Qt::ImEnterKeyType);
1393         QCoreApplication::sendEvent(object, &query);
1394         int inputHints = query.value(Qt::ImHints).toInt();
1395         Qt::EnterKeyType qtEnterKeyType = Qt::EnterKeyType(query.value(Qt::ImEnterKeyType).toInt());
1396 
1397         dispatchFocusGainEvent(inputHints);
1398 
1399         m_virtualKeyboard.setInputHints(inputHints);
1400         m_virtualKeyboard.setEnterKeyType(
1401             QQnxAbstractVirtualKeyboard::qtEnterKeyTypeToQnx(qtEnterKeyType)
1402         );
1403 
1404         if (!m_inputPanelVisible)
1405             showInputPanel();
1406     }
1407 }
1408 
checkSpelling(const QString & text,void * context,void (* spellCheckDone)(void * context,const QString & text,const QList<int> & indices))1409 bool QQnxInputContext::checkSpelling(const QString &text, void *context, void (*spellCheckDone)(void *context, const QString &text, const QList<int> &indices))
1410 {
1411     qInputContextDebug() << "text" << text;
1412 
1413     if (!imfAvailable())
1414         return false;
1415 
1416     if (!sSpellCheckSession)
1417         sSpellCheckSession = p_ictrl_open_session(&ic_funcs);
1418 
1419     action_event_t spellEvent;
1420     initEvent(&spellEvent.event, sSpellCheckSession, EVENT_ACTION, ACTION_CHECK_MISSPELLINGS, sizeof(spellEvent));
1421     int len = text.length();
1422     spellEvent.event_data = alloca(sizeof(wchar_t) * (len + 1));
1423     spellEvent.length_data = text.toWCharArray(static_cast<wchar_t*>(spellEvent.event_data)) * sizeof(wchar_t);
1424 
1425     int rc = p_ictrl_dispatch_event(reinterpret_cast<event_t*>(&spellEvent));
1426 
1427     if (rc == 0) {
1428         sSpellCheckQueue->enqueue(SpellCheckInfo(context, spellCheckDone));
1429         return true;
1430     }
1431     return false;
1432 }
1433 
1434 QT_END_NAMESPACE
1435