1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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_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
40static const char IBUS_PATH[] = "/org/freedesktop/IBus";
41
42static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
43static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
44static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
45
46static const char IBUS_PORTAL_SERVICE[] = "org.freedesktop.portal.IBus";
47static const char IBUS_PORTAL_INTERFACE[] = "org.freedesktop.IBus.Portal";
48static const char IBUS_PORTAL_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
49
50static const char *ibus_service = NULL;
51static const char *ibus_interface = NULL;
52static const char *ibus_input_interface = NULL;
53static char *input_ctx_path = NULL;
54static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 };
55static DBusConnection *ibus_conn = NULL;
56static bool ibus_is_portal_interface = false;
57static char *ibus_addr_file = NULL;
58static int inotify_fd = -1, inotify_wd = -1;
59
60static 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
94static 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
121static 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
185static 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
206static 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
220static 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
277static 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
311static 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
404static bool IBus_CheckConnection(SDL_DBusContext *dbus);
405
406static 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
426static 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 SDL_Window *window = SDL_GetKeyboardFocus();
488 if (SDL_TextInputActive(window)) {
489 SDL_IBus_SetFocus(true);
490 SDL_IBus_UpdateTextInputArea(window);
491 } else {
492 SDL_IBus_SetFocus(false);
493 }
494 return result;
495}
496
497static bool IBus_CheckConnection(SDL_DBusContext *dbus)
498{
499 if (!dbus) {
500 return false;
501 }
502
503 if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) {
504 return true;
505 }
506
507 if (inotify_fd > 0 && inotify_wd > 0) {
508 char buf[1024];
509 ssize_t readsize = read(inotify_fd, buf, sizeof(buf));
510 if (readsize > 0) {
511
512 char *p;
513 bool file_updated = false;
514
515 for (p = buf; p < buf + readsize; /**/) {
516 struct inotify_event *event = (struct inotify_event *)p;
517 if (event->len > 0) {
518 char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/');
519 if (!addr_file_no_path) {
520 return false;
521 }
522
523 if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) {
524 file_updated = true;
525 break;
526 }
527 }
528
529 p += sizeof(struct inotify_event) + event->len;
530 }
531
532 if (file_updated) {
533 char *addr = IBus_ReadAddressFromFile(ibus_addr_file);
534 if (addr) {
535 bool result = IBus_SetupConnection(dbus, addr);
536 SDL_free(addr);
537 return result;
538 }
539 }
540 }
541 }
542
543 return false;
544}
545
546bool SDL_IBus_Init(void)
547{
548 bool result = false;
549 SDL_DBusContext *dbus = SDL_DBus_GetContext();
550
551 if (dbus) {
552 char *addr_file = IBus_GetDBusAddressFilename();
553 char *addr;
554 char *addr_file_dir;
555
556 if (!addr_file) {
557 return false;
558 }
559
560 addr = IBus_ReadAddressFromFile(addr_file);
561 if (!addr) {
562 SDL_free(addr_file);
563 return false;
564 }
565
566 if (ibus_addr_file) {
567 SDL_free(ibus_addr_file);
568 }
569 ibus_addr_file = SDL_strdup(addr_file);
570
571 if (inotify_fd < 0) {
572 inotify_fd = inotify_init();
573 fcntl(inotify_fd, F_SETFL, O_NONBLOCK);
574 }
575
576 addr_file_dir = SDL_strrchr(addr_file, '/');
577 if (addr_file_dir) {
578 *addr_file_dir = 0;
579 }
580
581 inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY);
582 SDL_free(addr_file);
583
584 result = IBus_SetupConnection(dbus, addr);
585 SDL_free(addr);
586
587 // don't use the addr_file if using the portal interface.
588 if (result && ibus_is_portal_interface) {
589 if (inotify_fd > 0) {
590 if (inotify_wd > 0) {
591 inotify_rm_watch(inotify_fd, inotify_wd);
592 inotify_wd = -1;
593 }
594 close(inotify_fd);
595 inotify_fd = -1;
596 }
597 }
598 }
599
600 return result;
601}
602
603void SDL_IBus_Quit(void)
604{
605 SDL_DBusContext *dbus;
606
607 if (input_ctx_path) {
608 SDL_free(input_ctx_path);
609 input_ctx_path = NULL;
610 }
611
612 if (ibus_addr_file) {
613 SDL_free(ibus_addr_file);
614 ibus_addr_file = NULL;
615 }
616
617 dbus = SDL_DBus_GetContext();
618
619 // if using portal, ibus_conn == session_conn; don't release it here.
620 if (dbus && ibus_conn && !ibus_is_portal_interface) {
621 dbus->connection_close(ibus_conn);
622 dbus->connection_unref(ibus_conn);
623 }
624
625 ibus_conn = NULL;
626 ibus_service = NULL;
627 ibus_interface = NULL;
628 ibus_input_interface = NULL;
629 ibus_is_portal_interface = false;
630
631 if (inotify_fd > 0 && inotify_wd > 0) {
632 inotify_rm_watch(inotify_fd, inotify_wd);
633 inotify_wd = -1;
634 }
635
636 // !!! FIXME: should we close(inotify_fd) here?
637
638 SDL_RemoveHintCallback(SDL_HINT_IME_IMPLEMENTED_UI, IBus_SetCapabilities, NULL);
639
640 SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect));
641}
642
643static void IBus_SimpleMessage(const char *method)
644{
645 SDL_DBusContext *dbus = SDL_DBus_GetContext();
646
647 if ((input_ctx_path) && (IBus_CheckConnection(dbus))) {
648 SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, method, DBUS_TYPE_INVALID);
649 }
650}
651
652void SDL_IBus_SetFocus(bool focused)
653{
654 const char *method = focused ? "FocusIn" : "FocusOut";
655 IBus_SimpleMessage(method);
656}
657
658void SDL_IBus_Reset(void)
659{
660 IBus_SimpleMessage("Reset");
661}
662
663bool SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode, bool down)
664{
665 Uint32 result = 0;
666 SDL_DBusContext *dbus = SDL_DBus_GetContext();
667
668 if (IBus_CheckConnection(dbus)) {
669 Uint32 mods = IBus_ModState();
670 Uint32 ibus_keycode = keycode - 8;
671 if (!down) {
672 mods |= (1 << 30); // IBUS_RELEASE_MASK
673 }
674 if (!SDL_DBus_CallMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "ProcessKeyEvent",
675 DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &ibus_keycode, DBUS_TYPE_UINT32, &mods, DBUS_TYPE_INVALID,
676 DBUS_TYPE_BOOLEAN, &result, DBUS_TYPE_INVALID)) {
677 result = 0;
678 }
679 }
680
681 SDL_IBus_UpdateTextInputArea(SDL_GetKeyboardFocus());
682
683 return (result != 0);
684}
685
686void SDL_IBus_UpdateTextInputArea(SDL_Window *window)
687{
688 int x = 0, y = 0;
689 SDL_DBusContext *dbus;
690
691 if (!window) {
692 return;
693 }
694
695 // We'll use a square at the text input cursor location for the ibus_cursor
696 ibus_cursor_rect.x = window->text_input_rect.x + window->text_input_cursor;
697 ibus_cursor_rect.y = window->text_input_rect.y;
698 ibus_cursor_rect.w = window->text_input_rect.h;
699 ibus_cursor_rect.h = window->text_input_rect.h;
700
701 SDL_GetWindowPosition(window, &x, &y);
702
703#ifdef SDL_VIDEO_DRIVER_X11
704 {
705 SDL_PropertiesID props = SDL_GetWindowProperties(window);
706 Display *x_disp = (Display *)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_X11_DISPLAY_POINTER, NULL);
707 int x_screen = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_SCREEN_NUMBER, 0);
708 Window x_win = SDL_GetNumberProperty(props, SDL_PROP_WINDOW_X11_WINDOW_NUMBER, 0);
709 Window unused;
710
711 if (x_disp && x_win) {
712 X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused);
713 }
714 }
715#endif
716
717 x += ibus_cursor_rect.x;
718 y += ibus_cursor_rect.y;
719
720 dbus = SDL_DBus_GetContext();
721
722 if (IBus_CheckConnection(dbus)) {
723 SDL_DBus_CallVoidMethodOnConnection(ibus_conn, ibus_service, input_ctx_path, ibus_input_interface, "SetCursorLocation",
724 DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &ibus_cursor_rect.w, DBUS_TYPE_INT32, &ibus_cursor_rect.h, DBUS_TYPE_INVALID);
725 }
726}
727
728void SDL_IBus_PumpEvents(void)
729{
730 SDL_DBusContext *dbus = SDL_DBus_GetContext();
731
732 if (IBus_CheckConnection(dbus)) {
733 dbus->connection_read_write(ibus_conn, 0);
734
735 while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) {
736 // Do nothing, actual work happens in IBus_MessageHandler
737 }
738 }
739}
740
741#endif // SDL_USE_LIBDBUS
742
743#endif
744