SDL 2.0
SDL_fcitx.c
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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
50typedef 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
62static FcitxClient fcitx_client;
63
64static int
65GetDisplayNumber()
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);
87 }
88
89 return number;
90}
91
92static char*
93GetAppName()
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
121static DBusHandlerResult
122DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
123{
124 SDL_DBusContext *dbus = (SDL_DBusContext *)data;
125
126 if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
127 DBusMessageIter iter;
128 const char *text = NULL;
129
130 dbus->message_iter_init(msg, &iter);
131 dbus->message_iter_get_basic(&iter, &text);
132
133 if (text)
135
136 return DBUS_HANDLER_RESULT_HANDLED;
137 }
138
139 if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdatePreedit")) {
140 DBusMessageIter iter;
141 const char *text;
142
143 dbus->message_iter_init(msg, &iter);
144 dbus->message_iter_get_basic(&iter, &text);
145
146 if (text && *text) {
148 size_t text_bytes = SDL_strlen(text), i = 0;
149 size_t cursor = 0;
150
151 while (i < text_bytes) {
152 const size_t sz = SDL_utf8strlcpy(buf, text + i, sizeof(buf));
153 const size_t chars = SDL_utf8strlen(buf);
154
156
157 i += sz;
158 cursor += chars;
159 }
160 }
161
163 return DBUS_HANDLER_RESULT_HANDLED;
164 }
165
166 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
167}
168
169static void
170FcitxClientICCallMethod(FcitxClient *client, const char *method)
171{
172 SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
173}
174
175static void SDLCALL
176Fcitx_SetCapabilities(void *data,
177 const char *name,
178 const char *old_val,
179 const char *internal_editing)
180{
181 FcitxClient *client = (FcitxClient *)data;
182 Uint32 caps = CAPACITY_NONE;
183
184 if (!(internal_editing && *internal_editing == '1')) {
185 caps |= CAPACITY_PREEDIT;
186 }
187
188 SDL_DBus_CallVoidMethod(client->servicename, client->icname, FCITX_IC_DBUS_INTERFACE, "SetCapacity", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
189}
190
191static SDL_bool
192FcitxClientCreateIC(FcitxClient *client)
193{
194 char *appname = GetAppName();
195 pid_t pid = getpid();
196 int id = -1;
197 Uint32 enable, arg1, arg2, arg3, arg4;
198
199 if (!SDL_DBus_CallMethod(client->servicename, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateICv3",
200 DBUS_TYPE_STRING, &appname, DBUS_TYPE_INT32, &pid, DBUS_TYPE_INVALID,
201 DBUS_TYPE_INT32, &id, DBUS_TYPE_BOOLEAN, &enable, DBUS_TYPE_UINT32, &arg1, DBUS_TYPE_UINT32, &arg2, DBUS_TYPE_UINT32, &arg3, DBUS_TYPE_UINT32, &arg4, DBUS_TYPE_INVALID)) {
202 id = -1; /* just in case. */
203 }
204
205 SDL_free(appname);
206
207 if (id >= 0) {
208 SDL_DBusContext *dbus = client->dbus;
209
210 client->id = id;
211
212 SDL_snprintf(client->icname, IC_NAME_MAX, FCITX_IC_DBUS_PATH, client->id);
213
214 dbus->bus_add_match(dbus->session_conn,
215 "type='signal', interface='org.fcitx.Fcitx.InputContext'",
216 NULL);
217 dbus->connection_add_filter(dbus->session_conn,
218 &DBus_MessageFilter, dbus,
219 NULL);
220 dbus->connection_flush(dbus->session_conn);
221
222 SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, Fcitx_SetCapabilities, client);
223 return SDL_TRUE;
224 }
225
226 return SDL_FALSE;
227}
228
229static Uint32
230Fcitx_ModState(void)
231{
232 Uint32 fcitx_mods = 0;
233 SDL_Keymod sdl_mods = SDL_GetModState();
234
235 if (sdl_mods & KMOD_SHIFT) fcitx_mods |= FcitxKeyState_Shift;
236 if (sdl_mods & KMOD_CAPS) fcitx_mods |= FcitxKeyState_CapsLock;
237 if (sdl_mods & KMOD_CTRL) fcitx_mods |= FcitxKeyState_Ctrl;
238 if (sdl_mods & KMOD_ALT) fcitx_mods |= FcitxKeyState_Alt;
239 if (sdl_mods & KMOD_NUM) fcitx_mods |= FcitxKeyState_NumLock;
240 if (sdl_mods & KMOD_LGUI) fcitx_mods |= FcitxKeyState_Super;
241 if (sdl_mods & KMOD_RGUI) fcitx_mods |= FcitxKeyState_Meta;
242
243 return fcitx_mods;
244}
245
248{
249 fcitx_client.dbus = SDL_DBus_GetContext();
250
251 fcitx_client.cursor_rect.x = -1;
252 fcitx_client.cursor_rect.y = -1;
253 fcitx_client.cursor_rect.w = 0;
254 fcitx_client.cursor_rect.h = 0;
255
256 SDL_snprintf(fcitx_client.servicename, IC_NAME_MAX,
257 "%s-%d",
258 FCITX_DBUS_SERVICE, GetDisplayNumber());
259
260 return FcitxClientCreateIC(&fcitx_client);
261}
262
263void
265{
266 FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
267}
268
269void
271{
272 if (focused) {
273 FcitxClientICCallMethod(&fcitx_client, "FocusIn");
274 } else {
275 FcitxClientICCallMethod(&fcitx_client, "FocusOut");
276 }
277}
278
279void
280SDL_Fcitx_Reset(void)
281{
282 FcitxClientICCallMethod(&fcitx_client, "Reset");
283 FcitxClientICCallMethod(&fcitx_client, "CloseIC");
284}
285
288{
289 Uint32 state = Fcitx_ModState();
290 Uint32 handled = SDL_FALSE;
291 int type = FCITX_PRESS_KEY;
292 Uint32 event_time = 0;
293
294 if (SDL_DBus_CallMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
295 DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &state, DBUS_TYPE_INT32, &type, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
296 DBUS_TYPE_INT32, &handled, DBUS_TYPE_INVALID)) {
297 if (handled) {
299 return SDL_TRUE;
300 }
301 }
302
303 return SDL_FALSE;
304}
305
306void
308{
309 SDL_Window *focused_win = NULL;
310 SDL_SysWMinfo info;
311 int x = 0, y = 0;
312 SDL_Rect *cursor = &fcitx_client.cursor_rect;
313
314 if (rect) {
315 SDL_memcpy(cursor, rect, sizeof(SDL_Rect));
316 }
317
318 focused_win = SDL_GetKeyboardFocus();
319 if (!focused_win) {
320 return ;
321 }
322
323 SDL_VERSION(&info.version);
324 if (!SDL_GetWindowWMInfo(focused_win, &info)) {
325 return;
326 }
327
328 SDL_GetWindowPosition(focused_win, &x, &y);
329
330#if SDL_VIDEO_DRIVER_X11
331 if (info.subsystem == SDL_SYSWM_X11) {
332 SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata;
333
334 Display *x_disp = info.info.x11.display;
335 Window x_win = info.info.x11.window;
336 int x_screen = displaydata->screen;
337 Window unused;
338 X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
339 }
340#endif
341
342 if (cursor->x == -1 && cursor->y == -1 && cursor->w == 0 && cursor->h == 0) {
343 /* move to bottom left */
344 int w = 0, h = 0;
345 SDL_GetWindowSize(focused_win, &w, &h);
346 cursor->x = 0;
347 cursor->y = h;
348 }
349
350 x += cursor->x;
351 y += cursor->y;
352
353 SDL_DBus_CallVoidMethod(fcitx_client.servicename, fcitx_client.icname, FCITX_IC_DBUS_INTERFACE, "SetCursorRect",
354 DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &cursor->w, DBUS_TYPE_INT32, &cursor->h, DBUS_TYPE_INVALID);
355}
356
357void
359{
360 SDL_DBusContext *dbus = fcitx_client.dbus;
361 DBusConnection *conn = dbus->session_conn;
362
363 dbus->connection_read_write(conn, 0);
364
365 while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
366 /* Do nothing, actual work happens in DBus_MessageFilter */
367 usleep(10);
368 }
369}
370
371#endif /* HAVE_FCITX_FRONTEND_H */
372
373/* vi: set ts=4 sw=4 expandtab: */
#define SDL_strchr
#define SDL_GetWindowSize
#define SDL_strrchr
#define SDL_utf8strlcpy
#define SDL_GetKeyboardFocus
#define SDL_getenv
#define SDL_strlen
#define SDL_free
#define SDL_strdup
#define SDL_utf8strlen
#define SDL_strtod
#define SDL_GetModState
#define SDL_GetWindowPosition
#define SDL_memcpy
#define SDL_AddHintCallback
#define SDL_snprintf
#define SDL_GetWindowWMInfo
#define SDL_TEXTEDITINGEVENT_TEXT_SIZE
Definition: SDL_events.h:223
void SDL_Fcitx_SetFocus(SDL_bool focused)
void SDL_Fcitx_PumpEvents(void)
void SDL_Fcitx_Quit(void)
SDL_bool SDL_Fcitx_Init(void)
void SDL_Fcitx_Reset(void)
void SDL_Fcitx_UpdateTextRect(SDL_Rect *rect)
SDL_bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode)
#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:857
#define SDLCALL
Definition: SDL_internal.h:49
int SDL_SendEditingText(const char *text, int start, int length)
Definition: SDL_keyboard.c:812
int SDL_SendKeyboardText(const char *text)
Definition: SDL_keyboard.c:789
#define KMOD_CTRL
Definition: SDL_keycode.h:342
#define KMOD_SHIFT
Definition: SDL_keycode.h:343
#define KMOD_ALT
Definition: SDL_keycode.h:344
SDL_Keymod
Enumeration of valid key mods (possibly OR'd together).
Definition: SDL_keycode.h:326
@ KMOD_LGUI
Definition: SDL_keycode.h:334
@ KMOD_CAPS
Definition: SDL_keycode.h:337
@ KMOD_RGUI
Definition: SDL_keycode.h:335
@ KMOD_NUM
Definition: SDL_keycode.h:336
GLint GLint GLint GLint GLint GLint y
Definition: SDL_opengl.h:1574
GLint GLenum GLsizei GLsizei GLsizei GLint GLsizei const GLvoid * data
Definition: SDL_opengl.h:1974
GLint GLint GLint GLint GLint x
Definition: SDL_opengl.h:1574
GLuint GLuint GLsizei GLenum type
Definition: SDL_opengl.h:1571
GLuint GLuint GLuint GLuint arg1
GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg3
GLuint id
GLuint GLuint GLuint GLuint GLuint GLuint GLuint arg2
GLuint buffer
GLuint const GLchar * name
GLenum GLuint GLenum GLsizei const GLchar * buf
GLfloat GLfloat p
GLboolean enable
GLfloat GLfloat GLfloat GLfloat h
GLubyte GLubyte GLubyte GLubyte w
SDL_bool
Definition: SDL_stdinc.h:162
@ SDL_TRUE
Definition: SDL_stdinc.h:164
@ SDL_FALSE
Definition: SDL_stdinc.h:163
uint32_t Uint32
Definition: SDL_stdinc.h:203
SDL_VideoDisplay * SDL_GetDisplayForWindow(SDL_Window *window)
Definition: SDL_video.c:1089
@ SDL_SYSWM_X11
Definition: SDL_syswm.h:123
#define SDL_VERSION(x)
Macro to determine SDL version program was compiled against.
Definition: SDL_version.h:79
struct xkb_state * state
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 NULL
Definition: begin_code.h:167
A rectangle, with the origin at the upper left (integer).
Definition: SDL_rect.h:78
struct SDL_SysWMinfo::@17::@18 x11
union SDL_SysWMinfo::@17 info
SDL_SYSWM_TYPE subsystem
Definition: SDL_syswm.h:200
Display * display
Definition: SDL_syswm.h:220
Window window
Definition: SDL_syswm.h:221
SDL_version version
Definition: SDL_syswm.h:199
The type used to identify a window.
Definition: SDL_sysvideo.h:74
static char text[MAX_TEXT_LENGTH]
Definition: testime.c:47
SDL_Rect rect
Definition: testrelative.c:27
SDL_Cursor * cursor
Definition: testwm2.c:40