1 /* -*- Mode: C++; indent-tabs-mode: nil; c-basic-offset: 2; tab-width: 2 -*- */
2 /*
3  * im-nimf-qt5.cpp
4  * This file is part of Nimf.
5  *
6  * Copyright (C) 2015-2019 Hodong Kim <cogniti@gmail.com>
7  *
8  * Nimf is free software: you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published
10  * by the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * Nimf is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
16  * See the GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program;  If not, see <http://www.gnu.org/licenses/>.
20  */
21 
22 #include <QTextFormat>
23 #include <QInputMethodEvent>
24 #include <QtGui/qpa/qplatforminputcontext.h>
25 #include <QtGui/qpa/qplatforminputcontextplugin_p.h>
26 #include <QtWidgets/QApplication>
27 #include <QtWidgets/QWidget>
28 #include "nimf-im.h"
29 #include <gio/gio.h>
30 
31 #ifdef USE_DLFCN
32 
33 #include <dlfcn.h>
34 
35 typedef struct
36 {
37   NimfIM * (* im_new)                 (void);
38   void     (* im_focus_in)            (NimfIM              *im);
39   void     (* im_focus_out)           (NimfIM              *im);
40   void     (* im_reset)               (NimfIM              *im);
41   gboolean (* im_filter_event)        (NimfIM              *im,
42                                        NimfEvent           *event);
43   void     (* im_get_preedit_string)  (NimfIM              *im,
44                                        gchar              **str,
45                                        NimfPreeditAttr   ***attrs,
46                                        gint                *cursor_pos);
47   void     (* im_set_cursor_location) (NimfIM              *im,
48                                        const NimfRectangle *area);
49   void     (* im_set_use_preedit)     (NimfIM              *im,
50                                        gboolean             use_preedit);
51   void     (* im_set_surrounding)     (NimfIM              *im,
52                                        const char          *text,
53                                        gint                 len,
54                                        gint                 cursor_index);
55   NimfEvent * (* event_new)           (NimfEventType     type);
56   void        (* event_free)          (NimfEvent        *event);
57   void        (* preedit_attr_freev)  (NimfPreeditAttr **attrs);
58 } NimfAPI;
59 
60 typedef struct
61 {
62   void (* free) (gpointer mem);
63 } GLibAPI;
64 
65 typedef struct
66 {
67   gulong (* signal_connect_data) (gpointer       instance,
68                                   const gchar   *detailed_signal,
69                                   GCallback      c_handler,
70                                   gpointer       data,
71                                   GClosureNotify destroy_data,
72                                   GConnectFlags  connect_flags);
73   void   (* signal_emit_by_name) (gpointer       instance,
74                                   const gchar   *detailed_signal,
75                                   ...);
76   void   (* object_unref)        (gpointer       object);
77 } GObjectAPI;
78 
79 typedef struct
80 {
81   GSettings * (* settings_new)         (const gchar   *schema_id);
82   gboolean    (* settings_get_boolean) (GSettings     *settings,
83                                         const gchar   *key);
84   GSettingsSchemaSource *
85               (* settings_schema_source_get_default) (void);
86   GSettingsSchema *
87               (* settings_schema_source_lookup)
88                                         (GSettingsSchemaSource *source,
89                                          const gchar           *schema_id,
90                                          gboolean               recursive);
91   void        (* settings_schema_unref) (GSettingsSchema       *schema);
92 } GIOAPI;
93 
94 void *libnimf    = NULL;
95 void *libglib    = NULL;
96 void *libgobject = NULL;
97 void *libgio     = NULL;
98 NimfAPI    *nimf_api    = NULL;
99 GLibAPI    *glib_api    = NULL;
100 GObjectAPI *gobject_api = NULL;
101 GIOAPI     *gio_api     = NULL;
102 
103 #endif
104 
105 class NimfEventHandler : public QObject
106 {
107   Q_OBJECT
108 
109 public:
NimfEventHandler(NimfIM * im)110   NimfEventHandler(NimfIM *im)
111   {
112     m_im = im;
113   };
114 
~NimfEventHandler()115   ~NimfEventHandler()
116   {};
117 
118 protected:
119   bool eventFilter(QObject *obj, QEvent *event);
120 
121 private:
122   NimfIM *m_im;
123 };
124 
eventFilter(QObject * obj,QEvent * event)125 bool NimfEventHandler::eventFilter(QObject *obj, QEvent *event)
126 {
127   if (event->type() == QEvent::MouseButtonPress)
128 #ifndef USE_DLFCN
129     nimf_im_reset (m_im);
130 #else
131     nimf_api->im_reset (m_im);
132 #endif
133 
134   return QObject::eventFilter(obj, event);
135 }
136 
137 class NimfInputContext : public QPlatformInputContext
138 {
139   Q_OBJECT
140 public:
141    NimfInputContext ();
142   ~NimfInputContext ();
143 
144   virtual bool isValid () const;
145   virtual void reset ();
146   virtual void commit ();
147   virtual void update (Qt::InputMethodQueries);
148   virtual void invokeAction (QInputMethod::Action, int cursorPosition);
149   virtual bool filterEvent (const QEvent *event);
150   virtual QRectF keyboardRect () const;
151   virtual bool isAnimating () const;
152   virtual void showInputPanel ();
153   virtual void hideInputPanel ();
154   virtual bool isInputPanelVisible () const;
155   virtual QLocale locale () const;
156   virtual Qt::LayoutDirection inputDirection() const;
157   virtual void setFocusObject (QObject *object);
158 
159   // nimf signal callbacks
160   static void     on_preedit_start        (NimfIM      *im,
161                                            gpointer     user_data);
162   static void     on_preedit_end          (NimfIM      *im,
163                                            gpointer     user_data);
164   static void     on_preedit_changed      (NimfIM      *im,
165                                            gpointer     user_data);
166   static void     on_commit               (NimfIM      *im,
167                                            const gchar *text,
168                                            gpointer     user_data);
169   static gboolean on_retrieve_surrounding (NimfIM      *im,
170                                            gpointer     user_data);
171   static gboolean on_delete_surrounding   (NimfIM      *im,
172                                            gint         offset,
173                                            gint         n_chars,
174                                            gpointer     user_data);
175   static void     on_beep                 (NimfIM      *im,
176                                            gpointer     user_data);
177   // settings
178   static void on_changed_reset_on_mouse_button_press (GSettings *settings,
179                                                       gchar     *key,
180                                                       gpointer   user_data);
181 private:
182   NimfIM           *m_im;
183   GSettings        *m_settings;
184   NimfEventHandler *m_handler;
185   NimfRectangle     m_cursor_area;
186 };
187 
188 /* nimf signal callbacks */
189 void
on_preedit_start(NimfIM * im,gpointer user_data)190 NimfInputContext::on_preedit_start (NimfIM *im, gpointer user_data)
191 {
192 #ifndef USE_DLFCN
193   g_debug (G_STRLOC ": %s", G_STRFUNC);
194 #endif
195 }
196 
197 void
on_preedit_end(NimfIM * im,gpointer user_data)198 NimfInputContext::on_preedit_end (NimfIM *im, gpointer user_data)
199 {
200 #ifndef USE_DLFCN
201   g_debug (G_STRLOC ": %s", G_STRFUNC);
202 #endif
203 }
204 
205 void
on_preedit_changed(NimfIM * im,gpointer user_data)206 NimfInputContext::on_preedit_changed (NimfIM *im, gpointer user_data)
207 {
208 #ifndef USE_DLFCN
209   g_debug (G_STRLOC ": %s", G_STRFUNC);
210 #endif
211 
212   NimfPreeditAttr **preedit_attrs;
213   gchar            *str;
214   gint              cursor_pos;
215   gint              offset = 0;
216   guint             i, j, len;
217 
218 #ifndef USE_DLFCN
219   nimf_im_get_preedit_string (im, &str, &preedit_attrs, &cursor_pos);
220 #else
221   nimf_api->im_get_preedit_string (im, &str, &preedit_attrs, &cursor_pos);
222 #endif
223 
224   QString preeditText = QString::fromUtf8 (str);
225 #ifndef USE_DLFCN
226   g_free (str);
227 #else
228   glib_api->free (str);
229 #endif
230   QList <QInputMethodEvent::Attribute> attrs;
231 
232   for (i = 0; i < (guint) preeditText.size(); i++)
233   {
234     if (preeditText.at(i).isLowSurrogate())
235     {
236       offset++;
237       continue;
238     }
239 
240     QTextCharFormat format;
241 
242     for (j = 0; preedit_attrs[j]; j++)
243     {
244       switch (preedit_attrs[j]->type)
245       {
246         case NIMF_PREEDIT_ATTR_HIGHLIGHT:
247           if (preedit_attrs[j]->start_index <= i - offset &&
248               preedit_attrs[j]->end_index   >  i - offset)
249           {
250             format.setBackground(Qt::green);
251             format.setForeground(Qt::black);
252           }
253           break;
254         case NIMF_PREEDIT_ATTR_UNDERLINE:
255           if (preedit_attrs[j]->start_index <= i - offset &&
256               preedit_attrs[j]->end_index   >  i - offset)
257             format.setUnderlineStyle(QTextCharFormat::DashUnderline);
258           break;
259         default:
260           break;
261       }
262     }
263 
264     preeditText.at(i).isHighSurrogate() ? len = 2 : len = 1;
265     QInputMethodEvent::Attribute attr (QInputMethodEvent::TextFormat,
266                                        i, len, format);
267     attrs << attr;
268   }
269 
270 #ifndef USE_DLFCN
271   nimf_preedit_attr_freev (preedit_attrs);
272 #else
273   nimf_api->preedit_attr_freev (preedit_attrs);
274 #endif
275 
276   // cursor attribute
277   attrs << QInputMethodEvent::Attribute (QInputMethodEvent::Cursor,
278                                          cursor_pos + offset, true, 0);
279 
280   QInputMethodEvent event (preeditText, attrs);
281   QObject *object = qApp->focusObject ();
282 
283   if (!object)
284     return;
285 
286   QCoreApplication::sendEvent (object, &event);
287 }
288 
289 void
on_commit(NimfIM * im,const gchar * text,gpointer user_data)290 NimfInputContext::on_commit (NimfIM      *im,
291                              const gchar *text,
292                              gpointer     user_data)
293 {
294 #ifndef USE_DLFCN
295   g_debug (G_STRLOC ": %s", G_STRFUNC);
296 #endif
297 
298   QString str = QString::fromUtf8 (text);
299   QInputMethodEvent event;
300   event.setCommitString (str);
301 
302   QObject *obj = qApp->focusObject();
303 
304   if (!obj)
305     return;
306 
307   QCoreApplication::sendEvent (obj, &event);
308 }
309 
310 gboolean
on_retrieve_surrounding(NimfIM * im,gpointer user_data)311 NimfInputContext::on_retrieve_surrounding (NimfIM *im, gpointer user_data)
312 {
313 #ifndef USE_DLFCN
314   g_debug (G_STRLOC ": %s", G_STRFUNC);
315 #endif
316 
317   QObject *object = qApp->focusObject();
318 
319   if (!object)
320     return FALSE;
321 
322   NimfInputContext *context = static_cast<NimfInputContext *>(user_data);
323 
324   QInputMethodQueryEvent surrounding_query (Qt::ImSurroundingText);
325   QInputMethodQueryEvent position_query    (Qt::ImCursorPosition);
326 
327   QCoreApplication::sendEvent (object, &surrounding_query);
328   QCoreApplication::sendEvent (object, &position_query);
329 
330   QString string = surrounding_query.value (Qt::ImSurroundingText).toString();
331   uint pos = position_query.value (Qt::ImCursorPosition).toUInt();
332 
333 #ifndef USE_DLFCN
334   nimf_im_set_surrounding (context->m_im,
335                            string.toUtf8().constData(), -1, pos);
336 #else
337   nimf_api->im_set_surrounding (context->m_im,
338                                 string.toUtf8().constData(), -1, pos);
339 #endif
340 
341   return TRUE;
342 }
343 
344 gboolean
on_delete_surrounding(NimfIM * im,gint offset,gint n_chars,gpointer user_data)345 NimfInputContext::on_delete_surrounding (NimfIM   *im,
346                                          gint      offset,
347                                          gint      n_chars,
348                                          gpointer  user_data)
349 {
350 #ifndef USE_DLFCN
351   g_debug (G_STRLOC ": %s", G_STRFUNC);
352 #endif
353 
354   QObject *object = qApp->focusObject();
355 
356   if (!object)
357     return FALSE;
358 
359   QInputMethodEvent event;
360   event.setCommitString ("", offset, n_chars);
361   QCoreApplication::sendEvent (object, &event);
362 
363   return TRUE;
364 }
365 
366 void
on_beep(NimfIM * im,gpointer user_data)367 NimfInputContext::on_beep (NimfIM *im, gpointer user_data)
368 {
369 #ifndef USE_DLFCN
370   g_debug (G_STRLOC ": %s", G_STRFUNC);
371 #endif
372 
373   QApplication::beep();
374 }
375 
376 void
on_changed_reset_on_mouse_button_press(GSettings * settings,gchar * key,gpointer user_data)377 NimfInputContext::on_changed_reset_on_mouse_button_press (GSettings *settings,
378                                                           gchar     *key,
379                                                           gpointer   user_data)
380 {
381 #ifndef USE_DLFCN
382   g_debug (G_STRLOC ": %s", G_STRFUNC);
383 #endif
384 
385   NimfInputContext *context = static_cast<NimfInputContext *>(user_data);
386 
387 #ifndef USE_DLFCN
388   if (g_settings_get_boolean (settings, key))
389 #else
390   if (gio_api->settings_get_boolean (settings, key))
391 #endif
392   {
393     if (context->m_handler == NULL)
394     {
395       context->m_handler = new NimfEventHandler(context->m_im);
396       qApp->installEventFilter(context->m_handler);
397     }
398   }
399   else
400   {
401     if (context->m_handler)
402     {
403       qApp->removeEventFilter(context->m_handler);
404       delete context->m_handler;
405       context->m_handler = NULL;
406     }
407   }
408 }
409 
NimfInputContext()410 NimfInputContext::NimfInputContext ()
411 {
412 #ifndef USE_DLFCN
413   g_debug (G_STRLOC ": %s", G_STRFUNC);
414 #endif
415 
416   m_im                 = NULL;
417   m_settings           = NULL;
418   m_handler            = NULL;
419   m_cursor_area.x      = 0;
420   m_cursor_area.y      = 0;
421   m_cursor_area.width  = 0;
422   m_cursor_area.height = 0;
423 
424 #ifndef USE_DLFCN
425   m_im = nimf_im_new ();
426   m_settings = g_settings_new ("org.nimf.clients.qt5");
427   g_signal_connect (m_im, "preedit-start",
428                     G_CALLBACK (NimfInputContext::on_preedit_start), this);
429   g_signal_connect (m_im, "preedit-end",
430                     G_CALLBACK (NimfInputContext::on_preedit_end), this);
431   g_signal_connect (m_im, "preedit-changed",
432                     G_CALLBACK (NimfInputContext::on_preedit_changed), this);
433   g_signal_connect (m_im, "commit",
434                     G_CALLBACK (NimfInputContext::on_commit), this);
435   g_signal_connect (m_im, "retrieve-surrounding",
436                     G_CALLBACK (NimfInputContext::on_retrieve_surrounding),
437                     this);
438   g_signal_connect (m_im, "delete-surrounding",
439                     G_CALLBACK (NimfInputContext::on_delete_surrounding),
440                     this);
441   g_signal_connect (m_im, "beep",
442                     G_CALLBACK (NimfInputContext::on_beep), this);
443 
444   g_signal_connect (m_settings, "changed::reset-on-mouse-button-press",
445                     G_CALLBACK (NimfInputContext::on_changed_reset_on_mouse_button_press), this);
446   g_signal_emit_by_name (m_settings, "changed::reset-on-mouse-button-press",
447                                      "reset-on-mouse-button-press");
448 #else
449   if (!nimf_api || !glib_api || !gobject_api || !gio_api)
450   {
451     qWarning("The libraries for nimf are not ready.");
452     return;
453   }
454 
455   GSettingsSchemaSource *source;
456   GSettingsSchema       *schema;
457 
458   source = gio_api->settings_schema_source_get_default ();
459   schema = gio_api->settings_schema_source_lookup (source, "org.nimf.clients.qt5", TRUE);
460 
461   if (schema == NULL)
462   {
463     qWarning("org.nimf.clients.qt5 schema is not found.");
464     return;
465   }
466 
467   gio_api->settings_schema_unref (schema);
468 
469   m_im = nimf_api->im_new ();
470   m_settings = gio_api->settings_new ("org.nimf.clients.qt5");
471   gobject_api->signal_connect_data (m_im, "preedit-start",
472                                     G_CALLBACK (NimfInputContext::on_preedit_start), this, NULL, (GConnectFlags) 0);
473   gobject_api->signal_connect_data (m_im, "preedit-end",
474                                     G_CALLBACK (NimfInputContext::on_preedit_end), this, NULL, (GConnectFlags) 0);
475   gobject_api->signal_connect_data (m_im, "preedit-changed",
476                                     G_CALLBACK (NimfInputContext::on_preedit_changed), this, NULL, (GConnectFlags) 0);
477   gobject_api->signal_connect_data (m_im, "commit",
478                                     G_CALLBACK (NimfInputContext::on_commit), this, NULL, (GConnectFlags) 0);
479   gobject_api->signal_connect_data (m_im, "retrieve-surrounding",
480                                     G_CALLBACK (NimfInputContext::on_retrieve_surrounding),
481                                     this, NULL, (GConnectFlags) 0);
482   gobject_api->signal_connect_data (m_im, "delete-surrounding",
483                                     G_CALLBACK (NimfInputContext::on_delete_surrounding),
484                                     this, NULL, (GConnectFlags) 0);
485   gobject_api->signal_connect_data (m_im, "beep",
486                                     G_CALLBACK (NimfInputContext::on_beep), this, NULL, (GConnectFlags) 0);
487   gobject_api->signal_connect_data (m_settings, "changed::reset-on-mouse-button-press",
488                                     G_CALLBACK (NimfInputContext::on_changed_reset_on_mouse_button_press), this, NULL, (GConnectFlags) 0);
489   gobject_api->signal_emit_by_name (m_settings, "changed::reset-on-mouse-button-press",
490                                     "reset-on-mouse-button-press");
491 #endif
492 }
493 
~NimfInputContext()494 NimfInputContext::~NimfInputContext ()
495 {
496 #ifndef USE_DLFCN
497   g_debug (G_STRLOC ": %s", G_STRFUNC);
498 #endif
499 
500   if (m_handler)
501     delete m_handler;
502 
503   if (m_im)
504 #ifndef USE_DLFCN
505     g_object_unref (m_im);
506 #else
507     gobject_api->object_unref (m_im);
508 #endif
509 
510   if (m_settings)
511 #ifndef USE_DLFCN
512     g_object_unref (m_settings);
513 #else
514     gobject_api->object_unref (m_settings);
515 #endif
516 }
517 
518 bool
isValid() const519 NimfInputContext::isValid () const
520 {
521 #ifndef USE_DLFCN
522   g_debug (G_STRLOC ": %s", G_STRFUNC);
523 #endif
524 
525   if (m_im == NULL)
526     return false;
527 
528   return true;
529 }
530 
531 void
reset()532 NimfInputContext::reset ()
533 {
534 #ifndef USE_DLFCN
535   g_debug (G_STRLOC ": %s", G_STRFUNC);
536 
537   nimf_im_reset (m_im);
538 #else
539   nimf_api->im_reset (m_im);
540 #endif
541 }
542 
543 void
commit()544 NimfInputContext::commit ()
545 {
546 #ifndef USE_DLFCN
547   g_debug (G_STRLOC ": %s", G_STRFUNC);
548 
549   nimf_im_reset (m_im);
550 #else
551   nimf_api->im_reset (m_im);
552 #endif
553 }
554 
555 void
update(Qt::InputMethodQueries queries)556 NimfInputContext::update (Qt::InputMethodQueries queries)
557 {
558 #ifndef USE_DLFCN
559   g_debug (G_STRLOC ": %s", G_STRFUNC);
560 #endif
561 
562   if (queries & Qt::ImCursorRectangle)
563   {
564     QWidget *widget = qApp->focusWidget ();
565 
566     if (widget == NULL)
567       return;
568 
569     QRect  rect  = widget->inputMethodQuery(Qt::ImCursorRectangle).toRect();
570     QPoint point = widget->mapToGlobal (QPoint (0, 0));
571     rect.translate (point);
572 
573     if (m_cursor_area.x      != rect.x ()     ||
574         m_cursor_area.y      != rect.y ()     ||
575         m_cursor_area.width  != rect.width () ||
576         m_cursor_area.height != rect.height ())
577     {
578       m_cursor_area.x      = rect.x ();
579       m_cursor_area.y      = rect.y ();
580       m_cursor_area.width  = rect.width ();
581       m_cursor_area.height = rect.height ();
582 
583 #ifndef USE_DLFCN
584       nimf_im_set_cursor_location (m_im, &m_cursor_area);
585 #else
586       nimf_api->im_set_cursor_location (m_im, &m_cursor_area);
587 #endif
588     }
589   }
590 }
591 
592 void
invokeAction(QInputMethod::Action,int cursorPosition)593 NimfInputContext::invokeAction(QInputMethod::Action, int cursorPosition)
594 {
595 #ifndef USE_DLFCN
596   g_debug (G_STRLOC ": %s", G_STRFUNC);
597 #endif
598 }
599 
600 bool
filterEvent(const QEvent * event)601 NimfInputContext::filterEvent (const QEvent *event)
602 {
603 #ifndef USE_DLFCN
604   g_debug (G_STRLOC ": %s", G_STRFUNC);
605 #endif
606 
607   if (G_UNLIKELY (!qApp->focusObject() || !inputMethodAccepted()))
608     return false;
609 
610   gboolean         retval;
611   const QKeyEvent *key_event = static_cast<const QKeyEvent *>( event );
612   NimfEvent       *nimf_event;
613   NimfEventType    type = NIMF_EVENT_NOTHING;
614 
615   switch (event->type ())
616   {
617 #undef KeyPress
618     case QEvent::KeyPress:
619       type = NIMF_EVENT_KEY_PRESS;
620       break;
621 #undef KeyRelease
622     case QEvent::KeyRelease:
623       type = NIMF_EVENT_KEY_RELEASE;
624       break;
625     default:
626       return false;
627   }
628 
629 #ifndef USE_DLFCN
630   nimf_event = nimf_event_new (type);
631 #else
632   nimf_event = nimf_api->event_new (type);
633 #endif
634 
635   nimf_event->key.state            = key_event->nativeModifiers  ();
636   nimf_event->key.keyval           = key_event->nativeVirtualKey ();
637   nimf_event->key.hardware_keycode = key_event->nativeScanCode   ();
638 
639 #ifndef USE_DLFCN
640   retval = nimf_im_filter_event (m_im, nimf_event);
641   nimf_event_free (nimf_event);
642 #else
643   retval = nimf_api->im_filter_event (m_im, nimf_event);
644   nimf_api->event_free (nimf_event);
645 #endif
646 
647   return retval;
648 }
649 
650 QRectF
keyboardRect() const651 NimfInputContext::keyboardRect() const
652 {
653 #ifndef USE_DLFCN
654   g_debug (G_STRLOC ": %s", G_STRFUNC);
655 #endif
656   return QRectF ();
657 }
658 
659 bool
isAnimating() const660 NimfInputContext::isAnimating() const
661 {
662 #ifndef USE_DLFCN
663   g_debug (G_STRLOC ": %s", G_STRFUNC);
664 #endif
665   return false;
666 }
667 
668 void
showInputPanel()669 NimfInputContext::showInputPanel()
670 {
671 #ifndef USE_DLFCN
672   g_debug (G_STRLOC ": %s", G_STRFUNC);
673 #endif
674 }
675 
676 void
hideInputPanel()677 NimfInputContext::hideInputPanel()
678 {
679 #ifndef USE_DLFCN
680   g_debug (G_STRLOC ": %s", G_STRFUNC);
681 #endif
682 }
683 
684 bool
isInputPanelVisible() const685 NimfInputContext::isInputPanelVisible() const
686 {
687 #ifndef USE_DLFCN
688   g_debug (G_STRLOC ": %s", G_STRFUNC);
689 #endif
690   return false;
691 }
692 
693 QLocale
locale() const694 NimfInputContext::locale() const
695 {
696 #ifndef USE_DLFCN
697   g_debug (G_STRLOC ": %s", G_STRFUNC);
698 #endif
699   return QLocale ();
700 }
701 
702 Qt::LayoutDirection
inputDirection() const703 NimfInputContext::inputDirection() const
704 {
705 #ifndef USE_DLFCN
706   g_debug (G_STRLOC ": %s", G_STRFUNC);
707 #endif
708   return Qt::LayoutDirection ();
709 }
710 
711 void
setFocusObject(QObject * object)712 NimfInputContext::setFocusObject (QObject *object)
713 {
714 #ifndef USE_DLFCN
715   g_debug (G_STRLOC ": %s", G_STRFUNC);
716 #endif
717 
718   if (!object || !inputMethodAccepted())
719 #ifndef USE_DLFCN
720     nimf_im_focus_out (m_im);
721 #else
722     nimf_api->im_focus_out (m_im);
723 #endif
724 
725   QPlatformInputContext::setFocusObject (object);
726 
727   if (object && inputMethodAccepted())
728 #ifndef USE_DLFCN
729     nimf_im_focus_in (m_im);
730 #else
731     nimf_api->im_focus_in (m_im);
732 #endif
733 
734   update (Qt::ImCursorRectangle);
735 }
736 
737 /*
738  * class NimfInputContextPlugin
739  */
740 class NimfInputContextPlugin : public QPlatformInputContextPlugin
741 {
742   Q_OBJECT
743   Q_PLUGIN_METADATA(IID
744     QPlatformInputContextFactoryInterface_iid
745     FILE "./nimf.json")
746 
747 public:
NimfInputContextPlugin()748   NimfInputContextPlugin ()
749   {
750 #ifndef USE_DLFCN
751     g_debug (G_STRLOC ": %s", G_STRFUNC);
752 #endif
753 
754 #ifdef USE_DLFCN
755     libnimf    = dlopen ("libnimf.so.1",        RTLD_LAZY);
756     libglib    = dlopen ("libglib-2.0.so.0",    RTLD_LAZY);
757     libgobject = dlopen ("libgobject-2.0.so.0", RTLD_LAZY);
758     libgio     = dlopen ("libgio-2.0.so.0",     RTLD_LAZY);
759 
760     if (libnimf)
761     {
762       nimf_api = new NimfAPI;
763       nimf_api->im_new                 = reinterpret_cast<NimfIM* (*) ()> (dlsym (libnimf, "nimf_im_new"));
764       nimf_api->im_focus_in            = reinterpret_cast<void (*) (NimfIM*)> (dlsym (libnimf, "nimf_im_focus_in"));
765       nimf_api->im_focus_out           = reinterpret_cast<void (*) (NimfIM*)> (dlsym (libnimf, "nimf_im_focus_out"));
766       nimf_api->im_reset               = reinterpret_cast<void (*) (NimfIM*)> (dlsym (libnimf, "nimf_im_reset"));
767       nimf_api->im_filter_event        = reinterpret_cast<gboolean (*) (NimfIM*, NimfEvent*)> (dlsym (libnimf, "nimf_im_filter_event"));
768       nimf_api->im_get_preedit_string  = reinterpret_cast<void (*) (NimfIM*, gchar**, NimfPreeditAttr***, gint*)> (dlsym (libnimf, "nimf_im_get_preedit_string"));
769       nimf_api->im_set_cursor_location = reinterpret_cast<void (*) (NimfIM*, const NimfRectangle*)> (dlsym (libnimf, "nimf_im_set_cursor_location"));
770       nimf_api->im_set_use_preedit     = reinterpret_cast<void (*) (NimfIM*, gboolean)> (dlsym (libnimf, "nimf_im_set_use_preedit"));
771       nimf_api->im_set_surrounding     = reinterpret_cast<void (*) (NimfIM*, const char*, gint, gint)> (dlsym (libnimf, "nimf_im_set_surrounding"));
772       nimf_api->event_new              = reinterpret_cast<NimfEvent * (*) (NimfEventType)> (dlsym (libnimf, "nimf_event_new"));
773       nimf_api->event_free             = reinterpret_cast<void (*) (NimfEvent*)> (dlsym (libnimf, "nimf_event_free"));
774       nimf_api->preedit_attr_freev     = reinterpret_cast<void (*) (NimfPreeditAttr**)> (dlsym (libnimf, "nimf_preedit_attr_freev"));
775     }
776 
777     if (libglib)
778     {
779       glib_api = new GLibAPI;
780       glib_api->free = reinterpret_cast<void (*) (gpointer)> (dlsym (libglib, "g_free"));
781     }
782 
783     if (libgobject)
784     {
785       gobject_api = new GObjectAPI;
786       gobject_api->signal_connect_data = reinterpret_cast<gulong (*) (gpointer, const gchar*, GCallback, gpointer, GClosureNotify, GConnectFlags)> (dlsym (libgobject, "g_signal_connect_data"));
787       gobject_api->signal_emit_by_name = reinterpret_cast<void (*) (gpointer, const gchar*, ...)> (dlsym (libgobject, "g_signal_emit_by_name"));
788       gobject_api->object_unref        = reinterpret_cast<void (*) (gpointer)> (dlsym (libgobject, "g_object_unref"));
789     }
790 
791     if (libgio)
792     {
793       gio_api = new GIOAPI;
794       gio_api->settings_new          = reinterpret_cast<GSettings* (*) (const gchar*)> (dlsym (libgio, "g_settings_new"));
795       gio_api->settings_get_boolean  = reinterpret_cast<gboolean (*) (GSettings*, const gchar*)> (dlsym (libgio, "g_settings_get_boolean"));
796       gio_api->settings_schema_source_get_default
797                                      = reinterpret_cast<GSettingsSchemaSource* (*) ()> (dlsym (libgio, "g_settings_schema_source_get_default"));
798       gio_api->settings_schema_source_lookup
799                                      = reinterpret_cast<GSettingsSchema* (*) (GSettingsSchemaSource *, const gchar*, gboolean)> (dlsym (libgio, "g_settings_schema_source_lookup"));
800       gio_api->settings_schema_unref = reinterpret_cast<void (*) (GSettingsSchema*)> (dlsym (libgio, "g_settings_schema_unref"));
801     }
802 #endif
803   }
804 
~NimfInputContextPlugin()805   ~NimfInputContextPlugin ()
806   {
807 #ifndef USE_DLFCN
808     g_debug (G_STRLOC ": %s", G_STRFUNC);
809 #endif
810 
811 #ifdef USE_DLFCN
812     delete nimf_api;
813     delete glib_api;
814     delete gobject_api;
815     delete gio_api;
816 
817     nimf_api    = NULL;
818     glib_api    = NULL;
819     gobject_api = NULL;
820     gio_api     = NULL;
821 
822     if (libnimf)
823     {
824       dlclose (libnimf);
825       libnimf = NULL;
826     }
827 
828     if (libglib)
829     {
830       dlclose (libglib);
831       libglib = NULL;
832     }
833 
834     if (libgobject)
835     {
836       dlclose (libgobject);
837       libgobject = NULL;
838     }
839 
840     if (libgio)
841     {
842       dlclose (libgio);
843       libgio = NULL;
844     }
845 #endif
846   }
847 
keys() const848   virtual QStringList keys () const
849   {
850 #ifndef USE_DLFCN
851     g_debug (G_STRLOC ": %s", G_STRFUNC);
852 #endif
853 
854     return QStringList () <<  "nimf";
855   }
856 
create(const QString & key,const QStringList & paramList)857   virtual QPlatformInputContext *create (const QString     &key,
858                                          const QStringList &paramList)
859   {
860 #ifndef USE_DLFCN
861     g_debug (G_STRLOC ": %s", G_STRFUNC);
862 #endif
863 
864     return new NimfInputContext ();
865   }
866 };
867 
868 #include "im-nimf-qt5.moc"
869