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