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