1 /****************************************************************************
2 **
3 ** Copyright (C) 2011 - 2012 Research In Motion <blackberry-qt@qnx.com>
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module 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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 /* TODO
42  *   Support inputMethodHints to restrict input (needs additional features in IMF).
43 */
44 
45 // #define QBBINPUTCONTEXT_DEBUG
46 // #define QBBINPUTCONTEXT_IMF_EVENT_DEBUG
47 
48 #define STRX(x) #x
49 #define STR(x) STRX(x)
50 #define TAG __FILE__ "(" STR(__LINE__) ")" << __func__ << ":"
51 
52 #include <qbbeventthread.h>
53 #include <qbbinputcontext.h>
54 #include <qbbabstractvirtualkeyboard.h>
55 
56 #include <QAction>
57 #include <QCoreApplication>
58 #include <QDebug>
59 #include <QInputMethodEvent>
60 #include <QMutex>
61 #include <QTextCharFormat>
62 #include <QVariant>
63 #include <QVariantHash>
64 #include <QWaitCondition>
65 
66 #include <dlfcn.h>
67 #include "imf/imf_client.h"
68 #include "imf/input_control.h"
69 #include <process.h>
70 #include <sys/keycodes.h>
71 
72 // Someone tell me why input_control methods are in this namespace, but the rest is not.
73 using namespace InputMethodSystem;
74 
75 #define qs(x) QString::fromLatin1(x)
76 #define iarg(name) event->mArgs[qs(#name)] = QVariant::fromValue(name)
77 #define parg(name) event->mArgs[qs(#name)] = QVariant::fromValue((void*)name)
78 namespace
79 {
80 
81 spannable_string_t* toSpannableString(QString const& text);
82 static const input_session_t *sInputSession = 0;
isSessionOkay(input_session_t * ic)83 bool isSessionOkay(input_session_t *ic)
84 {
85     return ic !=0 && sInputSession != 0 && ic->component_id == sInputSession->component_id;
86 }
87 
88 enum ImfEventType
89 {
90     ImfBeginBatchEdit,
91     ImfClearMetaKeyStates,
92     ImfCommitText,
93     ImfDeleteSurroundingText,
94     ImfEndBatchEdit,
95     ImfFinishComposingText,
96     ImfGetCursorCapsMode,
97     ImfGetCursorPosition,
98     ImfGetExtractedText,
99     ImfGetSelectedText,
100     ImfGetTextAfterCursor,
101     ImfGetTextBeforeCursor,
102     ImfPerformEditorAction,
103     ImfReportFullscreenMode,
104     ImfSendEvent,
105     ImfSendAsyncEvent,
106     ImfSetComposingRegion,
107     ImfSetComposingText,
108     ImfSetSelection
109 };
110 
111 // We use this class as a round about way to support a posting synchronous event into
112 // Qt's main thread from the IMF thread.
113 class ImfEventResult
114 {
115 public:
ImfEventResult()116     ImfEventResult()
117     {
118         mMutex.lock();
119     }
120 
~ImfEventResult()121     ~ImfEventResult()
122     {
123         mMutex.unlock();
124     }
125 
wait()126     void wait()
127     {
128         mWait.wait(&mMutex);
129     }
130 
signal()131     void signal()
132     {
133         mWait.wakeAll();
134     }
135 
setResult(QVariant const & result)136     void setResult(QVariant const& result)
137     {
138         mMutex.lock();
139         mRetVal = result;
140         signal();
141         mMutex.unlock();
142     }
143 
getResult()144     QVariant& getResult()
145     {
146         return mRetVal;
147     }
148 
149 private:
150     QVariant mRetVal;
151     QMutex mMutex;
152     QWaitCondition mWait;
153 };
154 
155 class ImfEvent : public QEvent
156 {
157     public:
ImfEvent(input_session_t * session,ImfEventType type,ImfEventResult * result)158         ImfEvent(input_session_t* session, ImfEventType type, ImfEventResult* result) :
159             QEvent((QEvent::Type)sUserEventType),
160             mSession(session),
161             mImfType(type),
162             mResult(result)
163         {
164         }
~ImfEvent()165         ~ImfEvent() { }
166 
167     input_session_t* mSession;
168     ImfEventType mImfType;
169     QVariantHash mArgs;
170     ImfEventResult *mResult;
171 
172     static int sUserEventType;
173 };
174 int ImfEvent::sUserEventType = QEvent::registerEventType();
175 
imfBeginBatchEdit(input_session_t * ic)176 static int32_t imfBeginBatchEdit(input_session_t* ic)
177 {
178 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
179     qDebug() << TAG;
180 #endif
181 
182     if (!isSessionOkay(ic))
183         return 0;
184 
185     ImfEventResult result;
186     ImfEvent* event = new ImfEvent(ic, ImfBeginBatchEdit, &result);
187     QCoreApplication::postEvent(QCoreApplication::instance(), event);
188 
189     result.wait();
190     int32_t ret = result.getResult().toInt();
191 
192     return ret;
193 }
194 
imfClearMetaKeyStates(input_session_t * ic,int32_t states)195 static int32_t imfClearMetaKeyStates(input_session_t* ic, int32_t states)
196 {
197 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
198     qDebug() << TAG;
199 #endif
200 
201     if (!isSessionOkay(ic))
202         return 0;
203 
204     ImfEventResult result;
205     ImfEvent* event = new ImfEvent(ic, ImfClearMetaKeyStates, &result);
206     iarg(states);
207 
208     QCoreApplication::postEvent(QCoreApplication::instance(), event);
209 
210     result.wait();
211     int32_t ret = result.getResult().toInt();
212 
213     return ret;
214 }
215 
imfCommitText(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)216 static int32_t imfCommitText(input_session_t* ic, spannable_string_t* text, int32_t new_cursor_position)
217 {
218 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
219     qDebug() << TAG;
220 #endif
221 
222     if (!isSessionOkay(ic))
223         return 0;
224 
225     ImfEventResult result;
226     ImfEvent* event = new ImfEvent(ic, ImfCommitText, &result);
227     parg(text);
228     iarg(new_cursor_position);
229 
230     QCoreApplication::postEvent(QCoreApplication::instance(), event);
231 
232     result.wait();
233     int32_t ret = result.getResult().toInt();
234 
235     return ret;
236 }
237 
imfDeleteSurroundingText(input_session_t * ic,int32_t left_length,int32_t right_length)238 static int32_t imfDeleteSurroundingText(input_session_t* ic, int32_t left_length, int32_t right_length)
239 {
240 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
241     qDebug() << TAG;
242 #endif
243 
244     if (!isSessionOkay(ic))
245         return 0;
246 
247     ImfEventResult result;
248     ImfEvent* event = new ImfEvent(ic, ImfDeleteSurroundingText, &result);
249     iarg(left_length);
250     iarg(right_length);
251 
252     QCoreApplication::postEvent(QCoreApplication::instance(), event);
253 
254     result.wait();
255     int32_t ret = result.getResult().toInt();
256 
257     return ret;
258 }
259 
imfEndBatchEdit(input_session_t * ic)260 static int32_t imfEndBatchEdit(input_session_t* ic)
261 {
262 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
263     qDebug() << TAG;
264 #endif
265 
266     if (!isSessionOkay(ic))
267         return 0;
268 
269     ImfEventResult result;
270     ImfEvent* event = new ImfEvent(ic, ImfEndBatchEdit, &result);
271 
272     QCoreApplication::postEvent(QCoreApplication::instance(), event);
273 
274     result.wait();
275     int32_t ret = result.getResult().toInt();
276 
277     return ret;
278 }
279 
imfFinishComposingText(input_session_t * ic)280 static int32_t imfFinishComposingText(input_session_t* ic)
281 {
282 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
283     qDebug() << TAG;
284 #endif
285 
286     if (!isSessionOkay(ic))
287         return 0;
288 
289     ImfEventResult result;
290     ImfEvent* event = new ImfEvent(ic, ImfFinishComposingText, &result);
291 
292     QCoreApplication::postEvent(QCoreApplication::instance(), event);
293 
294     result.wait();
295     int32_t ret = result.getResult().toInt();
296 
297     return ret;
298 }
299 
imfGetCursorCapsMode(input_session_t * ic,int32_t req_modes)300 static int32_t imfGetCursorCapsMode(input_session_t* ic, int32_t req_modes)
301 {
302 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
303     qDebug() << TAG;
304 #endif
305 
306     if (!isSessionOkay(ic))
307         return 0;
308 
309     ImfEventResult result;
310     ImfEvent* event = new ImfEvent(ic, ImfGetCursorCapsMode, &result);
311     iarg(req_modes);
312 
313     QCoreApplication::postEvent(QCoreApplication::instance(), event);
314 
315     int32_t ret = result.getResult().value<int>();
316     return ret;
317 }
318 
imfGetCursorPosition(input_session_t * ic)319 static int32_t imfGetCursorPosition(input_session_t* ic)
320 {
321 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
322     qDebug() << TAG;
323 #endif
324 
325     if (!isSessionOkay(ic))
326         return 0;
327 
328     ImfEventResult result;
329     ImfEvent* event = new ImfEvent(ic, ImfGetCursorPosition, &result);
330 
331     QCoreApplication::postEvent(QCoreApplication::instance(), event);
332 
333     result.wait();
334     int32_t ret = result.getResult().toInt();
335 
336     return ret;
337 }
338 
imfGetExtractedText(input_session_t * ic,extracted_text_request_t * request,int32_t flags)339 static extracted_text_t* imfGetExtractedText(input_session_t* ic, extracted_text_request_t* request, int32_t flags)
340 {
341 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
342     qDebug() << TAG;
343 #endif
344 
345     if (!isSessionOkay(ic)) {
346         extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
347         et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1);
348         return et;
349     }
350 
351     ImfEventResult result;
352     ImfEvent* event = new ImfEvent(ic, ImfGetExtractedText, &result);
353     parg(request);
354     iarg(flags);
355 
356     QCoreApplication::postEvent(QCoreApplication::instance(), event);
357 
358     result.wait();
359     void* ret = result.getResult().value<void*>();
360     return (extracted_text_t*)ret;
361 }
362 
imfGetSelectedText(input_session_t * ic,int32_t flags)363 static spannable_string_t* imfGetSelectedText(input_session_t* ic, int32_t flags)
364 {
365 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
366     qDebug() << TAG;
367 #endif
368 
369     if (!isSessionOkay(ic))
370         return toSpannableString("");
371 
372     ImfEventResult result;
373     ImfEvent* event = new ImfEvent(ic, ImfGetSelectedText, &result);
374     iarg(flags);
375 
376     QCoreApplication::postEvent(QCoreApplication::instance(), event);
377 
378     result.wait();
379     void* ret = result.getResult().value<void*>();
380     return (spannable_string_t*)ret;
381 }
382 
imfGetTextAfterCursor(input_session_t * ic,int32_t n,int32_t flags)383 static spannable_string_t* imfGetTextAfterCursor(input_session_t* ic, int32_t n, int32_t flags)
384 {
385 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
386     qDebug() << TAG;
387 #endif
388 
389     if (!isSessionOkay(ic))
390         return toSpannableString("");
391 
392     ImfEventResult result;
393     ImfEvent* event = new ImfEvent(ic, ImfGetTextAfterCursor, &result);
394     iarg(n);
395     iarg(flags);
396 
397     QCoreApplication::postEvent(QCoreApplication::instance(), event);
398 
399     result.wait();
400     void* ret = result.getResult().value<void*>();
401     return (spannable_string_t*)ret;
402 }
403 
imfGetTextBeforeCursor(input_session_t * ic,int32_t n,int32_t flags)404 static spannable_string_t* imfGetTextBeforeCursor(input_session_t* ic, int32_t n, int32_t flags)
405 {
406 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
407     qDebug() << TAG;
408 #endif
409 
410     if (!isSessionOkay(ic))
411         return toSpannableString("");
412 
413     ImfEventResult result;
414     ImfEvent* event = new ImfEvent(ic, ImfGetTextBeforeCursor, &result);
415     iarg(n);
416     iarg(flags);
417 
418     QCoreApplication::postEvent(QCoreApplication::instance(), event);
419 
420     result.wait();
421     void* ret = result.getResult().value<void*>();
422     return (spannable_string_t*)ret;
423 }
424 
imfPerformEditorAction(input_session_t * ic,int32_t editor_action)425 static int32_t imfPerformEditorAction(input_session_t* ic, int32_t editor_action)
426 {
427 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
428     qDebug() << TAG;
429 #endif
430 
431     if (!isSessionOkay(ic))
432         return 0;
433 
434     ImfEventResult result;
435     ImfEvent* event = new ImfEvent(ic, ImfPerformEditorAction, &result);
436     iarg(editor_action);
437 
438     QCoreApplication::postEvent(QCoreApplication::instance(), event);
439 
440     result.wait();
441     int32_t ret = result.getResult().value<int>();
442     return ret;
443 }
444 
imfReportFullscreenMode(input_session_t * ic,int32_t enabled)445 static int32_t imfReportFullscreenMode(input_session_t* ic, int32_t enabled)
446 {
447 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
448     qDebug() << TAG;
449 #endif
450 
451     if (!isSessionOkay(ic))
452         return 0;
453 
454     ImfEventResult result;
455     ImfEvent* event = new ImfEvent(ic, ImfReportFullscreenMode, &result);
456     iarg(enabled);
457 
458     QCoreApplication::postEvent(QCoreApplication::instance(), event);
459 
460     result.wait();
461     int32_t ret = result.getResult().value<int>();
462     return ret;
463 }
464 
imfSendEvent(input_session_t * ic,event_t * event)465 static int32_t imfSendEvent(input_session_t* ic, event_t * event)
466 {
467 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
468     qDebug() << TAG;
469 #endif
470 
471     if (!isSessionOkay(ic))
472         return 0;
473 
474     ImfEvent* imfEvent = new ImfEvent(ic, ImfSendEvent, 0);
475     imfEvent->mArgs[qs("event")] = QVariant::fromValue((void*)event);
476 
477     QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
478 
479     return 0;
480 }
481 
imfSendAsyncEvent(input_session_t * ic,event_t * event)482 static int32_t imfSendAsyncEvent(input_session_t* ic, event_t * event)
483 {
484 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
485     qDebug() << TAG;
486 #endif
487 
488     if (!isSessionOkay(ic))
489         return 0;
490 
491     ImfEvent* imfEvent = new ImfEvent(ic, ImfSendAsyncEvent, 0);
492     imfEvent->mArgs[qs("event")] = QVariant::fromValue((void*)event);
493 
494     QCoreApplication::postEvent(QCoreApplication::instance(), imfEvent);
495 
496     return 0;
497 }
498 
imfSetComposingRegion(input_session_t * ic,int32_t start,int32_t end)499 static int32_t imfSetComposingRegion(input_session_t* ic, int32_t start, int32_t end)
500 {
501 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
502     qDebug() << TAG;
503 #endif
504 
505     if (!isSessionOkay(ic))
506         return 0;
507 
508     ImfEventResult result;
509     ImfEvent* event = new ImfEvent(ic, ImfSetComposingRegion, &result);
510     iarg(start);
511     iarg(end);
512 
513     QCoreApplication::postEvent(QCoreApplication::instance(), event);
514 
515     result.wait();
516     int32_t ret = result.getResult().value<int>();
517     return ret;
518 }
519 
imfSetComposingText(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)520 static int32_t imfSetComposingText(input_session_t* ic, spannable_string_t* text, int32_t new_cursor_position)
521 {
522 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
523     qDebug() << TAG;
524 #endif
525 
526     if (!isSessionOkay(ic))
527         return 0;
528 
529     ImfEventResult result;
530     ImfEvent* event = new ImfEvent(ic, ImfSetComposingText, &result);
531     parg(text);
532     iarg(new_cursor_position);
533 
534     QCoreApplication::postEvent(QCoreApplication::instance(), event);
535 
536     result.wait();
537     int32_t ret = result.getResult().value<int>();
538     return ret;
539 }
540 
imfSetSelection(input_session_t * ic,int32_t start,int32_t end)541 static int32_t imfSetSelection(input_session_t* ic, int32_t start, int32_t end)
542 {
543 #if defined(QBBINPUTCONTEXT_IMF_EVENT_DEBUG)
544     qDebug() << TAG;
545 #endif
546 
547     if (!isSessionOkay(ic))
548         return 0;
549 
550     ImfEventResult result;
551     ImfEvent* event = new ImfEvent(ic, ImfSetSelection, &result);
552     iarg(start);
553     iarg(end);
554 
555     QCoreApplication::postEvent(QCoreApplication::instance(), event);
556 
557     result.wait();
558     int32_t ret = result.getResult().value<int>();
559     return ret;
560 }
561 
562 static connection_interface_t ic_funcs = {
563     imfBeginBatchEdit,
564     imfClearMetaKeyStates,
565     imfCommitText,
566     imfDeleteSurroundingText,
567     imfEndBatchEdit,
568     imfFinishComposingText,
569     imfGetCursorCapsMode,
570     imfGetCursorPosition,
571     imfGetExtractedText,
572     imfGetSelectedText,
573     imfGetTextAfterCursor,
574     imfGetTextBeforeCursor,
575     imfPerformEditorAction,
576     imfReportFullscreenMode,
577     NULL, //ic_send_key_event
578     imfSendEvent,
579     imfSendAsyncEvent,
580     imfSetComposingRegion,
581     imfSetComposingText,
582     imfSetSelection,
583     NULL, //ic_set_candidates,
584 };
585 
586 static void
initEvent(event_t * pEvent,const input_session_t * pSession,EventType eventType,int eventId)587 initEvent(event_t *pEvent, const input_session_t *pSession, EventType eventType, int eventId)
588 {
589     static int s_transactionId;
590 
591     // Make sure structure is squeaky clean since it's not clear just what is significant.
592     memset(pEvent, 0, sizeof(event_t));
593     pEvent->event_type = eventType;
594     pEvent->event_id = eventId;
595     pEvent->pid = getpid();
596     pEvent->component_id = pSession->component_id;
597     pEvent->transaction_id = ++s_transactionId;
598 }
599 
toSpannableString(QString const & text)600 spannable_string_t* toSpannableString(QString const& text)
601 {
602 #if defined(QBBINPUTCONTEXT_DEBUG)
603     qDebug() << TAG << text;
604 #endif
605 
606     spannable_string_t *pString = (spannable_string_t *)malloc(sizeof(spannable_string_t));
607     pString->str = (wchar_t *)malloc(sizeof(wchar_t) * text.length()+1);
608     pString->length = text.length();
609     pString->spans = NULL;
610     pString->spans_count = 0;
611 
612     QChar const* pData = text.constData();
613     wchar_t* pDst = pString->str;
614 
615     while (!pData->isNull())
616     {
617         *pDst = pData->unicode();
618         pDst++;
619         pData++;
620     }
621     *pDst = 0;
622 
623     return pString;
624 }
625 
626 } // namespace
627 
628 static const input_session_t *(*p_ictrl_open_session)(connection_interface_t *) = 0;
629 static void (*p_ictrl_close_session)(input_session_t *) = 0;
630 static int32_t (*p_ictrl_dispatch_event)(event_t*) = 0;
631 static int32_t (*p_imf_client_init)() = 0;
632 static void (*p_imf_client_disconnect)() = 0;
633 static int32_t (*p_vkb_init_selection_service)() = 0;
634 static int32_t (*p_ictrl_get_num_active_sessions)() = 0;
635 static bool s_imfInitFailed = false;
636 
imfAvailable()637 static bool imfAvailable()
638 {
639     static bool s_imfDisabled = getenv("DISABLE_IMF") != NULL;
640     static bool s_imfReady = false;
641 
642     if ( s_imfInitFailed || s_imfDisabled) {
643         return false;
644     }
645     else if ( s_imfReady ) {
646         return true;
647     }
648 
649     if ( p_imf_client_init == NULL ) {
650         void *handle = dlopen("libinput_client.so.1", 0);
651         if ( handle ) {
652             p_imf_client_init = (int32_t (*)()) dlsym(handle, "imf_client_init");
653             p_imf_client_disconnect = (void (*)()) dlsym(handle, "imf_client_disconnect");
654             p_ictrl_open_session = (const input_session_t* (*)(connection_interface_t*))dlsym(handle, "ictrl_open_session");
655             p_ictrl_close_session = (void (*)(input_session_t*))dlsym(handle, "ictrl_close_session");
656             p_ictrl_dispatch_event = (int32_t (*)(event_t*))dlsym(handle, "ictrl_dispatch_event");
657             p_vkb_init_selection_service = (int32_t (*)())dlsym(handle, "vkb_init_selection_service");
658             p_ictrl_get_num_active_sessions = (int32_t (*)())dlsym(handle, "ictrl_get_num_active_sessions");
659         }
660         else
661         {
662             qCritical() << TAG << "libinput_client.so.1 is not present - IMF services are disabled.";
663             s_imfDisabled = true;
664             return false;
665         }
666         if ( p_imf_client_init && p_ictrl_open_session && p_ictrl_dispatch_event ) {
667             s_imfReady = true;
668         }
669         else {
670             p_ictrl_open_session = NULL;
671             p_ictrl_dispatch_event = NULL;
672             s_imfDisabled = true;
673             qCritical() << TAG << "libinput_client.so.1 did not contain the correct symbols, library mismatch? IMF services are disabled.";
674             return false;
675         }
676     }
677 
678     return s_imfReady;
679 }
680 
681 QT_BEGIN_NAMESPACE
682 
QBBInputContext(QBBAbstractVirtualKeyboard & keyboard,QObject * parent)683 QBBInputContext::QBBInputContext(QBBAbstractVirtualKeyboard &keyboard, QObject* parent):
684          QInputContext(parent),
685          mLastCaretPos(0),
686          mIsComposing(false),
687          mVirtualKeyboard(keyboard)
688 {
689 #if defined(QBBINPUTCONTEXT_DEBUG)
690     qDebug() << TAG;
691 #endif
692 
693     if (!imfAvailable())
694         return;
695 
696    if ( p_imf_client_init() != 0 ) {
697         s_imfInitFailed = true;
698         qCritical("imf_client_init failed - IMF services will be unavailable");
699     }
700 
701     QCoreApplication::instance()->installEventFilter(this);
702 
703     // p_vkb_init_selection_service();
704 }
705 
~QBBInputContext()706 QBBInputContext::~QBBInputContext()
707 {
708 #if defined(QBBINPUTCONTEXT_DEBUG)
709     qDebug() << TAG;
710 #endif
711 
712     if (!imfAvailable())
713         return;
714 
715     QCoreApplication::instance()->removeEventFilter(this);
716     p_imf_client_disconnect();
717 }
718 
719 #define getarg(type, name) type name = imfEvent->mArgs[qs(#name)].value<type>()
720 #define getparg(type, name) type name = (type)(imfEvent->mArgs[qs(#name)].value<void*>())
721 
eventFilter(QObject * obj,QEvent * event)722 bool QBBInputContext::eventFilter(QObject *obj, QEvent *event)
723 {
724 
725     if (event->type() == ImfEvent::sUserEventType) {
726         // Forward the event to our real handler.
727         ImfEvent* imfEvent = static_cast<ImfEvent*>(event);
728         switch (imfEvent->mImfType) {
729         case ImfBeginBatchEdit:
730         {
731             int32_t ret = onBeginBatchEdit(imfEvent->mSession);
732             imfEvent->mResult->setResult(QVariant::fromValue(ret));
733             break;
734         }
735 
736         case ImfClearMetaKeyStates:
737         {
738             getarg(int32_t, states);
739             int32_t ret = onClearMetaKeyStates(imfEvent->mSession, states);
740             imfEvent->mResult->setResult(QVariant::fromValue(ret));
741             break;
742         }
743 
744         case ImfCommitText:
745         {
746             getparg(spannable_string_t*, text);
747             getarg(int32_t, new_cursor_position);
748             int32_t ret = onCommitText(imfEvent->mSession, text, new_cursor_position);
749             imfEvent->mResult->setResult(QVariant::fromValue(ret));
750             break;
751         }
752 
753         case ImfDeleteSurroundingText:
754         {
755             getarg(int32_t, left_length);
756             getarg(int32_t, right_length);
757             int32_t ret = onDeleteSurroundingText(imfEvent->mSession, left_length, right_length);
758             imfEvent->mResult->setResult(QVariant::fromValue(ret));
759             break;
760         }
761 
762         case ImfEndBatchEdit:
763         {
764             int32_t ret = onEndBatchEdit(imfEvent->mSession);
765             imfEvent->mResult->setResult(QVariant::fromValue(ret));
766             break;
767         }
768 
769         case ImfFinishComposingText:
770         {
771             int32_t ret = onFinishComposingText(imfEvent->mSession);
772             imfEvent->mResult->setResult(QVariant::fromValue(ret));
773             break;
774         }
775 
776         case ImfGetCursorCapsMode:
777         {
778             getarg(int32_t, req_modes);
779             int32_t ret = onGetCursorCapsMode(imfEvent->mSession, req_modes);
780             imfEvent->mResult->setResult(QVariant::fromValue(ret));
781             break;
782         }
783 
784         case ImfGetCursorPosition:
785         {
786             int32_t ret = onGetCursorPosition(imfEvent->mSession);
787             imfEvent->mResult->setResult(QVariant::fromValue(ret));
788             break;
789         }
790 
791         case ImfGetExtractedText:
792         {
793             getparg(extracted_text_request_t*, request);
794             getarg(int32_t, flags);
795             extracted_text_t* ret = onGetExtractedText(imfEvent->mSession, request, flags);
796             imfEvent->mResult->setResult(QVariant::fromValue((void*)ret));
797             break;
798         }
799 
800         case ImfGetSelectedText:
801         {
802             getarg(int32_t, flags);
803             spannable_string_t* ret = onGetSelectedText(imfEvent->mSession, flags);
804             imfEvent->mResult->setResult(QVariant::fromValue((void*)ret));
805             break;
806         }
807 
808         case ImfGetTextAfterCursor:
809         {
810             getarg(int32_t, n);
811             getarg(int32_t, flags);
812             spannable_string_t* ret = onGetTextAfterCursor(imfEvent->mSession, n, flags);
813             imfEvent->mResult->setResult(QVariant::fromValue((void*)ret));
814             break;
815         }
816 
817         case ImfGetTextBeforeCursor:
818         {
819             getarg(int32_t, n);
820             getarg(int32_t, flags);
821             spannable_string_t* ret = onGetTextBeforeCursor(imfEvent->mSession, n, flags);
822             imfEvent->mResult->setResult(QVariant::fromValue((void*)ret));
823             break;
824         }
825 
826         case ImfPerformEditorAction:
827         {
828             getarg(int32_t, editor_action);
829             int32_t ret = onPerformEditorAction(imfEvent->mSession, editor_action);
830             imfEvent->mResult->setResult(QVariant::fromValue(ret));
831             break;
832         }
833 
834         case ImfReportFullscreenMode:
835         {
836             getarg(int32_t, enabled);
837             int32_t ret = onReportFullscreenMode(imfEvent->mSession, enabled);
838             imfEvent->mResult->setResult(QVariant::fromValue(ret));
839             break;
840         }
841 
842         case ImfSendEvent:
843         {
844             getparg(event_t*, event);
845             onSendEvent(imfEvent->mSession, event);
846             break;
847         }
848 
849         case ImfSendAsyncEvent:
850         {
851             getparg(event_t*, event);
852             onSendAsyncEvent(imfEvent->mSession, event);
853             break;
854         }
855 
856         case ImfSetComposingRegion:
857         {
858             getarg(int32_t, start);
859             getarg(int32_t, end);
860             int32_t ret = onSetComposingRegion(imfEvent->mSession, start, end);
861             imfEvent->mResult->setResult(QVariant::fromValue(ret));
862             break;
863         }
864 
865         case ImfSetComposingText:
866         {
867             getparg(spannable_string_t*, text);
868             getarg(int32_t, new_cursor_position);
869             int32_t ret = onSetComposingText(imfEvent->mSession, text, new_cursor_position);
870             imfEvent->mResult->setResult(QVariant::fromValue(ret));
871             break;
872         }
873 
874         case ImfSetSelection:
875         {
876             getarg(int32_t, start);
877             getarg(int32_t, end);
878             int32_t ret = onSetSelection(imfEvent->mSession, start, end);
879             imfEvent->mResult->setResult(QVariant::fromValue(ret));
880             break;
881         }
882         }; //switch
883 
884         return true;
885     } else {
886         // standard event processing
887         return QObject::eventFilter(obj, event);
888     }
889 }
890 
identifierName()891 QString QBBInputContext::identifierName()
892 {
893     return tr("PlayBook IMF");
894 }
895 
language()896 QString QBBInputContext::language()
897 {
898     return mVirtualKeyboard.languageId();
899 }
900 
filterEvent(const QEvent * event)901 bool QBBInputContext::filterEvent( const QEvent *event )
902 {
903 #if defined(QBBINPUTCONTEXT_DEBUG)
904     qDebug() << TAG << event;
905 #endif
906 
907     switch (event->type()) {
908     case QEvent::CloseSoftwareInputPanel: {
909         return dispatchCloseSoftwareInputPanel();
910     }
911     case QEvent::RequestSoftwareInputPanel: {
912         return dispatchRequestSoftwareInputPanel();
913     }
914     default:
915         return false;
916     }
917 }
918 
actions()919 QList<QAction *> QBBInputContext::actions()
920 {
921 #if defined(QBBINPUTCONTEXT_DEBUG)
922     qDebug() << TAG;
923 #endif
924     return QInputContext::actions();
925 }
926 
font() const927 QFont QBBInputContext::font() const
928 {
929 #if defined(QBBINPUTCONTEXT_DEBUG)
930     qDebug() << TAG;
931 #endif
932     return QInputContext::font();
933 }
934 
isComposing() const935 bool QBBInputContext::isComposing() const
936 {
937 #if defined(QBBINPUTCONTEXT_DEBUG)
938     qDebug() << TAG;
939 #endif
940     return mIsComposing;
941 }
942 
mouseHandler(int x,QMouseEvent * event)943 void QBBInputContext::mouseHandler(int x, QMouseEvent * event)
944 {
945 #if defined(QBBINPUTCONTEXT_DEBUG)
946     qDebug() << TAG;
947 #endif
948     return QInputContext::mouseHandler(x, event);
949 }
950 
reset()951 void QBBInputContext::reset()
952 {
953 #if defined(QBBINPUTCONTEXT_DEBUG)
954     qDebug() << TAG;
955 #endif
956 
957     endComposition();
958 }
959 
setFocusWidget(QWidget * widget)960 void QBBInputContext::setFocusWidget(QWidget * widget)
961 {
962 #if defined(QBBINPUTCONTEXT_DEBUG)
963     qDebug() << TAG;
964 #endif
965 
966     if (hasSession()) {
967         endComposition();
968         dispatchFocusEvent(FOCUS_LOST);
969         closeSession();
970     }
971 
972     // Update the widget before moving on.
973     QInputContext::setFocusWidget(widget);
974 
975     // If we have hidden text, or any flags that restrict input (exclusive flags), then we just disable
976     // imf for this field.
977     if (widget != 0 && !(widget->inputMethodHints() & Qt::ImhHiddenText) && !(widget->inputMethodHints()  >= Qt::ImhDigitsOnly && widget->inputMethodHints() <= Qt::ImhUrlCharactersOnly))
978     {
979 #if defined(QBBINPUTCONTEXT_DEBUG)
980         qDebug() << TAG << "Starting input session for " << widget;
981 #endif
982         openSession();
983         dispatchFocusEvent(FOCUS_GAINED, widget->inputMethodHints());
984     }
985 }
986 
update()987 void QBBInputContext::update()
988 {
989 #if defined(QBBINPUTCONTEXT_DEBUG)
990     qDebug() << TAG;
991 #endif
992 
993     reset();
994 
995     QInputContext::update();
996 }
997 
widgetDestroyed(QWidget * widget)998 void QBBInputContext::widgetDestroyed(QWidget * widget)
999 {
1000 #if defined(QBBINPUTCONTEXT_DEBUG)
1001     qDebug() << TAG;
1002 #endif
1003     QInputContext::widgetDestroyed(widget);
1004 }
1005 
closeSession()1006 void QBBInputContext::closeSession()
1007 {
1008 #if defined(QBBINPUTCONTEXT_DEBUG)
1009     qDebug() << TAG;;
1010 #endif
1011 
1012     if (!imfAvailable())
1013         return;
1014 
1015     if (sInputSession) {
1016         p_ictrl_close_session((input_session_t *)sInputSession);
1017         sInputSession = 0;
1018     }
1019 }
1020 
openSession()1021 void QBBInputContext::openSession()
1022 {
1023 #if defined(QBBINPUTCONTEXT_DEBUG)
1024     qDebug() << TAG;;
1025 #endif
1026 
1027     if (!imfAvailable())
1028         return;
1029 
1030     closeSession();
1031     sInputSession = p_ictrl_open_session(&ic_funcs);
1032 }
1033 
hasSession()1034 bool QBBInputContext::hasSession()
1035 {
1036     return sInputSession != 0;
1037 }
1038 
hasSelectedText()1039 bool QBBInputContext::hasSelectedText()
1040 {
1041     if (focusWidget()) {
1042         return focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt() != focusWidget()->inputMethodQuery(Qt::ImAnchorPosition).toInt();
1043     }
1044     else
1045     {
1046         return false;
1047     }
1048 }
1049 
dispatchRequestSoftwareInputPanel()1050 bool QBBInputContext::dispatchRequestSoftwareInputPanel()
1051 {
1052     mVirtualKeyboard.showKeyboard();
1053 #if defined(QBBINPUTCONTEXT_DEBUG)
1054     qDebug() << "QBB: requesting virtual keyboard";
1055 #endif
1056 
1057     if (!imfAvailable() || !focusWidget())
1058         return true;
1059 
1060     // This also means that the caret position has moved
1061     int caretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
1062     caret_event_t caretEvent;
1063     memset(&caretEvent, 0, sizeof(caret_event_t));
1064     initEvent(&caretEvent.event, sInputSession, EVENT_CARET, CARET_POS_CHANGED);
1065     caretEvent.old_pos = mLastCaretPos;
1066     mLastCaretPos = caretEvent.new_pos = caretPos;
1067     p_ictrl_dispatch_event((event_t *)&caretEvent);
1068     return true;
1069 }
1070 
dispatchCloseSoftwareInputPanel()1071 bool QBBInputContext::dispatchCloseSoftwareInputPanel()
1072 {
1073     mVirtualKeyboard.hideKeyboard();
1074 #if defined(QBBINPUTCONTEXT_DEBUG)
1075     qDebug() << "QBB: hiding virtual keyboard";
1076 #endif
1077 
1078     // This also means we are stopping composition, but we should already have done that.
1079     return true;
1080 }
1081 
1082 /*
1083  * IMF Event Dispatchers.
1084  */
dispatchFocusEvent(FocusEventId id,int hints)1085 bool QBBInputContext::dispatchFocusEvent(FocusEventId id, int hints)
1086 {
1087 #if defined(QBBINPUTCONTEXT_DEBUG)
1088     qDebug() << TAG;
1089 #endif
1090 
1091     if (!sInputSession) {
1092         qWarning() << TAG << "Attempt to dispatch a focus event with no input session.";
1093         return false;
1094     }
1095 
1096     if (!imfAvailable())
1097         return false;
1098 
1099     // Set the last caret position to 0 since we don't really have one and we don't
1100     // want to have the old one.
1101     mLastCaretPos = 0;
1102 
1103     focus_event_t focusEvent;
1104     memset(&focusEvent, 0, sizeof(focusEvent));
1105     initEvent(&focusEvent.event, sInputSession, EVENT_FOCUS, id);
1106     focusEvent.style = DEFAULT_STYLE;
1107 
1108     if (hints && Qt::ImhNoPredictiveText)
1109         focusEvent.style |= NO_PREDICTION | NO_AUTO_CORRECTION;
1110     if (hints && Qt::ImhNoAutoUppercase)
1111         focusEvent.style |= NO_AUTO_TEXT;
1112 
1113     p_ictrl_dispatch_event((event_t *)&focusEvent);
1114 
1115     return true;
1116 }
1117 
handleKeyboardEvent(int flags,int sym,int mod,int scan,int cap)1118 bool QBBInputContext::handleKeyboardEvent(int flags, int sym, int mod, int scan, int cap)
1119 {
1120     if (!imfAvailable())
1121         return false;
1122 
1123     int key = (flags & KEY_SYM_VALID) ? sym : cap;
1124     bool navKey = false;
1125     switch ( key ) {
1126     case KEYCODE_RETURN:
1127          /* In a single line edit we should end composition because enter might be used by something.
1128             endComposition();
1129             return false;*/
1130         break;
1131 
1132     case KEYCODE_BACKSPACE:
1133     case KEYCODE_DELETE:
1134         // If there is a selection range, then we want a delete key to operate on that (by
1135         // deleting the contents of the select range) rather than operating on the composition
1136         // range.
1137         if (hasSelectedText())
1138             return false;
1139         break;
1140     case  KEYCODE_LEFT:
1141         key = NAVIGATE_LEFT;
1142         navKey = true;
1143         break;
1144     case  KEYCODE_RIGHT:
1145         key = NAVIGATE_RIGHT;
1146         navKey = true;
1147         break;
1148     case  KEYCODE_UP:
1149         key = NAVIGATE_UP;
1150         navKey = true;
1151         break;
1152     case  KEYCODE_DOWN:
1153         key = NAVIGATE_DOWN;
1154         navKey = true;
1155         break;
1156     case  KEYCODE_CAPS_LOCK:
1157     case  KEYCODE_LEFT_SHIFT:
1158     case  KEYCODE_RIGHT_SHIFT:
1159     case  KEYCODE_LEFT_CTRL:
1160     case  KEYCODE_RIGHT_CTRL:
1161     case  KEYCODE_LEFT_ALT:
1162     case  KEYCODE_RIGHT_ALT:
1163     case  KEYCODE_MENU:
1164     case  KEYCODE_LEFT_HYPER:
1165     case  KEYCODE_RIGHT_HYPER:
1166     case  KEYCODE_INSERT:
1167     case  KEYCODE_HOME:
1168     case  KEYCODE_PG_UP:
1169     case  KEYCODE_END:
1170     case  KEYCODE_PG_DOWN:
1171         // Don't send these
1172         key = 0;
1173         break;
1174     }
1175 
1176     if ( mod & KEYMOD_CTRL ) {
1177         // If CTRL is pressed, just let AIR handle it.  But terminate any composition first
1178         //endComposition();
1179         return false;
1180     }
1181 
1182     // Pass the keys we don't know about on through
1183     if ( key == 0 )
1184         return false;
1185 
1186     // IMF doesn't need key releases so just swallow them.
1187     if (!(flags & KEY_DOWN))
1188         return true;
1189 
1190     if ( navKey ) {
1191         // Even if we're forwarding up events, we can't do this for
1192         // navigation keys.
1193         if ( flags & KEY_DOWN ) {
1194             navigation_event_t navEvent;
1195             initEvent(&navEvent.event, sInputSession, EVENT_NAVIGATION, key);
1196             navEvent.magnitude = 1;
1197 #if defined(QBBINPUTCONTEXT_DEBUG)
1198             qDebug() << TAG << "dispatch navigation event " << key;
1199 #endif
1200             p_ictrl_dispatch_event(&navEvent.event);
1201         }
1202     }
1203     else {
1204         key_event_t keyEvent;
1205         initEvent(&keyEvent.event, sInputSession, EVENT_KEY, flags & KEY_DOWN ? IMF_KEY_DOWN : IMF_KEY_UP);
1206         keyEvent.key_code = key;
1207         keyEvent.character = 0;
1208         keyEvent.meta_key_state = 0;
1209 
1210         p_ictrl_dispatch_event(&keyEvent.event);
1211 #if defined(QBBINPUTCONTEXT_DEBUG)
1212         qDebug() << TAG << "dispatch key event " << key;
1213 #endif
1214     }
1215 
1216     scan = 0;
1217     return true;
1218 }
1219 
endComposition()1220 void QBBInputContext::endComposition()
1221 {
1222     if (!imfAvailable())
1223         return;
1224 
1225     if (!isComposing())
1226         return;
1227 
1228     QList<QInputMethodEvent::Attribute> attributes;
1229     QInputMethodEvent event(QLatin1String(""), attributes);
1230     event.setCommitString(mComposingText);
1231     mComposingText = QString();
1232     mIsComposing = false;
1233     sendEvent(event);
1234 
1235     action_event_t actionEvent;
1236     memset(&actionEvent, 0, sizeof(actionEvent));
1237     initEvent(&actionEvent.event, sInputSession, EVENT_ACTION, ACTION_END_COMPOSITION);
1238     p_ictrl_dispatch_event(&actionEvent.event);
1239 }
1240 
setComposingText(QString const & composingText)1241 void QBBInputContext::setComposingText(QString const& composingText)
1242 {
1243     mComposingText = composingText;
1244     mIsComposing = true;
1245 
1246     QList<QInputMethodEvent::Attribute> attributes;
1247     QTextCharFormat format;
1248     format.setFontUnderline(true);
1249     attributes.push_back(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, 0, composingText.length(), format));
1250 
1251     QInputMethodEvent event(composingText, attributes);
1252 
1253     sendEvent(event);
1254 }
1255 
processEvent(event_t * event)1256 int32_t QBBInputContext::processEvent(event_t* event)
1257 {
1258     int32_t result = -1;
1259     switch (event->event_type) {
1260     case EVENT_SPELL_CHECK: {
1261         #if defined(QBBINPUTCONTEXT_DEBUG)
1262         qDebug() << TAG << "EVENT_SPELL_CHECK";
1263         #endif
1264         result = 0;
1265         break;
1266     }
1267 
1268     case EVENT_NAVIGATION: {
1269         #if defined(QBBINPUTCONTEXT_DEBUG)
1270         qDebug() << TAG << "EVENT_NAVIGATION";
1271         #endif
1272 
1273         int key = event->event_id == NAVIGATE_UP ? KEYCODE_UP :
1274             event->event_id == NAVIGATE_DOWN ? KEYCODE_DOWN :
1275             event->event_id == NAVIGATE_LEFT ? KEYCODE_LEFT :
1276             event->event_id == NAVIGATE_RIGHT ? KEYCODE_RIGHT : 0;
1277 
1278         QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, key, 0, 0, 0);
1279         QBBEventThread::injectKeyboardEvent(KEY_CAP_VALID, key, 0, 0, 0);
1280         result = 0;
1281         break;
1282     }
1283 
1284     case EVENT_KEY: {
1285         #if defined(QBBINPUTCONTEXT_DEBUG)
1286         qDebug() << TAG << "EVENT_KEY";
1287         #endif
1288         key_event_t* kevent = (key_event_t*) event;
1289 
1290         QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
1291         QBBEventThread::injectKeyboardEvent(KEY_SYM_VALID | KEY_CAP_VALID, kevent->key_code, 0, 0, kevent->key_code);
1292 
1293         result = 0;
1294         break;
1295     }
1296 
1297     case EVENT_ACTION:
1298             // Don't care, indicates that IMF is done.
1299         break;
1300 
1301     case EVENT_CARET:
1302     case EVENT_NOTHING:
1303     case EVENT_FOCUS:
1304     case EVENT_USER_ACTION:
1305     case EVENT_STROKE:
1306     case EVENT_INVOKE_LATER:
1307         qCritical() << TAG << "Unsupported event type: " << event->event_type;
1308         break;
1309     default:
1310         qCritical() << TAG << "Unknown event type: " << event->event_type;
1311     }
1312     return result;
1313 }
1314 
1315 /*
1316  * IMF Event Handlers
1317  */
1318 
onBeginBatchEdit(input_session_t * ic)1319 int32_t QBBInputContext::onBeginBatchEdit(input_session_t* ic)
1320 {
1321 #if defined(QBBINPUTCONTEXT_DEBUG)
1322     qDebug() << TAG;
1323 #endif
1324 
1325     if (!isSessionOkay(ic))
1326         return 0;
1327 
1328     // We don't care.
1329     return 0;
1330 }
1331 
onClearMetaKeyStates(input_session_t * ic,int32_t states)1332 int32_t QBBInputContext::onClearMetaKeyStates(input_session_t* ic, int32_t states)
1333 {
1334     Q_UNUSED(states);
1335 #if defined(QBBINPUTCONTEXT_DEBUG)
1336     qDebug() << TAG;
1337 #endif
1338 
1339     if (!isSessionOkay(ic))
1340         return 0;
1341 
1342     // Should never get called.
1343     qCritical() << TAG << "onClearMetaKeyStates is unsupported.";
1344     return 0;
1345 }
1346 
onCommitText(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)1347 int32_t QBBInputContext::onCommitText(input_session_t* ic, spannable_string_t* text, int32_t new_cursor_position)
1348 {
1349     Q_UNUSED(new_cursor_position);  // TODO: How can we set the cursor position it's not part of the API.
1350     if (!isSessionOkay(ic))
1351         return 0;
1352 
1353     QString commitString = QString::fromWCharArray(text->str, text->length);
1354 
1355 #if defined(QBBINPUTCONTEXT_DEBUG)
1356     qDebug() << TAG << "Committing [" << commitString << "]";
1357 #endif
1358 
1359     QList<QInputMethodEvent::Attribute> attributes;
1360     QInputMethodEvent event(QLatin1String(""), attributes);
1361     event.setCommitString(commitString, 0, 0);
1362 
1363     sendEvent(event);
1364     mComposingText = QString();
1365 
1366     return 0;
1367 }
1368 
onDeleteSurroundingText(input_session_t * ic,int32_t left_length,int32_t right_length)1369 int32_t QBBInputContext::onDeleteSurroundingText(input_session_t* ic, int32_t left_length, int32_t right_length)
1370 {
1371 #if defined(QBBINPUTCONTEXT_DEBUG)
1372     qDebug() << TAG << "L:" << left_length << " R:" << right_length;
1373 #endif
1374 
1375     if (!isSessionOkay(ic))
1376         return 0;
1377 
1378     if (hasSelectedText()) {
1379         QBBEventThread::injectKeyboardEvent(KEY_DOWN | KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
1380         QBBEventThread::injectKeyboardEvent(KEY_CAP_VALID, KEYCODE_DELETE, 0, 0, 0);
1381         reset();
1382         return 0;
1383     }
1384 
1385     int replacementLength = left_length + right_length;
1386     int replacementStart = -left_length;
1387 
1388     QList<QInputMethodEvent::Attribute> attributes;
1389     QInputMethodEvent event(QLatin1String(""), attributes);
1390     event.setCommitString(QLatin1String(""), replacementStart, replacementLength);
1391     sendEvent(event);
1392 
1393     return 0;
1394 }
1395 
onEndBatchEdit(input_session_t * ic)1396 int32_t QBBInputContext::onEndBatchEdit(input_session_t* ic)
1397 {
1398 #if defined(QBBINPUTCONTEXT_DEBUG)
1399     qDebug() << TAG;
1400 #endif
1401 
1402     if (!isSessionOkay(ic))
1403         return 0;
1404 
1405     return 0;
1406 }
1407 
onFinishComposingText(input_session_t * ic)1408 int32_t QBBInputContext::onFinishComposingText(input_session_t* ic)
1409 {
1410 #if defined(QBBINPUTCONTEXT_DEBUG)
1411     qDebug() << TAG;
1412 #endif
1413 
1414     if (!isSessionOkay(ic))
1415         return 0;
1416 
1417     // Only update the control, no need to send a message back to imf (don't call
1418     // end composition)
1419     QList<QInputMethodEvent::Attribute> attributes;
1420     QInputMethodEvent event(QLatin1String(""), attributes);
1421     event.setCommitString(mComposingText);
1422     mComposingText = QString();
1423     mIsComposing = false;
1424     sendEvent(event);
1425 
1426     return 0;
1427 }
1428 
onGetCursorCapsMode(input_session_t * ic,int32_t req_modes)1429 int32_t QBBInputContext::onGetCursorCapsMode(input_session_t* ic, int32_t req_modes)
1430 {
1431     Q_UNUSED(req_modes);
1432 #if defined(QBBINPUTCONTEXT_DEBUG)
1433     qDebug() << TAG;
1434 #endif
1435 
1436     if (!isSessionOkay(ic))
1437         return 0;
1438 
1439     // Should never get called.
1440     qCritical() << TAG << "onGetCursorCapsMode is unsupported.";
1441 
1442     return 0;
1443 }
1444 
onGetCursorPosition(input_session_t * ic)1445 int32_t QBBInputContext::onGetCursorPosition(input_session_t* ic)
1446 {
1447 #if defined(QBBINPUTCONTEXT_DEBUG)
1448     qDebug() << TAG;
1449 #endif
1450 
1451     if (!isSessionOkay(ic))
1452         return 0;
1453 
1454     mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
1455     return mLastCaretPos;
1456 }
1457 
onGetExtractedText(input_session_t * ic,extracted_text_request_t * request,int32_t flags)1458 extracted_text_t* QBBInputContext::onGetExtractedText(input_session_t* ic, extracted_text_request_t* request, int32_t flags)
1459 {
1460     Q_UNUSED(flags);
1461     Q_UNUSED(request);
1462 #if defined(QBBINPUTCONTEXT_DEBUG)
1463     qDebug() << TAG;
1464 #endif
1465 
1466     if (!isSessionOkay(ic)) {
1467         extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
1468         et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1);
1469         return 0;
1470     }
1471 
1472     // Used to update dictionaries, but not supported right now.
1473     extracted_text_t *et = (extracted_text_t *)calloc(sizeof(extracted_text_t),1);
1474     et->text = (spannable_string_t *)calloc(sizeof(spannable_string_t),1);
1475 
1476     return et;
1477 }
1478 
onGetSelectedText(input_session_t * ic,int32_t flags)1479 spannable_string_t* QBBInputContext::onGetSelectedText(input_session_t* ic, int32_t flags)
1480 {
1481     Q_UNUSED(flags);
1482 #if defined(QBBINPUTCONTEXT_DEBUG)
1483     qDebug() << TAG;
1484 #endif
1485 
1486     if (!isSessionOkay(ic))
1487         return toSpannableString("");
1488 
1489     QString text = focusWidget()->inputMethodQuery(Qt::ImCurrentSelection).toString();
1490     return toSpannableString(text);
1491 }
1492 
onGetTextAfterCursor(input_session_t * ic,int32_t n,int32_t flags)1493 spannable_string_t* QBBInputContext::onGetTextAfterCursor(input_session_t* ic, int32_t n, int32_t flags)
1494 {
1495     Q_UNUSED(flags);
1496 #if defined(QBBINPUTCONTEXT_DEBUG)
1497     qDebug() << TAG;
1498 #endif
1499 
1500     if (!isSessionOkay(ic))
1501         return toSpannableString("");
1502 
1503     QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
1504     mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
1505 
1506     return toSpannableString(text.mid(mLastCaretPos+1, n));
1507 }
1508 
onGetTextBeforeCursor(input_session_t * ic,int32_t n,int32_t flags)1509 spannable_string_t* QBBInputContext::onGetTextBeforeCursor(input_session_t* ic, int32_t n, int32_t flags)
1510 {
1511     Q_UNUSED(flags);
1512 #if defined(QBBINPUTCONTEXT_DEBUG)
1513     qDebug() << TAG;
1514 #endif
1515 
1516     if (!isSessionOkay(ic))
1517         return toSpannableString("");
1518 
1519     QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
1520     mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
1521 
1522     if (n < mLastCaretPos)
1523     {
1524         return toSpannableString(text.mid(mLastCaretPos - n, n));
1525     }
1526     else
1527         return toSpannableString(text.mid(0, mLastCaretPos));
1528 }
1529 
onPerformEditorAction(input_session_t * ic,int32_t editor_action)1530 int32_t QBBInputContext::onPerformEditorAction(input_session_t* ic, int32_t editor_action)
1531 {
1532     Q_UNUSED(editor_action);
1533 #if defined(QBBINPUTCONTEXT_DEBUG)
1534     qDebug() << TAG;
1535 #endif
1536 
1537     if (!isSessionOkay(ic))
1538         return 0;
1539 
1540     // Should never get called.
1541     qCritical() << TAG << "onPerformEditorAction is unsupported.";
1542 
1543     return 0;
1544 }
1545 
onReportFullscreenMode(input_session_t * ic,int32_t enabled)1546 int32_t QBBInputContext::onReportFullscreenMode(input_session_t* ic, int32_t enabled)
1547 {
1548     Q_UNUSED(enabled);
1549 #if defined(QBBINPUTCONTEXT_DEBUG)
1550     qDebug() << TAG;
1551 #endif
1552 
1553     if (!isSessionOkay(ic))
1554         return 0;
1555 
1556     // Should never get called.
1557     qCritical() << TAG << "onReportFullscreenMode is unsupported.";
1558 
1559     return 0;
1560 }
1561 
onSendEvent(input_session_t * ic,event_t * event)1562 int32_t QBBInputContext::onSendEvent(input_session_t* ic, event_t * event)
1563 {
1564 #if defined(QBBINPUTCONTEXT_DEBUG)
1565     qDebug() << TAG;
1566 #endif
1567 
1568     if (!isSessionOkay(ic))
1569         return 0;
1570 
1571     return processEvent(event);
1572 }
1573 
onSendAsyncEvent(input_session_t * ic,event_t * event)1574 int32_t QBBInputContext::onSendAsyncEvent(input_session_t* ic, event_t * event)
1575 {
1576 #if defined(QBBINPUTCONTEXT_DEBUG)
1577     qDebug() << TAG;
1578 #endif
1579 
1580     if (!isSessionOkay(ic))
1581         return 0;
1582 
1583     return processEvent(event);
1584 }
1585 
onSetComposingRegion(input_session_t * ic,int32_t start,int32_t end)1586 int32_t QBBInputContext::onSetComposingRegion(input_session_t* ic, int32_t start, int32_t end)
1587 {
1588 #if defined(QBBINPUTCONTEXT_DEBUG)
1589     qDebug() << TAG;
1590 #endif
1591 
1592     if (!isSessionOkay(ic))
1593         return 0;
1594 
1595     if (!focusWidget())
1596     {
1597         qCritical() << "No focus widget!";
1598         return 0;
1599     }
1600 
1601     QList<QInputMethodEvent::Attribute> attributes;
1602 
1603     mLastCaretPos = focusWidget()->inputMethodQuery(Qt::ImCursorPosition).toInt();
1604     QString text = focusWidget()->inputMethodQuery(Qt::ImSurroundingText).toString();
1605     QString empty = QString::fromLatin1("");
1606     text = text.mid(start, end - start);
1607 
1608     // Delete the current text.
1609     {
1610         QInputMethodEvent event(empty, attributes);
1611         event.setCommitString(empty, start - mLastCaretPos, end - start);
1612         sendEvent(event);
1613     }
1614 
1615     // Move the specified text into a preedit string.
1616     {
1617         setComposingText(text);
1618     }
1619 
1620     return 0;
1621 }
1622 
onSetComposingText(input_session_t * ic,spannable_string_t * text,int32_t new_cursor_position)1623 int32_t QBBInputContext::onSetComposingText(input_session_t* ic, spannable_string_t* text, int32_t new_cursor_position)
1624 {
1625     Q_UNUSED(new_cursor_position);
1626 #if defined(QBBINPUTCONTEXT_DEBUG)
1627     qDebug() << TAG;
1628 #endif
1629 
1630     if (!isSessionOkay(ic))
1631         return 0;
1632 
1633     if (!focusWidget())
1634     {
1635         qCritical() << "No focus widget!";
1636         return 0;
1637     }
1638 
1639     mIsComposing = true;
1640 
1641     QString preeditString = QString::fromWCharArray(text->str, text->length);
1642     setComposingText(preeditString);
1643 
1644     return 0;
1645 }
1646 
onSetSelection(input_session_t * ic,int32_t start,int32_t end)1647 int32_t QBBInputContext::onSetSelection(input_session_t* ic, int32_t start, int32_t end)
1648 {
1649     Q_UNUSED(start);
1650     Q_UNUSED(end);
1651 #if defined(QBBINPUTCONTEXT_DEBUG)
1652     qDebug() << TAG;
1653 #endif
1654 
1655     if (!isSessionOkay(ic))
1656         return 0;
1657 
1658     // Should never get called.
1659     qCritical() << TAG << "onSetSelection is unsupported.";
1660 
1661     return 0;
1662 }
1663 
1664 QT_END_NAMESPACE
1665