Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/core/linux/SDL_fcitx.c
10279 views
1
/*
2
Simple DirectMedia Layer
3
Copyright (C) 1997-2025 Sam Lantinga <[email protected]>
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
#include <unistd.h>
24
25
#include "SDL_fcitx.h"
26
//#include "../../video/SDL_sysvideo.h"
27
#include "SDL_dbus.h"
28
29
#ifdef SDL_VIDEO_DRIVER_X11
30
#include "../../video/x11/SDL_x11video.h"
31
#endif
32
33
#define FCITX_DBUS_SERVICE "org.freedesktop.portal.Fcitx"
34
35
#define FCITX_IM_DBUS_PATH "/org/freedesktop/portal/inputmethod"
36
37
#define FCITX_IM_DBUS_INTERFACE "org.fcitx.Fcitx.InputMethod1"
38
#define FCITX_IC_DBUS_INTERFACE "org.fcitx.Fcitx.InputContext1"
39
40
#define DBUS_TIMEOUT 500
41
42
typedef struct FcitxClient
43
{
44
SDL_DBusContext *dbus;
45
46
char *ic_path;
47
48
int id;
49
50
SDL_Rect cursor_rect;
51
} FcitxClient;
52
53
static FcitxClient fcitx_client;
54
55
static char *GetAppName(void)
56
{
57
#if defined(SDL_PLATFORM_LINUX) || defined(SDL_PLATFORM_FREEBSD)
58
char *spot;
59
char procfile[1024];
60
char linkfile[1024];
61
int linksize;
62
63
#ifdef SDL_PLATFORM_LINUX
64
(void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/exe", getpid());
65
#elif defined(SDL_PLATFORM_FREEBSD)
66
(void)SDL_snprintf(procfile, sizeof(procfile), "/proc/%d/file", getpid());
67
#endif
68
linksize = readlink(procfile, linkfile, sizeof(linkfile) - 1);
69
if (linksize > 0) {
70
linkfile[linksize] = '\0';
71
spot = SDL_strrchr(linkfile, '/');
72
if (spot) {
73
return SDL_strdup(spot + 1);
74
} else {
75
return SDL_strdup(linkfile);
76
}
77
}
78
#endif // SDL_PLATFORM_LINUX || SDL_PLATFORM_FREEBSD
79
80
return SDL_strdup("SDL_App");
81
}
82
83
static size_t Fcitx_GetPreeditString(SDL_DBusContext *dbus,
84
DBusMessage *msg,
85
char **ret,
86
Sint32 *start_pos,
87
Sint32 *end_pos)
88
{
89
char *text = NULL, *subtext;
90
size_t text_bytes = 0;
91
DBusMessageIter iter, array, sub;
92
Sint32 p_start_pos = -1;
93
Sint32 p_end_pos = -1;
94
95
dbus->message_iter_init(msg, &iter);
96
// Message type is a(si)i, we only need string part
97
if (dbus->message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
98
size_t pos = 0;
99
// First pass: calculate string length
100
dbus->message_iter_recurse(&iter, &array);
101
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
102
dbus->message_iter_recurse(&array, &sub);
103
subtext = NULL;
104
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
105
dbus->message_iter_get_basic(&sub, &subtext);
106
if (subtext && *subtext) {
107
text_bytes += SDL_strlen(subtext);
108
}
109
}
110
dbus->message_iter_next(&sub);
111
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_INT32 && p_end_pos == -1) {
112
// Type is a bit field defined as follows:
113
// bit 3: Underline, bit 4: HighLight, bit 5: DontCommit,
114
// bit 6: Bold, bit 7: Strike, bit 8: Italic
115
Sint32 type;
116
dbus->message_iter_get_basic(&sub, &type);
117
// We only consider highlight
118
if (type & (1 << 4)) {
119
if (p_start_pos == -1) {
120
p_start_pos = pos;
121
}
122
} else if (p_start_pos != -1 && p_end_pos == -1) {
123
p_end_pos = pos;
124
}
125
}
126
dbus->message_iter_next(&array);
127
if (subtext && *subtext) {
128
pos += SDL_utf8strlen(subtext);
129
}
130
}
131
if (p_start_pos != -1 && p_end_pos == -1) {
132
p_end_pos = pos;
133
}
134
if (text_bytes) {
135
text = SDL_malloc(text_bytes + 1);
136
}
137
138
if (text) {
139
char *pivot = text;
140
// Second pass: join all the sub string
141
dbus->message_iter_recurse(&iter, &array);
142
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_STRUCT) {
143
dbus->message_iter_recurse(&array, &sub);
144
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_STRING) {
145
dbus->message_iter_get_basic(&sub, &subtext);
146
if (subtext && *subtext) {
147
size_t length = SDL_strlen(subtext);
148
SDL_strlcpy(pivot, subtext, length + 1);
149
pivot += length;
150
}
151
}
152
dbus->message_iter_next(&array);
153
}
154
} else {
155
text_bytes = 0;
156
}
157
}
158
159
*ret = text;
160
*start_pos = p_start_pos;
161
*end_pos = p_end_pos;
162
return text_bytes;
163
}
164
165
static Sint32 Fcitx_GetPreeditCursorByte(SDL_DBusContext *dbus, DBusMessage *msg)
166
{
167
Sint32 byte = -1;
168
DBusMessageIter iter;
169
170
dbus->message_iter_init(msg, &iter);
171
172
dbus->message_iter_next(&iter);
173
174
if (dbus->message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
175
return -1;
176
}
177
178
dbus->message_iter_get_basic(&iter, &byte);
179
180
return byte;
181
}
182
183
static DBusHandlerResult DBus_MessageFilter(DBusConnection *conn, DBusMessage *msg, void *data)
184
{
185
SDL_DBusContext *dbus = (SDL_DBusContext *)data;
186
187
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "CommitString")) {
188
DBusMessageIter iter;
189
const char *text = NULL;
190
191
dbus->message_iter_init(msg, &iter);
192
dbus->message_iter_get_basic(&iter, &text);
193
194
//SDL_SendKeyboardText(text);
195
196
return DBUS_HANDLER_RESULT_HANDLED;
197
}
198
199
if (dbus->message_is_signal(msg, FCITX_IC_DBUS_INTERFACE, "UpdateFormattedPreedit")) {
200
char *text = NULL;
201
Sint32 start_pos, end_pos;
202
size_t text_bytes = Fcitx_GetPreeditString(dbus, msg, &text, &start_pos, &end_pos);
203
if (text_bytes) {
204
if (start_pos == -1) {
205
Sint32 byte_pos = Fcitx_GetPreeditCursorByte(dbus, msg);
206
start_pos = byte_pos >= 0 ? SDL_utf8strnlen(text, byte_pos) : -1;
207
}
208
//SDL_SendEditingText(text, start_pos, end_pos >= 0 ? end_pos - start_pos : -1);
209
SDL_free(text);
210
} else {
211
//SDL_SendEditingText("", 0, 0);
212
}
213
214
//SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
215
return DBUS_HANDLER_RESULT_HANDLED;
216
}
217
218
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
219
}
220
221
static void FcitxClientICCallMethod(FcitxClient *client, const char *method)
222
{
223
if (!client->ic_path) {
224
return;
225
}
226
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, method, DBUS_TYPE_INVALID);
227
}
228
229
static void SDLCALL Fcitx_SetCapabilities(void *data,
230
const char *name,
231
const char *old_val,
232
const char *hint)
233
{
234
FcitxClient *client = (FcitxClient *)data;
235
Uint64 caps = 0;
236
if (!client->ic_path) {
237
return;
238
}
239
240
if (hint && SDL_strstr(hint, "composition")) {
241
caps |= (1 << 1); // Preedit Flag
242
caps |= (1 << 4); // Formatted Preedit Flag
243
}
244
if (hint && SDL_strstr(hint, "candidates")) {
245
// FIXME, turn off native candidate rendering
246
}
247
248
SDL_DBus_CallVoidMethod(FCITX_DBUS_SERVICE, client->ic_path, FCITX_IC_DBUS_INTERFACE, "SetCapability", DBUS_TYPE_UINT64, &caps, DBUS_TYPE_INVALID);
249
}
250
251
static bool FcitxCreateInputContext(SDL_DBusContext *dbus, const char *appname, char **ic_path)
252
{
253
const char *program = "program";
254
bool result = false;
255
256
if (dbus && dbus->session_conn) {
257
DBusMessage *msg = dbus->message_new_method_call(FCITX_DBUS_SERVICE, FCITX_IM_DBUS_PATH, FCITX_IM_DBUS_INTERFACE, "CreateInputContext");
258
if (msg) {
259
DBusMessage *reply = NULL;
260
DBusMessageIter args, array, sub;
261
dbus->message_iter_init_append(msg, &args);
262
dbus->message_iter_open_container(&args, DBUS_TYPE_ARRAY, "(ss)", &array);
263
dbus->message_iter_open_container(&array, DBUS_TYPE_STRUCT, 0, &sub);
264
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &program);
265
dbus->message_iter_append_basic(&sub, DBUS_TYPE_STRING, &appname);
266
dbus->message_iter_close_container(&array, &sub);
267
dbus->message_iter_close_container(&args, &array);
268
reply = dbus->connection_send_with_reply_and_block(dbus->session_conn, msg, 300, NULL);
269
if (reply) {
270
if (dbus->message_get_args(reply, NULL, DBUS_TYPE_OBJECT_PATH, ic_path, DBUS_TYPE_INVALID)) {
271
result = true;
272
}
273
dbus->message_unref(reply);
274
}
275
dbus->message_unref(msg);
276
}
277
}
278
return result;
279
}
280
281
static bool FcitxClientCreateIC(FcitxClient *client)
282
{
283
char *appname = GetAppName();
284
char *ic_path = NULL;
285
SDL_DBusContext *dbus = client->dbus;
286
287
// SDL_DBus_CallMethod cannot handle a(ss) type, call dbus function directly
288
if (!FcitxCreateInputContext(dbus, appname, &ic_path)) {
289
ic_path = NULL; // just in case.
290
}
291
292
SDL_free(appname);
293
294
if (ic_path) {
295
SDL_free(client->ic_path);
296
client->ic_path = SDL_strdup(ic_path);
297
298
dbus->bus_add_match(dbus->session_conn,
299
"type='signal', interface='org.fcitx.Fcitx.InputContext1'",
300
NULL);
301
dbus->connection_add_filter(dbus->session_conn,
302
&DBus_MessageFilter, dbus,
303
NULL);
304
dbus->connection_flush(dbus->session_conn);
305
306
SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, Fcitx_SetCapabilities, client);
307
return true;
308
}
309
310
return false;
311
}
312
313
static Uint32 Fcitx_ModState(void)
314
{
315
Uint32 fcitx_mods = 0;
316
SDL_Keymod sdl_mods = SDL_GetModState();
317
318
if (sdl_mods & SDL_KMOD_SHIFT) {
319
fcitx_mods |= (1 << 0);
320
}
321
if (sdl_mods & SDL_KMOD_CAPS) {
322
fcitx_mods |= (1 << 1);
323
}
324
if (sdl_mods & SDL_KMOD_CTRL) {
325
fcitx_mods |= (1 << 2);
326
}
327
if (sdl_mods & SDL_KMOD_ALT) {
328
fcitx_mods |= (1 << 3);
329
}
330
if (sdl_mods & SDL_KMOD_NUM) {
331
fcitx_mods |= (1 << 4);
332
}
333
if (sdl_mods & SDL_KMOD_MODE) {
334
fcitx_mods |= (1 << 7);
335
}
336
if (sdl_mods & SDL_KMOD_LGUI) {
337
fcitx_mods |= (1 << 6);
338
}
339
if (sdl_mods & SDL_KMOD_RGUI) {
340
fcitx_mods |= (1 << 28);
341
}
342
343
return fcitx_mods;
344
}
345
346
bool SDL_Fcitx_Init(void)
347
{
348
fcitx_client.dbus = SDL_DBus_GetContext();
349
350
fcitx_client.cursor_rect.x = -1;
351
fcitx_client.cursor_rect.y = -1;
352
fcitx_client.cursor_rect.w = 0;
353
fcitx_client.cursor_rect.h = 0;
354
355
return FcitxClientCreateIC(&fcitx_client);
356
}
357
358
void SDL_Fcitx_Quit(void)
359
{
360
FcitxClientICCallMethod(&fcitx_client, "DestroyIC");
361
if (fcitx_client.ic_path) {
362
SDL_free(fcitx_client.ic_path);
363
fcitx_client.ic_path = NULL;
364
}
365
}
366
367
void SDL_Fcitx_SetFocus(bool focused)
368
{
369
if (focused) {
370
FcitxClientICCallMethod(&fcitx_client, "FocusIn");
371
} else {
372
FcitxClientICCallMethod(&fcitx_client, "FocusOut");
373
}
374
}
375
376
void SDL_Fcitx_Reset(void)
377
{
378
FcitxClientICCallMethod(&fcitx_client, "Reset");
379
}
380
381
bool SDL_Fcitx_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
382
{
383
Uint32 mod_state = Fcitx_ModState();
384
Uint32 handled = false;
385
Uint32 is_release = !down;
386
Uint32 event_time = 0;
387
388
if (!fcitx_client.ic_path) {
389
return false;
390
}
391
392
if (SDL_DBus_CallMethod(FCITX_DBUS_SERVICE, fcitx_client.ic_path, FCITX_IC_DBUS_INTERFACE, "ProcessKeyEvent",
393
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &mod_state, DBUS_TYPE_BOOLEAN, &is_release, DBUS_TYPE_UINT32, &event_time, DBUS_TYPE_INVALID,
394
DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID)) {
395
if (handled) {
396
//SDL_Fcitx_UpdateTextInputArea(SDL_GetKeyboardFocus());
397
return true;
398
}
399
}
400
401
return false;
402
}
403
404
void SDL_Fcitx_PumpEvents(void)
405
{
406
SDL_DBusContext *dbus = fcitx_client.dbus;
407
DBusConnection *conn = dbus->session_conn;
408
409
dbus->connection_read_write(conn, 0);
410
411
while (dbus->connection_dispatch(conn) == DBUS_DISPATCH_DATA_REMAINS) {
412
// Do nothing, actual work happens in DBus_MessageFilter
413
}
414
}
415
416