Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/thirdparty/sdl/core/linux/SDL_ibus.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
#ifdef HAVE_IBUS_IBUS_H
24
#include "SDL_ibus.h"
25
#include "SDL_dbus.h"
26
27
#ifdef SDL_USE_LIBDBUS
28
29
//#include "../../video/SDL_sysvideo.h"
30
#include "../../events/SDL_keyboard_c.h"
31
32
#ifdef SDL_VIDEO_DRIVER_X11
33
#include "../../video/x11/SDL_x11video.h"
34
#endif
35
36
#include <sys/inotify.h>
37
#include <unistd.h>
38
#include <fcntl.h>
39
40
static const char IBUS_PATH[] = "/org/freedesktop/IBus";
41
42
static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
43
static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
44
static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
45
46
static const char IBUS_PORTAL_SERVICE[] = "org.freedesktop.portal.IBus";
47
static const char IBUS_PORTAL_INTERFACE[] = "org.freedesktop.IBus.Portal";
48
static const char IBUS_PORTAL_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
49
50
static const char *ibus_service = NULL;
51
static const char *ibus_interface = NULL;
52
static const char *ibus_input_interface = NULL;
53
static char *input_ctx_path = NULL;
54
static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 };
55
static DBusConnection *ibus_conn = NULL;
56
static bool ibus_is_portal_interface = false;
57
static char *ibus_addr_file = NULL;
58
static int inotify_fd = -1, inotify_wd = -1;
59
60
static Uint32 IBus_ModState(void)
61
{
62
Uint32 ibus_mods = 0;
63
SDL_Keymod sdl_mods = SDL_GetModState();
64
65
// Not sure about MOD3, MOD4 and HYPER mappings
66
if (sdl_mods & SDL_KMOD_LSHIFT) {
67
ibus_mods |= IBUS_SHIFT_MASK;
68
}
69
if (sdl_mods & SDL_KMOD_CAPS) {
70
ibus_mods |= IBUS_LOCK_MASK;
71
}
72
if (sdl_mods & SDL_KMOD_LCTRL) {
73
ibus_mods |= IBUS_CONTROL_MASK;
74
}
75
if (sdl_mods & SDL_KMOD_LALT) {
76
ibus_mods |= IBUS_MOD1_MASK;
77
}
78
if (sdl_mods & SDL_KMOD_NUM) {
79
ibus_mods |= IBUS_MOD2_MASK;
80
}
81
if (sdl_mods & SDL_KMOD_MODE) {
82
ibus_mods |= IBUS_MOD5_MASK;
83
}
84
if (sdl_mods & SDL_KMOD_LGUI) {
85
ibus_mods |= IBUS_SUPER_MASK;
86
}
87
if (sdl_mods & SDL_KMOD_RGUI) {
88
ibus_mods |= IBUS_META_MASK;
89
}
90
91
return ibus_mods;
92
}
93
94
static bool IBus_EnterVariant(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
95
DBusMessageIter *inside, const char *struct_id, size_t id_size)
96
{
97
DBusMessageIter sub;
98
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) {
99
return false;
100
}
101
102
dbus->message_iter_recurse(iter, &sub);
103
104
if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRUCT) {
105
return false;
106
}
107
108
dbus->message_iter_recurse(&sub, inside);
109
110
if (dbus->message_iter_get_arg_type(inside) != DBUS_TYPE_STRING) {
111
return false;
112
}
113
114
dbus->message_iter_get_basic(inside, &struct_id);
115
if (!struct_id || SDL_strncmp(struct_id, struct_id, id_size) != 0) {
116
return false;
117
}
118
return true;
119
}
120
121
static bool IBus_GetDecorationPosition(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
122
Uint32 *start_pos, Uint32 *end_pos)
123
{
124
DBusMessageIter sub1, sub2, array;
125
126
if (!IBus_EnterVariant(conn, iter, dbus, &sub1, "IBusText", sizeof("IBusText"))) {
127
return false;
128
}
129
130
dbus->message_iter_next(&sub1);
131
dbus->message_iter_next(&sub1);
132
dbus->message_iter_next(&sub1);
133
134
if (!IBus_EnterVariant(conn, &sub1, dbus, &sub2, "IBusAttrList", sizeof("IBusAttrList"))) {
135
return false;
136
}
137
138
dbus->message_iter_next(&sub2);
139
dbus->message_iter_next(&sub2);
140
141
if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_ARRAY) {
142
return false;
143
}
144
145
dbus->message_iter_recurse(&sub2, &array);
146
147
while (dbus->message_iter_get_arg_type(&array) == DBUS_TYPE_VARIANT) {
148
DBusMessageIter sub;
149
if (IBus_EnterVariant(conn, &array, dbus, &sub, "IBusAttribute", sizeof("IBusAttribute"))) {
150
Uint32 type;
151
152
dbus->message_iter_next(&sub);
153
dbus->message_iter_next(&sub);
154
155
// From here on, the structure looks like this:
156
// Uint32 type: 1=underline, 2=foreground, 3=background
157
// Uint32 value: for underline it's 0=NONE, 1=SINGLE, 2=DOUBLE,
158
// 3=LOW, 4=ERROR
159
// for foreground and background it's a color
160
// Uint32 start_index: starting position for the style (utf8-char)
161
// Uint32 end_index: end position for the style (utf8-char)
162
163
dbus->message_iter_get_basic(&sub, &type);
164
// We only use the background type to determine the selection
165
if (type == 3) {
166
Uint32 start = -1;
167
dbus->message_iter_next(&sub);
168
dbus->message_iter_next(&sub);
169
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
170
dbus->message_iter_get_basic(&sub, &start);
171
dbus->message_iter_next(&sub);
172
if (dbus->message_iter_get_arg_type(&sub) == DBUS_TYPE_UINT32) {
173
dbus->message_iter_get_basic(&sub, end_pos);
174
*start_pos = start;
175
return true;
176
}
177
}
178
}
179
}
180
dbus->message_iter_next(&array);
181
}
182
return false;
183
}
184
185
static const char *IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus)
186
{
187
// The text we need is nested weirdly, use dbus-monitor to see the structure better
188
const char *text = NULL;
189
DBusMessageIter sub;
190
191
if (!IBus_EnterVariant(conn, iter, dbus, &sub, "IBusText", sizeof("IBusText"))) {
192
return NULL;
193
}
194
195
dbus->message_iter_next(&sub);
196
dbus->message_iter_next(&sub);
197
198
if (dbus->message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) {
199
return NULL;
200
}
201
dbus->message_iter_get_basic(&sub, &text);
202
203
return text;
204
}
205
206
static bool IBus_GetVariantCursorPos(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus,
207
Uint32 *pos)
208
{
209
dbus->message_iter_next(iter);
210
211
if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) {
212
return false;
213
}
214
215
dbus->message_iter_get_basic(iter, pos);
216
217
return true;
218
}
219
220
static DBusHandlerResult IBus_MessageHandler(DBusConnection *conn, DBusMessage *msg, void *user_data)
221
{
222
SDL_DBusContext *dbus = (SDL_DBusContext *)user_data;
223
224
if (dbus->message_is_signal(msg, ibus_input_interface, "CommitText")) {
225
DBusMessageIter iter;
226
const char *text;
227
228
dbus->message_iter_init(msg, &iter);
229
text = IBus_GetVariantText(conn, &iter, dbus);
230
231
SDL_SendKeyboardText(text);
232
233
return DBUS_HANDLER_RESULT_HANDLED;
234
}
235
236
if (dbus->message_is_signal(msg, ibus_input_interface, "UpdatePreeditText")) {
237
DBusMessageIter iter;
238
const char *text;
239
240
dbus->message_iter_init(msg, &iter);
241
text = IBus_GetVariantText(conn, &iter, dbus);
242
243
if (text) {
244
Uint32 pos, start_pos, end_pos;
245
bool has_pos = false;
246
bool has_dec_pos = false;
247
248
dbus->message_iter_init(msg, &iter);
249
has_dec_pos = IBus_GetDecorationPosition(conn, &iter, dbus, &start_pos, &end_pos);
250
if (!has_dec_pos) {
251
dbus->message_iter_init(msg, &iter);
252
has_pos = IBus_GetVariantCursorPos(conn, &iter, dbus, &pos);
253
}
254
255
if (has_dec_pos) {
256
SDL_SendEditingText(text, start_pos, end_pos - start_pos);
257
} else if (has_pos) {
258
SDL_SendEditingText(text, pos, -1);
259
} else {
260
SDL_SendEditingText(text, -1, -1);
261
}
262
}
263
264
//SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
265
266
return DBUS_HANDLER_RESULT_HANDLED;
267
}
268
269
if (dbus->message_is_signal(msg, ibus_input_interface, "HidePreeditText")) {
270
SDL_SendEditingText("", 0, 0);
271
return DBUS_HANDLER_RESULT_HANDLED;
272
}
273
274
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
275
}
276
277
static char *IBus_ReadAddressFromFile(const char *file_path)
278
{
279
char addr_buf[1024];
280
bool success = false;
281
FILE *addr_file;
282
283
addr_file = fopen(file_path, "r");
284
if (!addr_file) {
285
return NULL;
286
}
287
288
while (fgets(addr_buf, sizeof(addr_buf), addr_file)) {
289
if (SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=") - 1) == 0) {
290
size_t sz = SDL_strlen(addr_buf);
291
if (addr_buf[sz - 1] == '\n') {
292
addr_buf[sz - 1] = 0;
293
}
294
if (addr_buf[sz - 2] == '\r') {
295
addr_buf[sz - 2] = 0;
296
}
297
success = true;
298
break;
299
}
300
}
301
302
(void)fclose(addr_file);
303
304
if (success) {
305
return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1));
306
} else {
307
return NULL;
308
}
309
}
310
311
static char *IBus_GetDBusAddressFilename(void)
312
{
313
SDL_DBusContext *dbus;
314
const char *disp_env;
315
char config_dir[PATH_MAX];
316
char *display = NULL;
317
const char *addr;
318
const char *conf_env;
319
char *key;
320
char file_path[PATH_MAX];
321
const char *host;
322
char *disp_num, *screen_num;
323
324
if (ibus_addr_file) {
325
return SDL_strdup(ibus_addr_file);
326
}
327
328
dbus = SDL_DBus_GetContext();
329
if (!dbus) {
330
return NULL;
331
}
332
333
// Use this environment variable if it exists.
334
addr = SDL_getenv("IBUS_ADDRESS");
335
if (addr && *addr) {
336
return SDL_strdup(addr);
337
}
338
339
/* Otherwise, we have to get the hostname, display, machine id, config dir
340
and look up the address from a filepath using all those bits, eek. */
341
disp_env = SDL_getenv("DISPLAY");
342
343
if (!disp_env || !*disp_env) {
344
display = SDL_strdup(":0.0");
345
} else {
346
display = SDL_strdup(disp_env);
347
}
348
349
host = display;
350
disp_num = SDL_strrchr(display, ':');
351
screen_num = SDL_strrchr(display, '.');
352
353
if (!disp_num) {
354
SDL_free(display);
355
return NULL;
356
}
357
358
*disp_num = 0;
359
disp_num++;
360
361
if (screen_num) {
362
*screen_num = 0;
363
}
364
365
if (!*host) {
366
const char *session = SDL_getenv("XDG_SESSION_TYPE");
367
if (session && SDL_strcmp(session, "wayland") == 0) {
368
host = "unix-wayland";
369
} else {
370
host = "unix";
371
}
372
}
373
374
SDL_memset(config_dir, 0, sizeof(config_dir));
375
376
conf_env = SDL_getenv("XDG_CONFIG_HOME");
377
if (conf_env && *conf_env) {
378
SDL_strlcpy(config_dir, conf_env, sizeof(config_dir));
379
} else {
380
const char *home_env = SDL_getenv("HOME");
381
if (!home_env || !*home_env) {
382
SDL_free(display);
383
return NULL;
384
}
385
(void)SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env);
386
}
387
388
key = SDL_DBus_GetLocalMachineId();
389
390
if (!key) {
391
SDL_free(display);
392
return NULL;
393
}
394
395
SDL_memset(file_path, 0, sizeof(file_path));
396
(void)SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s",
397
config_dir, key, host, disp_num);
398
dbus->free(key);
399
SDL_free(display);
400
401
return SDL_strdup(file_path);
402
}
403
404
static bool IBus_CheckConnection(SDL_DBusContext *dbus);
405
406
static void SDLCALL IBus_SetCapabilities(void *data, const char *name, const char *old_val,
407
const char *hint)
408
{
409
SDL_DBusContext *dbus = SDL_DBus_GetContext();
410
411
if (IBus_CheckConnection(dbus)) {
412
Uint32 caps = IBUS_CAP_FOCUS;
413
414
if (hint && SDL_strstr(hint, "composition")) {
415
caps |= IBUS_CAP_PREEDIT_TEXT;
416
}
417
if (hint && SDL_strstr(hint, "candidates")) {
418
// FIXME, turn off native candidate rendering
419
}
420
421
SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCapabilities",
422
DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID);
423
}
424
}
425
426
static bool IBus_SetupConnection(SDL_DBusContext *dbus, const char *addr)
427
{
428
const char *client_name = "SDL3_Application";
429
const char *path = NULL;
430
bool result = false;
431
DBusObjectPathVTable ibus_vtable;
432
433
SDL_zero(ibus_vtable);
434
ibus_vtable.message_function = &IBus_MessageHandler;
435
436
/* try the portal interface first. Modern systems have this in general,
437
and sandbox things like FlakPak and Snaps, etc, require it. */
438
439
ibus_is_portal_interface = true;
440
ibus_service = IBUS_PORTAL_SERVICE;
441
ibus_interface = IBUS_PORTAL_INTERFACE;
442
ibus_input_interface = IBUS_PORTAL_INPUT_INTERFACE;
443
ibus_conn = dbus->session_conn;
444
445
result = SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
446
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
447
DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
448
if (!result) {
449
ibus_is_portal_interface = false;
450
ibus_service = IBUS_SERVICE;
451
ibus_interface = IBUS_INTERFACE;
452
ibus_input_interface = IBUS_INPUT_INTERFACE;
453
ibus_conn = dbus->connection_open_private(addr, NULL);
454
455
if (!ibus_conn) {
456
return false; // oh well.
457
}
458
459
dbus->connection_flush(ibus_conn);
460
461
if (!dbus->bus_register(ibus_conn, NULL)) {
462
ibus_conn = NULL;
463
return false;
464
}
465
466
dbus->connection_flush(ibus_conn);
467
468
result = SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, IBUS_PATH, ibus_interface, "CreateInputContext",
469
DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID,
470
DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID);
471
} else {
472
// re-using dbus->session_conn
473
dbus->connection_ref(ibus_conn);
474
}
475
476
if (result) {
477
char matchstr[128];
478
(void)SDL_snprintf(matchstr, sizeof(matchstr), "type='signal',interface='%s'", ibus_input_interface);
479
SDL_free(input_ctx_path);
480
input_ctx_path = SDL_strdup(path);
481
SDL_AddHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
482
dbus->bus_add_match(ibus_conn, matchstr, NULL);
483
dbus->connection_try_register_object_path(ibus_conn, input_ctx_path, &ibus_vtable, dbus, NULL);
484
dbus->connection_flush(ibus_conn);
485
}
486
487
return result;
488
}
489
490
static bool IBus_CheckConnection(SDL_DBusContext *dbus)
491
{
492
if (!dbus) {
493
return false;
494
}
495
496
if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) {
497
return true;
498
}
499
500
if (inotify_fd > 0 && inotify_wd > 0) {
501
char buf[1024];
502
ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
503
if (readsize > 0) {
504
505
char *p;
506
bool file_updated = false;
507
508
for (p = buf; p < buf + readsize; /**/) {
509
struct inotify_event *event = (struct inotify_event *)p;
510
if (event->len > 0) {
511
char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
512
if (!addr_file_no_path) {
513
return false;
514
}
515
516
if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) {
517
file_updated = true;
518
break;
519
}
520
}
521
522
p += sizeof(struct inotify_event) + event->len;
523
}
524
525
if (file_updated) {
526
char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
527
if (addr) {
528
bool result = IBus_SetupConnection(dbus, addr);
529
SDL_free(addr);
530
return result;
531
}
532
}
533
}
534
}
535
536
return false;
537
}
538
539
bool SDL_IBus_Init(void)
540
{
541
bool result = false;
542
SDL_DBusContext *dbus = SDL_DBus_GetContext();
543
544
if (dbus) {
545
char *addr_file = IBus_GetDBusAddressFilename();
546
char *addr;
547
char *addr_file_dir;
548
549
if (!addr_file) {
550
return false;
551
}
552
553
addr = IBus_ReadAddressFromFile(addr_file);
554
if (!addr) {
555
SDL_free(addr_file);
556
return false;
557
}
558
559
if (ibus_addr_file) {
560
SDL_free(ibus_addr_file);
561
}
562
ibus_addr_file = SDL_strdup(addr_file);
563
564
if (inotify_fd < 0) {
565
inotify_fd = inotify_init();
566
fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
567
}
568
569
addr_file_dir = SDL_strrchr(addr_file, '/');
570
if (addr_file_dir) {
571
*addr_file_dir = 0;
572
}
573
574
inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
575
SDL_free(addr_file);
576
577
result = IBus_SetupConnection(dbus, addr);
578
SDL_free(addr);
579
580
// don't use the addr_file if using the portal interface.
581
if (result && ibus_is_portal_interface) {
582
if (inotify_fd > 0) {
583
if (inotify_wd > 0) {
584
inotify_rm_watch(inotify_fd, inotify_wd);
585
inotify_wd = -1;
586
}
587
close(inotify_fd);
588
inotify_fd = -1;
589
}
590
}
591
}
592
593
return result;
594
}
595
596
void SDL_IBus_Quit(void)
597
{
598
SDL_DBusContext *dbus;
599
600
if (input_ctx_path) {
601
SDL_free(input_ctx_path);
602
input_ctx_path = NULL;
603
}
604
605
if (ibus_addr_file) {
606
SDL_free(ibus_addr_file);
607
ibus_addr_file = NULL;
608
}
609
610
dbus = SDL_DBus_GetContext();
611
612
// if using portal, ibus_conn == session_conn; don't release it here.
613
if (dbus && ibus_conn && !ibus_is_portal_interface) {
614
dbus->connection_close(ibus_conn);
615
dbus->connection_unref(ibus_conn);
616
}
617
618
ibus_conn = NULL;
619
ibus_service = NULL;
620
ibus_interface = NULL;
621
ibus_input_interface = NULL;
622
ibus_is_portal_interface = false;
623
624
if (inotify_fd > 0 && inotify_wd > 0) {
625
inotify_rm_watch(inotify_fd, inotify_wd);
626
inotify_wd = -1;
627
}
628
629
// !!! FIXME: should we close(inotify_fd) here?
630
631
SDL_RemoveHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
632
633
SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect));
634
}
635
636
static void IBus_SimpleMessage(const char *method)
637
{
638
SDL_DBusContext *dbus = SDL_DBus_GetContext();
639
640
if ((input_ctx_path) && (IBus_CheckConnection(dbus))) {
641
SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, method, DBUS_TYPE_INVALID);
642
}
643
}
644
645
void SDL_IBus_SetFocus(bool focused)
646
{
647
const char *method = focused ? "FocusIn" : "FocusOut";
648
IBus_SimpleMessage(method);
649
}
650
651
void SDL_IBus_Reset(void)
652
{
653
IBus_SimpleMessage("Reset");
654
}
655
656
bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
657
{
658
Uint32 result = 0;
659
SDL_DBusContext *dbus = SDL_DBus_GetContext();
660
661
if (IBus_CheckConnection(dbus)) {
662
Uint32 mods = IBus_ModState();
663
Uint32 ibus_keycode = keycode - 8;
664
if (!down) {
665
mods |= (1 << 30); // IBUS_RELEASE_MASK
666
}
667
if (!SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "ProcessKeyEvent",
668
DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &ibus_keycode, DBUS_TYPE_UINT32, &mods, DBUS_TYPE_INVALID,
669
DBUS_TYPE_BOOLEAN, &result, DBUS_TYPE_INVALID)) {
670
result = 0;
671
}
672
}
673
674
//SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
675
676
return (result != 0);
677
}
678
679
void SDL_IBus_PumpEvents(void)
680
{
681
SDL_DBusContext *dbus = SDL_DBus_GetContext();
682
683
if (IBus_CheckConnection(dbus)) {
684
dbus->connection_read_write(ibus_conn, 0);
685
686
while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) {
687
// Do nothing, actual work happens in IBus_MessageHandler
688
}
689
}
690
}
691
692
#endif // SDL_USE_LIBDBUS
693
694
#endif
695
696