1 #include <QKeyEvent>
2 #include <QGuiApplication>
3 #include <QInputMethod>
4 #include <QTextCharFormat>
5 #include <QPalette>
6 #include <QWindow>
7 
8 #include <unistd.h>
9 #include <errno.h>
10 #include <signal.h>
11 #if 0
12 #include <X11/Xlib.h>
13 #include <X11/keysym.h>
14 #include <X11/Xutil.h>
15 #else
16 // confliction of qt & x11
17 typedef unsigned int KeySym;
18 struct Display;
19 typedef unsigned int Window;
20 typedef struct {
21     short x, y;
22 } XPoint;
23 #endif
24 #include "../util.h"
25 #include "gcin-im-client.h"
26 #include "qgcinplatforminputcontext.h"
27 
28 static WId focused_win;
29 
30 #include <qpa/qplatformnativeinterface.h>
31 
32 #if DEBUG
33 FILE *out_fp;
__gcin_dbg_(const char * fmt,...)34 void __gcin_dbg_(const char *fmt,...)
35 {
36   va_list args;
37 
38   if (!out_fp) {
39 #if 0
40 	  out_fp = fopen("/tmp/a.txt", "w");
41 #else
42 	out_fp = stdout;
43 #endif
44 }
45 
46   va_start(args, fmt);
47   vfprintf(out_fp, fmt, args);
48   fflush(out_fp);
49   va_end(args);
50 }
51 #endif
52 
QGcinPlatformInputContext()53 QGcinPlatformInputContext::QGcinPlatformInputContext()
54 {
55 	dbg("QGcinPlatformInputContext::QGcinPlatformInputContext() \n");
56    QPlatformNativeInterface *native = QGuiApplication::platformNativeInterface();
57     if(!native)
58         return;
59     Display *display = static_cast<Display *>(native->nativeResourceForWindow("display", NULL));
60 
61   if (!(gcin_ch = gcin_im_client_open(display))) {
62     perror("cannot open gcin_ch");
63     dbg("gcin_im_client_open error\n");
64     return;
65   }
66 
67   dbg("QGcinPlatformInputContext succ\n");
68 }
69 
~QGcinPlatformInputContext()70 QGcinPlatformInputContext::~QGcinPlatformInputContext()
71 {
72 	if (gcin_ch==NULL)
73 		return;
74 	gcin_im_client_close(gcin_ch);
75 	gcin_ch = NULL;
76 }
77 
78 
isValid() const79 bool QGcinPlatformInputContext::isValid() const
80 {
81 	dbg("QGcinPlatformInputContext::isValid()\n");
82     return true;
83 }
84 
invokeAction(QInputMethod::Action action,int cursorPosition)85 void QGcinPlatformInputContext::invokeAction(QInputMethod::Action action, int cursorPosition)
86 {
87 	dbg("QGcinPlatformInputContext::invokeAction(n");
88 #if 0
89     if (action == QInputMethod::Click
90         && (cursorPosition <= 0 || cursorPosition >= m_preedit.length())
91     )
92     {
93         // qDebug() << action << cursorPosition;
94         commitPreedit();
95     }
96 #endif
97 }
98 
commitPreedit()99 void QGcinPlatformInputContext::commitPreedit()
100 {
101   dbg("QGcinPlatformInputContext::commitPreedit\n");
102   // use this to flush
103   int preedit_cursor_position=0;
104   int sub_comp_len;
105   char *str=NULL;
106   GCIN_PREEDIT_ATTR att[GCIN_PREEDIT_ATTR_MAX_N];
107   gcin_im_client_get_preedit(gcin_ch, &str, att, &preedit_cursor_position, &sub_comp_len);
108   if (str) {
109 	  if (strlen(str) > 0) {
110 		  dbg("send enter to flush\n");
111 		  send_key_press(0xff0d, 0); // Enter
112       } else
113 		dbg("empty string\n");
114 	  free(str);
115 	 update_preedit();
116   }  else
117 	dbg("no str\n");
118 }
119 
120 
reset()121 void QGcinPlatformInputContext::reset()
122 {
123 	dbg("QGcinPlatformInputContext::reset()\n");
124  if (gcin_ch) {
125     gcin_im_client_reset(gcin_ch);
126     update_preedit();
127 }
128 }
129 
update(Qt::InputMethodQueries queries)130 void QGcinPlatformInputContext::update(Qt::InputMethodQueries queries )
131 {
132    dbg("QGcinPlatformInputContext::update\n");
133     QObject *input = qApp->focusObject();
134     if (!input)
135         return;
136 
137     QInputMethodQueryEvent query(queries);
138     QGuiApplication::sendEvent(input, &query);
139 
140     if (queries & Qt::ImCursorRectangle) {
141         cursorMoved();
142     }
143 
144 #if 0
145     if (queries & Qt::ImHints) {
146         Qt::InputMethodHints hints = Qt::InputMethodHints(query.value(Qt::ImHints).toUInt());
147 
148        if (hints & Qt::ImhPreferNumbers)
149     }
150 #endif
151 }
152 
153 // this one is essential
commit()154 void QGcinPlatformInputContext::commit()
155 {
156 	dbg("QGcinPlatformInputContext::commit()\n");
157 	commitPreedit();
158 //    QPlatformInputContext::commit();
159 }
160 
161 
setFocusObject(QObject * object)162 void QGcinPlatformInputContext::setFocusObject(QObject* object)
163 {
164 	dbg("QGcinPlatformInputContext::setFocusObject\n");
165     QWindow *window = qApp->focusWindow();
166     if (!window) {
167 		dbg("no window, focus out\n");
168 		focused_win = 0;
169 		char *rstr = NULL;
170 		gcin_im_client_focus_out2(gcin_ch, &rstr);
171 		if (rstr) {
172 			send_str(rstr);
173 		} else
174 			dbg("no str in preedit\n");
175 		return;
176 	}
177 
178      WId win = window->winId();
179 
180      if (focused_win && win != focused_win) {
181 		if (gcin_ch)
182 			gcin_im_client_focus_out(gcin_ch);
183 	}
184 
185 	focused_win = win;
186 
187 	if (gcin_ch) {
188 		gcin_im_client_set_window(gcin_ch, win);
189 		gcin_im_client_focus_in(gcin_ch);
190 	}
191 }
192 
193 static int last_x=-1, last_y=-1;
cursorMoved()194 void QGcinPlatformInputContext::cursorMoved()
195 {
196 	dbg(" QGcinPlatformInputContext::cursorMoved()\n");
197 
198     QWindow *inputWindow = qApp->focusWindow();
199     if (!inputWindow)
200         return;
201 
202     QRect r = qApp->inputMethod()->cursorRectangle().toRect();
203     if(!r.isValid())
204         return;
205 #if 0
206     r.moveTopLeft(inputWindow->mapToGlobal(r.topLeft()));
207 #endif
208 
209    // gcin server will clear the string if the cursor is moved, make sure the x,y is valid
210 	int x = r.left(),  y = r.bottom();
211 	if (x > inputWindow->width() || y > inputWindow->height() || x < 0 || y < 0)
212 		return;
213 
214   if (gcin_ch && (x !=  last_x || y != last_y)   ) {
215 	 last_x = x; last_y = y;
216 	 dbg("move cursor %d, %d\n", x, y);
217     gcin_im_client_set_cursor_location(gcin_ch, x,  y);
218   }
219 }
220 
221 
222 
update_preedit()223 void QGcinPlatformInputContext::update_preedit()
224 {
225   if (!gcin_ch)
226     return;
227   QList<QInputMethodEvent::Attribute> attrList;
228 //  QString preedit_string;
229   int preedit_cursor_position=0;
230   int sub_comp_len;
231   char *str=NULL;
232   GCIN_PREEDIT_ATTR att[GCIN_PREEDIT_ATTR_MAX_N];
233   int attN = gcin_im_client_get_preedit(gcin_ch, &str, att, &preedit_cursor_position, &sub_comp_len);
234 
235   int ret;
236   gcin_im_client_set_flags(gcin_ch, FLAG_GCIN_client_handle_use_preedit, &ret);
237 
238   QObject *input = qApp->focusObject();
239 
240   if (!input || !str) {
241     free(str);
242     return;
243   }
244 
245 
246 #if DBG || 0
247   dbg"update_preedit attN:%d '%s'\n", attN, str);
248 #endif
249   int i;
250   for(i=0; i < attN; i++) {
251     int ofs0 = att[i].ofs0;
252     int len = att[i].ofs1 - att[i].ofs0;
253     QTextCharFormat format;
254 
255     switch (att[i].flag) {
256       case GCIN_PREEDIT_ATTR_FLAG_REVERSE:
257           {
258             QBrush brush;
259             QPalette palette;
260             palette = QGuiApplication::palette();
261             format.setBackground(QBrush(QColor(palette.color(QPalette::Active, QPalette::Highlight))));
262             format.setForeground(QBrush(QColor(palette.color(QPalette::Active, QPalette::HighlightedText))));
263           }
264           break;
265       case GCIN_PREEDIT_ATTR_FLAG_UNDERLINE:
266           {
267               format.setUnderlineStyle(QTextCharFormat::DashUnderline);
268           }
269     }
270 
271 	attrList.append(QInputMethodEvent::Attribute(QInputMethodEvent::TextFormat, ofs0,  len, format));
272   }
273 
274   attrList.append(QInputMethodEvent::Attribute(QInputMethodEvent::Cursor,  preedit_cursor_position, 1, 0));
275 
276   QInputMethodEvent im_event (QString::fromUtf8(str), attrList);
277   send_event (im_event);
278   free(str);
279 }
280 
send_event(QInputMethodEvent e)281 void QGcinPlatformInputContext::send_event(QInputMethodEvent e) {
282     QObject *input = qApp->focusObject();
283     if (!input)
284         return;
285     QCoreApplication::sendEvent(input, &e);
286 }
287 
send_str(char * rstr)288 void QGcinPlatformInputContext::send_str(char *rstr) {
289 	dbg("send_str %s\n",  rstr);
290 	  QString inputText = QString::fromUtf8(rstr);
291 	  free(rstr);
292 	  QInputMethodEvent commit_event;
293 	  commit_event.setCommitString (inputText);
294 	  send_event (commit_event);
295 }
296 
send_key_press(quint32 keysym,quint32 state)297 bool QGcinPlatformInputContext::send_key_press(quint32 keysym, quint32 state) {
298 	dbg("send_key_press\n");
299 		char *rstr  = NULL;
300 		int result = gcin_im_client_forward_key_press(gcin_ch, keysym, state, &rstr);
301 
302 		  if (rstr) {
303 			  send_str(rstr);
304 		  }
305 
306 	return result;
307 }
308 
filterEvent(const QEvent * event)309 bool QGcinPlatformInputContext::filterEvent(const QEvent* event)
310 {
311 	dbg("QGcinPlatformInputContext::filterEvent\n");
312         if (event->type() != QEvent::KeyPress && event->type() != QEvent::KeyRelease)
313            goto ret;
314 
315         const QKeyEvent* keyEvent;
316         keyEvent = static_cast<const QKeyEvent*>(event);
317         quint32 keysym ;
318         keysym = keyEvent->nativeVirtualKey();
319 //        quint32 keycode = keyEvent->nativeScanCode();
320         quint32  state;
321         state = keyEvent->nativeModifiers();
322 
323         if (!inputMethodAccepted())
324             goto ret;
325 
326         QObject *input;
327         input = qApp->focusObject();
328 
329         if (!input)
330 			goto ret;
331 
332 		int result;
333 #if 1
334 		if (event->type() == QEvent::KeyPress) {
335 			if (send_key_press(keysym, state)) {
336 				update_preedit();
337 				return true;
338 			}
339 		} else {
340 			char *rstr = NULL;
341 			 result = gcin_im_client_forward_key_release(gcin_ch,   keysym, state, &rstr);
342 			if (rstr)
343 				free(rstr);
344 
345 			if (result)
346 				return true;
347 		}
348 #endif
349 
350 ret:
351     return QPlatformInputContext::filterEvent(event);
352 }
353