SDL  2.0
SDL_fcitx.c
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2016 Sam Lantinga <slouken@libsdl.org>
4 
5  This software is provided 'as-is', without any express or implied
6  warranty. In no event will the authors be held liable for any damages
7  arising from the use of this software.
8 
9  Permission is granted to anyone to use this software for any purpose,
10  including commercial applications, and to alter it and redistribute it
11  freely, subject to the following restrictions:
12 
13  1. The origin of this software must not be misrepresented; you must not
14  claim that you wrote the original software. If you use this software
15  in a product, an acknowledgment in the product documentation would be
16  appreciated but is not required.
17  2. Altered source versions must be plainly marked as such, and must not be
18  misrepresented as being the original software.
19  3. This notice may not be removed or altered from any source distribution.
20 */
21 #include "../../SDL_internal.h"
22 
23 #ifdef HAVE_FCITX_FRONTEND_H
24 
25 #include <fcitx/frontend.h>
26 #include <unistd.h>
27 
28 #include "SDL_fcitx.h"
29 #include "SDL_keycode.h"
30 #include "SDL_keyboard.h"
31 #include "../../events/SDL_keyboard_c.h"
32 #include "SDL_dbus.h"
33 #include "SDL_syswm.h"
34 #if SDL_VIDEO_DRIVER_X11
35 # include "../../video/x11/SDL_x11video.h"
36 #endif
37 #include "SDL_hints.h"
38 
39 #define FCITX_DBUS_SERVICE "org.fcitx.Fcitx"
40 
41 #define FCITX_IM_DBUS_PATH "/inputmethod"
42 #define FCITX_IC_DBUS_PATH "/inputcontext_%d"
43 
44 #define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod"
45 #define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext"
46 
47 #define IC_NAME_MAX 64
48 #define DBUS_TIMEOUT 500
49 
50 typedef struct _FcitxClient
51 {
52  SDL_DBusContext *dbus;
53 
54  char servicename[IC_NAME_MAX];
55  char icname[IC_NAME_MAX];
56 
57  int id;
58 
59  SDL_Rect cursor_rect;
60 } FcitxClient;
61 
62 static FcitxClient fcitx_client;
63 
64 static int
65 GetDisplayNumber()
66 {
67  const char *display = SDL_getenv("DISPLAY");
68  const char *p = NULL;
69  int number = 0;
70 
71  if (display == NULL)
72  return 0;
73 
74  display = SDL_strchr(display, ':');
75  if (display == NULL)
76  return 0;
77 
78  display++;
79  p = SDL_strchr(display, '.');
80  if (p == NULL && display != NULL) {
81  number = SDL_strtod(display, NULL);
82  } else {
83  char *buffer = SDL_strdup(display);
84  buffer[p - display] = '\0';
85  number = SDL_strtod(buffer, NULL);
86  SDL_free(buffer);
87  }
88 
89  return number;
90 }
91 
92 static char*
93 GetAppName()
94 {
95 #if defined(__LINUX__) || defined(__FREEBSD__)
96  char *spot;
97  char procfile[1024];
98  char linkfile[1024];
99  int linksize;
100 
101 #if defined(__LINUX__)
102  SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
103 #elif defined(__FREEBSD__)
104  SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
105 #endif
106  linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
107  if (linksize > 0) {
108  linkfile[linksize] = '\0';
109  spot = SDL_strrchr(linkfile, '/');
110  if (spot) {
111  return SDL_strdup(spot + 1);
112  } else {
113  return SDL_strdup(linkfile);
114  }
115  }
116 #endif /* __LINUX__ || __FREEBSD__ */
117 
118  return SDL_strdup("SDL_App");
119 }
120 
121 /*
122  * Copied from fcitx source
123  */
124 #define CONT(i) ISUTF8_CB(in[i])
125 #define VAL(i, s) ((in[i]&0x3f) << s)
126 
127 static char *
128 _fcitx_utf8_get_char(const char *i, uint32_t *chr)
129 {
130  const unsigned char* in = (const unsigned char *)i;
131  if (!(in[0] & 0x80)) {
132  *(chr) = *(in);
133  return (char *)in + 1;
134  }
135 
136  /* 2-byte, 0x80-0x7ff */
137  if ((in[0] & 0xe0) == 0xc0 && CONT(1)) {
138  *chr = ((in[0] & 0x1f) << 6) | VAL(1, 0);
139  return (char *)in + 2;
140  }
141 
142  /* 3-byte, 0x800-0xffff */
143  if ((in[0] & 0xf0) == 0xe0 && CONT(1) && CONT(2)) {
144  *chr = ((in[0] & 0xf) << 12) | VAL(1, 6) | VAL(2, 0);
145  return (char *)in + 3;
146  }
147 
148  /* 4-byte, 0x10000-0x1FFFFF */
149  if ((in[0] & 0xf8) == 0xf0 && CONT(1) && CONT(2) && CONT(3)) {
150  *chr = ((in[0] & 0x7) << 18) | VAL(1, 12) | VAL(2, 6) | VAL(3, 0);
151  return (char *)in + 4;
152  }
153 
154  /* 5-byte, 0x200000-0x3FFFFFF */
155  if ((in[0] & 0xfc) == 0xf8 && CONT(1) && CONT(2) && CONT(3) && CONT(4)) {
156  *chr = ((in[0] & 0x3) << 24) | VAL(1, 18) | VAL(2, 12) | VAL(3, 6) | VAL(4, 0);
157  return (char *)in + 5;
158  }
159 
160  /* 6-byte, 0x400000-0x7FFFFFF */
161  if ((in[0] & 0xfe) == 0xfc && CONT(1) && CONT(2) && CONT(3) && CONT(4) && CONT(5)) {
162  *chr = ((in[0] & 0x1) << 30) | VAL(1, 24) | VAL(2, 18) | VAL(3, 12) | VAL(4, 6) | VAL(5, 0);
163  return (char *)in + 6;
164  }
165 
166  *chr = *in;
167 
168  return (char *)in + 1;
169 }
170 
171 static size_t
172 _fcitx_utf8_strlen(const char *s)
173 {
174  unsigned int l = 0;
175 
176  while (*s) {
177  uint32_t chr;
178 
179  s = _fcitx_utf8_get_char(s, &chr);
180  l++;
181  }
182 
183  return l;
184 }
185 
186 static DBusHandlerResult
187 DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
188 {
189  SDL_DBusContext *dbus = (SDL_DBusContext *)data;
190 
191  if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
192  DBusMessageIter iter;
193  const char *text = NULL;
194 
195  dbus->message_iter_init(msg, &iter);
196  dbus->message_iter_get_basic(&iter, &text);
197 
198  if (text)
199  SDL_SendKeyboardText(text);
200 
201  return DBUS_HANDLER_RESULT_HANDLED;
202  }
203 
204  if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdatePreedit")) {
205  DBusMessageIter iter;
206  const char *text;
207 
208  dbus->message_iter_init(msg, &iter);
209  dbus->message_iter_get_basic(&iter, &text);
210 
211  if (text && *text) {
213  size_t text_bytes = SDL_strlen(text), i = 0;
214  size_t cursor = 0;
215 
216  while (i < text_bytes) {
217  size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
218  size_t chars = _fcitx_utf8_strlen(buf);
219 
220  SDL_SendEditingText(buf, cursor, chars);
221 
222  i += sz;
223  cursor += chars;
224  }
225  }
226 
228  return DBUS_HANDLER_RESULT_HANDLED;
229  }
230 
231  return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
232 }
233 
234 static DBusMessage*
235 FcitxClientICNewMethod(FcitxClient *client,
236  const char *method)
237 {
238  SDL_DBusContext *dbus = client->dbus;
239  return dbus->message_new_method_call(
240  client->servicename,
241  client->icname,
242  FCITX_IC_DBUS_INTERFACE,
243  method);
244 }
245 
246 static void
247 FcitxClientICCallMethod(FcitxClient *client,
248  const char *method)
249 {
250  SDL_DBusContext *dbus = client->dbus;
251  DBusMessage *msg = FcitxClientICNewMethod(client, method);
252 
253  if (msg == NULL)
254  return ;
255 
256  if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
257  dbus->connection_flush(dbus->session_conn);
258  }
259 
260  dbus->message_unref(msg);
261 }
262 
263 static void
264 Fcitx_SetCapabilities(void *data,
265  const char *name,
266  const char *old_val,
267  const char *internal_editing)
268 {
269  FcitxClient *client = (FcitxClient *)data;
270  SDL_DBusContext *dbus = client->dbus;
271  Uint32 caps = CAPACITY_NONE;
272 
273  DBusMessage *msg = FcitxClientICNewMethod(client, "SetCapacity");
274  if (msg == NULL)
275  return ;
276 
277  if (!(internal_editing && *internal_editing == '1')) {
278  caps |= CAPACITY_PREEDIT;
279  }
280 
281  dbus->message_append_args(msg,
282  DBUS_TYPE_UINT32, &caps,
283  DBUS_TYPE_INVALID);
284  if (dbus->connection_send(dbus->session_conn, msg, NULL)) {
285  dbus->connection_flush(dbus->session_conn);
286  }
287 
288  dbus->message_unref(msg);
289 }
290 
291 static void
292 FcitxClientCreateIC(FcitxClient *client)
293 {
294  char *appname = NULL;
295  pid_t pid = 0;
296  int id = 0;
298  Uint32 arg1, arg2, arg3, arg4;
299 
300  SDL_DBusContext *dbus = client->dbus;
301  DBusMessage *reply = NULL;
302  DBusMessage *msg = dbus->message_new_method_call(
303  client->servicename,
304  FCITX_IM_DBUS_PATH,
305  FCITX_IM_DBUS_INTERFACE,
306  "CreateICv3"
307  );
308 
309  if (msg == NULL)
310  return ;
311 
312  appname = GetAppName();
313  pid = getpid();
314  dbus->message_append_args(msg,
315  DBUS_TYPE_STRING, &appname,
316  DBUS_TYPE_INT32, &pid,
317  DBUS_TYPE_INVALID);
318 
319  do {
320  reply = dbus->connection_send_with_reply_and_block(
321  dbus->session_conn,
322  msg,
323  DBUS_TIMEOUT,
324  NULL);
325 
326  if (!reply)
327  break;
328  if (!dbus->message_get_args(reply, NULL,
329  DBUS_TYPE_INT32, &id,
330  DBUS_TYPE_BOOLEAN, &enable,
331  DBUS_TYPE_UINT32, &arg1,
332  DBUS_TYPE_UINT32, &arg2,
333  DBUS_TYPE_UINT32, &arg3,
334  DBUS_TYPE_UINT32, &arg4,
335  DBUS_TYPE_INVALID))
336  break;
337 
338  if (id < 0)
339  break;
340  client->id = id;
341 
342  SDL_snprintf(client->icname, IC_NAME_MAX,
343  FCITX_IC_DBUS_PATH, client->id);
344 
345  dbus->bus_add_match(dbus->session_conn,
346  "type='signal', interface='org.fcitx.Fcitx.InputContext'",
347  NULL);
348  dbus->connection_add_filter(dbus->session_conn,
349  &DBus_MessageFilter, dbus,
350  NULL);
351  dbus->connection_flush(dbus->session_conn);
352 
353  SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, &Fcitx_SetCapabilities, client);
354  }
355  while (0);
356 
357  if (reply)
358  dbus->message_unref(reply);
359  dbus->message_unref(msg);
360  SDL_free(appname);
361 }
362 
363 static Uint32
364 Fcitx_ModState(void)
365 {
366  Uint32 fcitx_mods = 0;
367  SDL_Keymod sdl_mods = SDL_GetModState();
368 
369  if (sdl_mods & KMOD_SHIFT) fcitx_mods |= FcitxKeyState_Shift;
370  if (sdl_mods & KMOD_CAPS) fcitx_mods |= FcitxKeyState_CapsLock;
371  if (sdl_mods & KMOD_CTRL) fcitx_mods |= FcitxKeyState_Ctrl;
372  if (sdl_mods & KMOD_ALT) fcitx_mods |= FcitxKeyState_Alt;
373  if (sdl_mods & KMOD_NUM) fcitx_mods |= FcitxKeyState_NumLock;
374  if (sdl_mods & KMOD_LGUI) fcitx_mods |= FcitxKeyState_Super;
375  if (sdl_mods & KMOD_RGUI) fcitx_mods |= FcitxKeyState_Meta;
376 
377  return fcitx_mods;
378 }
379 
380 SDL_bool
382 {
383  fcitx_client.dbus = SDL_DBus_GetContext();
384 
385  fcitx_client.cursor_rect.x = -1;
386  fcitx_client.cursor_rect.y = -1;
387  fcitx_client.cursor_rect.w = 0;
388  fcitx_client.cursor_rect.h = 0;
389 
390  SDL_snprintf(fcitx_client.servicename, IC_NAME_MAX,
391  "%s-%d",
392  FCITX_DBUS_SERVICE, GetDisplayNumber());
393 
394  FcitxClientCreateIC(&fcitx_client);
395 
396  return SDL_TRUE;
397 }
398 
399 void
401 {
402  FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
403 }
404 
405 void
407 {
408  if (focused) {
409  FcitxClientICCallMethod(&fcitx_client, "FocusIn");
410  } else {
411  FcitxClientICCallMethod(&fcitx_client, "FocusOut");
412  }
413 }
414 
415 void
416 SDL_Fcitx_Reset(void)
417 {
418  FcitxClientICCallMethod(&fcitx_client, "Reset");
419  FcitxClientICCallMethod(&fcitx_client, "CloseIC");
420 }
421 
422 SDL_bool
423 SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
424 {
425  DBusMessage *msg = NULL;
426  DBusMessage *reply = NULL;
427  SDL_DBusContext *dbus = fcitx_client.dbus;
428 
429  Uint32 state = 0;
430  SDL_bool handled = SDL_FALSE;
431  int type = FCITX_PRESS_KEY;
432  Uint32 event_time = 0;
433 
434  msg = FcitxClientICNewMethod(&fcitx_client, "ProcessKeyEvent");
435  if (msg == NULL)
436  return SDL_FALSE;
437 
438  state = Fcitx_ModState();
439  dbus->message_append_args(msg,
440  DBUS_TYPE_UINT32, &keysym,
441  DBUS_TYPE_UINT32, &keycode,
442  DBUS_TYPE_UINT32, &state,
443  DBUS_TYPE_INT32, &type,
444  DBUS_TYPE_UINT32, &event_time,
445  DBUS_TYPE_INVALID);
446 
447  reply = dbus->connection_send_with_reply_and_block(dbus->session_conn,
448  msg,
449  -1,
450  NULL);
451 
452  if (reply) {
453  dbus->message_get_args(reply,
454  NULL,
455  DBUS_TYPE_INT32, &handled,
456  DBUS_TYPE_INVALID);
457 
458  dbus->message_unref(reply);
459  }
460 
461  if (handled) {
463  }
464 
465  return handled;
466 }
467 
468 void
470 {
471  SDL_Window *focused_win = NULL;
472  SDL_SysWMinfo info;
473  int x = 0, y = 0;
474  SDL_Rect *cursor = &fcitx_client.cursor_rect;
475 
476  SDL_DBusContext *dbus = fcitx_client.dbus;
477  DBusMessage *msg = NULL;
478  DBusConnection *conn;
479 
480  if (rect) {
481  SDL_memcpy(cursor, rect, sizeof(SDL_Rect));
482  }
483 
484  focused_win = SDL_GetKeyboardFocus();
485  if (!focused_win) {
486  return ;
487  }
488 
489  SDL_VERSION(&info.version);
490  if (!SDL_GetWindowWMInfo(focused_win, &info)) {
491  return;
492  }
493 
494  SDL_GetWindowPosition(focused_win, &x, &y);
495 
496 #if SDL_VIDEO_DRIVER_X11
497  if (info.subsystem == SDL_SYSWM_X11) {
498  SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
499 
500  Display *x_disp = info.info.x11.display;
501  Window x_win = info.info.x11.window;
502  int x_screen = displaydata->screen;
503  Window unused;
504  X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
505  }
506 #endif
507 
508  if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
509  // move to bottom left
510  int w = 0, h = 0;
511  SDL_GetWindowSize(focused_win, &w, &h);
512  cursor->x = 0;
513  cursor->y = h;
514  }
515 
516  x += cursor->x;
517  y += cursor->y;
518 
519  msg = FcitxClientICNewMethod(&fcitx_client, "SetCursorRect");
520  if (msg == NULL)
521  return ;
522 
523  dbus->message_append_args(msg,
524  DBUS_TYPE_INT32, &x,
525  DBUS_TYPE_INT32, &y,
526  DBUS_TYPE_INT32, &cursor->w,
527  DBUS_TYPE_INT32, &cursor->h,
528  DBUS_TYPE_INVALID);
529 
530  conn = dbus->session_conn;
531  if (dbus->connection_send(conn, msg, NULL))
532  dbus->connection_flush(conn);
533 
534  dbus->message_unref(msg);
535 }
536 
537 void
539 {
540  SDL_DBusContext *dbus = fcitx_client.dbus;
541  DBusConnection *conn = dbus->session_conn;
542 
543  dbus->connection_read_write(conn, 0);
544 
545  while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
546  /* Do nothing, actual work happens in DBus_MessageFilter */
547  usleep(10);
548  }
549 }
550 
551 #endif /* HAVE_FCITX_FRONTEND_H */
552 
553 /* vi: set ts=4 sw=4 expandtab: */
GLuint id
void SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
GLdouble s
Definition: SDL_opengl.h:2056
GLuint GLuint GLuint GLuint arg1
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1567
SDL_Rect rect
Definition: testrelative.c:27
#define KMOD_CTRL
Definition: SDL_keycode.h:334
struct xkb_state * state
GLfloat GLfloat p
#define SDL_utf8strlcpy
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1967
SDL_version version
Definition: SDL_syswm.h:195
GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg2
#define KMOD_ALT
Definition: SDL_keycode.h:336
SDL_SYSWM_TYPE subsystem
Definition: SDL_syswm.h:196
GLuint const GLchar * name
#define SDL_GetKeyboardFocus
uint32_t Uint32
An unsigned 32-bit integer type.
Definition: SDL_stdinc.h:161
#define SDL_strchr
SDL_bool SDL_Fcitx_Init(void)
#define SDL_VERSION(x)
Macro to determine SDL version program was compiled against.
Definition: SDL_version.h:79
#define SDL_GetWindowSize
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1564
#define SDL_memcpy
void SDL_Fcitx_PumpEvents()
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1567
#define KMOD_SHIFT
Definition: SDL_keycode.h:335
void SDL_Fcitx_Reset(void)
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:774
void SDL_free(void *mem)
#define SDL_GetWindowPosition
int x
Definition: SDL_rect.h:66
GLenum GLuint GLenum GLsizei const GLchar * buf
int w
Definition: SDL_rect.h:67
struct SDL_SysWMinfo::@18::@19 x11
#define SDL_HINT_IME_INTERNAL_EDITING
A variable to control whether certain IMEs should handle text editing internally instead of sending S...
Definition: SDL_hints.h:623
SDL_Keymod
Enumeration of valid key mods (possibly OR&#39;d together).
Definition: SDL_keycode.h:317
SDL_Cursor * cursor
#define SDL_getenv
#define SDL_strtod
return Display return Display Bool Bool int int int return Display XEvent Bool(*) XPointer return Display return Display Drawable _Xconst char unsigned int unsigned int return Display Pixmap Pixmap XColor XColor unsigned int unsigned int return Display _Xconst char char int char return Display Visual unsigned int int int char unsigned int unsigned int in i)
Definition: SDL_x11sym.h:50
#define SDL_GetWindowWMInfo
#define NULL
Definition: begin_code.h:143
void SDL_Fcitx_Quit(void)
SDL_bool
Definition: SDL_stdinc.h:130
unsigned int uint32_t
GLboolean enable
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1058
#define SDL_strlen
int h
Definition: SDL_rect.h:67
#define SDL_strdup
The type used to identify a window.
Definition: SDL_sysvideo.h:71
GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg3
#define SDL_AddHintCallback
GLuint buffer
void SDL_Fcitx_SetFocus(SDL_bool focused)
#define SDL_snprintf
union SDL_SysWMinfo::@18 info
GLubyte GLubyte GLubyte GLubyte w
GLuint in
#define SDL_strrchr
#define SDL_GetModState
int y
Definition: SDL_rect.h:66
GLfloat GLfloat GLfloat GLfloat h
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:797
A rectangle, with the origin at the upper left.
Definition: SDL_rect.h:64
#define SDL_TEXTEDITINGEVENT_TEXT_SIZE
Definition: SDL_events.h:202