Atlas - SDL_ibus.c

Home / ext / SDL2 / src / core / linux Lines: 2 | Size: 16850 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)]
[FILE BEGIN]
1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2018 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.h" 25#include "SDL_syswm.h" 26#include "SDL_ibus.h" 27#include "SDL_dbus.h" 28#include "../../video/SDL_sysvideo.h" 29#include "../../events/SDL_keyboard_c.h" 30 31#if SDL_VIDEO_DRIVER_X11 32 #include "../../video/x11/SDL_x11video.h" 33#endif 34 35#include <sys/inotify.h> 36#include <unistd.h> 37#include <fcntl.h> 38 39static const char IBUS_SERVICE[] = "org.freedesktop.IBus"; 40static const char IBUS_PATH[] = "/org/freedesktop/IBus"; 41static const char IBUS_INTERFACE[] = "org.freedesktop.IBus"; 42static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext"; 43 44static char *input_ctx_path = NULL; 45static SDL_Rect ibus_cursor_rect = { 0, 0, 0, 0 }; 46static DBusConnection *ibus_conn = NULL; 47static char *ibus_addr_file = NULL; 48static int inotify_fd = -1, inotify_wd = -1; 49 50static Uint32 51IBus_ModState(void) 52{ 53 Uint32 ibus_mods = 0; 54 SDL_Keymod sdl_mods = SDL_GetModState(); 55 56 /* Not sure about MOD3, MOD4 and HYPER mappings */ 57 if (sdl_mods & KMOD_LSHIFT) ibus_mods |= IBUS_SHIFT_MASK; 58 if (sdl_mods & KMOD_CAPS) ibus_mods |= IBUS_LOCK_MASK; 59 if (sdl_mods & KMOD_LCTRL) ibus_mods |= IBUS_CONTROL_MASK; 60 if (sdl_mods & KMOD_LALT) ibus_mods |= IBUS_MOD1_MASK; 61 if (sdl_mods & KMOD_NUM) ibus_mods |= IBUS_MOD2_MASK; 62 if (sdl_mods & KMOD_MODE) ibus_mods |= IBUS_MOD5_MASK; 63 if (sdl_mods & KMOD_LGUI) ibus_mods |= IBUS_SUPER_MASK; 64 if (sdl_mods & KMOD_RGUI) ibus_mods |= IBUS_META_MASK; 65 66 return ibus_mods; 67} 68 69static const char * 70IBus_GetVariantText(DBusConnection *conn, DBusMessageIter *iter, SDL_DBusContext *dbus) 71{ 72 /* The text we need is nested weirdly, use dbus-monitor to see the structure better */ 73 const char *text = NULL; 74 const char *struct_id = NULL; 75 DBusMessageIter sub1, sub2; 76 77 if (dbus->message_iter_get_arg_type(iter) != DBUS_TYPE_VARIANT) { 78 return NULL; 79 } 80 81 dbus->message_iter_recurse(iter, &sub1); 82 83 if (dbus->message_iter_get_arg_type(&sub1) != DBUS_TYPE_STRUCT) { 84 return NULL; 85 } 86 87 dbus->message_iter_recurse(&sub1, &sub2); 88 89 if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) { 90 return NULL; 91 } 92 93 dbus->message_iter_get_basic(&sub2, &struct_id); 94 if (!struct_id || SDL_strncmp(struct_id, "IBusText", sizeof("IBusText")) != 0) { 95 return NULL; 96 } 97 98 dbus->message_iter_next(&sub2); 99 dbus->message_iter_next(&sub2); 100 101 if (dbus->message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) { 102 return NULL; 103 } 104 105 dbus->message_iter_get_basic(&sub2, &text); 106 107 return text; 108} 109 110static DBusHandlerResult 111IBus_MessageHandler(DBusConnection *conn, DBusMessage *msg, void *user_data) 112{ 113 SDL_DBusContext *dbus = (SDL_DBusContext *)user_data; 114 115 if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "CommitText")) { 116 DBusMessageIter iter; 117 const char *text; 118 119 dbus->message_iter_init(msg, &iter); 120 121 text = IBus_GetVariantText(conn, &iter, dbus); 122 if (text && *text) { 123 char buf[SDL_TEXTINPUTEVENT_TEXT_SIZE]; 124 size_t text_bytes = SDL_strlen(text), i = 0; 125 126 while (i < text_bytes) { 127 size_t sz = SDL_utf8strlcpy(buf, text+i, sizeof(buf)); 128 SDL_SendKeyboardText(buf); 129 130 i += sz; 131 } 132 } 133 134 return DBUS_HANDLER_RESULT_HANDLED; 135 } 136 137 if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "UpdatePreeditText")) { 138 DBusMessageIter iter; 139 const char *text; 140 141 dbus->message_iter_init(msg, &iter); 142 text = IBus_GetVariantText(conn, &iter, dbus); 143 144 if (text) { 145 char buf[SDL_TEXTEDITINGEVENT_TEXT_SIZE]; 146 size_t text_bytes = SDL_strlen(text), i = 0; 147 size_t cursor = 0; 148 149 do { 150 const size_t sz = SDL_utf8strlcpy(buf, text+i, sizeof(buf)); 151 const size_t chars = SDL_utf8strlen(buf); 152 153 SDL_SendEditingText(buf, cursor, chars); 154 155 i += sz; 156 cursor += chars; 157 } while (i < text_bytes); 158 } 159 160 SDL_IBus_UpdateTextRect(NULL); 161 162 return DBUS_HANDLER_RESULT_HANDLED; 163 } 164 165 if (dbus->message_is_signal(msg, IBUS_INPUT_INTERFACE, "HidePreeditText")) { 166 SDL_SendEditingText("", 0, 0); 167 return DBUS_HANDLER_RESULT_HANDLED; 168 } 169 170 return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; 171} 172 173static char * 174IBus_ReadAddressFromFile(const char *file_path) 175{ 176 char addr_buf[1024]; 177 SDL_bool success = SDL_FALSE; 178 FILE *addr_file; 179 180 addr_file = fopen(file_path, "r"); 181 if (!addr_file) { 182 return NULL; 183 } 184 185 while (fgets(addr_buf, sizeof(addr_buf), addr_file)) { 186 if (SDL_strncmp(addr_buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) { 187 size_t sz = SDL_strlen(addr_buf); 188 if (addr_buf[sz-1] == '\n') addr_buf[sz-1] = 0; 189 if (addr_buf[sz-2] == '\r') addr_buf[sz-2] = 0; 190 success = SDL_TRUE; 191 break; 192 } 193 } 194 195 fclose(addr_file); 196 197 if (success) { 198 return SDL_strdup(addr_buf + (sizeof("IBUS_ADDRESS=") - 1)); 199 } else { 200 return NULL; 201 } 202} 203 204static char * 205IBus_GetDBusAddressFilename(void) 206{ 207 SDL_DBusContext *dbus; 208 const char *disp_env; 209 char config_dir[PATH_MAX]; 210 char *display = NULL; 211 const char *addr; 212 const char *conf_env; 213 char *key; 214 char file_path[PATH_MAX]; 215 const char *host; 216 char *disp_num, *screen_num; 217 218 if (ibus_addr_file) { 219 return SDL_strdup(ibus_addr_file); 220 } 221 222 dbus = SDL_DBus_GetContext(); 223 if (!dbus) { 224 return NULL; 225 } 226 227 /* Use this environment variable if it exists. */ 228 addr = SDL_getenv("IBUS_ADDRESS"); 229 if (addr && *addr) { 230 return SDL_strdup(addr); 231 } 232 233 /* Otherwise, we have to get the hostname, display, machine id, config dir 234 and look up the address from a filepath using all those bits, eek. */ 235 disp_env = SDL_getenv("DISPLAY"); 236 237 if (!disp_env || !*disp_env) { 238 display = SDL_strdup(":0.0"); 239 } else { 240 display = SDL_strdup(disp_env); 241 } 242 243 host = display; 244 disp_num = SDL_strrchr(display, ':'); 245 screen_num = SDL_strrchr(display, '.'); 246 247 if (!disp_num) { 248 SDL_free(display); 249 return NULL; 250 } 251 252 *disp_num = 0; 253 disp_num++; 254 255 if (screen_num) { 256 *screen_num = 0; 257 } 258 259 if (!*host) { 260 host = "unix"; 261 } 262 263 SDL_memset(config_dir, 0, sizeof(config_dir)); 264 265 conf_env = SDL_getenv("XDG_CONFIG_HOME"); 266 if (conf_env && *conf_env) { 267 SDL_strlcpy(config_dir, conf_env, sizeof(config_dir)); 268 } else { 269 const char *home_env = SDL_getenv("HOME"); 270 if (!home_env || !*home_env) { 271 SDL_free(display); 272 return NULL; 273 } 274 SDL_snprintf(config_dir, sizeof(config_dir), "%s/.config", home_env); 275 } 276 277 key = dbus->get_local_machine_id(); 278 279 SDL_memset(file_path, 0, sizeof(file_path)); 280 SDL_snprintf(file_path, sizeof(file_path), "%s/ibus/bus/%s-%s-%s", 281 config_dir, key, host, disp_num); 282 dbus->free(key); 283 SDL_free(display); 284 285 return SDL_strdup(file_path); 286} 287 288static SDL_bool IBus_CheckConnection(SDL_DBusContext *dbus); 289 290static void SDLCALL 291IBus_SetCapabilities(void *data, const char *name, const char *old_val, 292 const char *internal_editing) 293{ 294 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 295 296 if (IBus_CheckConnection(dbus)) { 297 Uint32 caps = IBUS_CAP_FOCUS; 298 if (!(internal_editing && *internal_editing == '1')) { 299 caps |= IBUS_CAP_PREEDIT_TEXT; 300 } 301 302 SDL_DBus_CallVoidMethodOnConnection(ibus_conn, IBUS_SERVICE, input_ctx_path, IBUS_INPUT_INTERFACE, "SetCapabilities", 303 DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID); 304 } 305} 306 307 308static SDL_bool 309IBus_SetupConnection(SDL_DBusContext *dbus, const char* addr) 310{ 311 const char *client_name = "SDL2_Application"; 312 const char *path = NULL; 313 SDL_bool result = SDL_FALSE; 314 DBusObjectPathVTable ibus_vtable; 315 316 SDL_zero(ibus_vtable); 317 ibus_vtable.message_function = &IBus_MessageHandler; 318 319 ibus_conn = dbus->connection_open_private(addr, NULL); 320 321 if (!ibus_conn) { 322 return SDL_FALSE; 323 } 324 325 dbus->connection_flush(ibus_conn); 326 327 if (!dbus->bus_register(ibus_conn, NULL)) { 328 ibus_conn = NULL; 329 return SDL_FALSE; 330 } 331 332 dbus->connection_flush(ibus_conn); 333 334 if (SDL_DBus_CallMethodOnConnection(ibus_conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext", 335 DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID, 336 DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) { 337 SDL_free(input_ctx_path); 338 input_ctx_path = SDL_strdup(path); 339 SDL_AddHintCallback(SDL_HINT_IME_INTERNAL_EDITING, IBus_SetCapabilities, NULL); 340 341 dbus->bus_add_match(ibus_conn, "type='signal',interface='org.freedesktop.IBus.InputContext'", NULL); 342 dbus->connection_try_register_object_path(ibus_conn, input_ctx_path, &ibus_vtable, dbus, NULL); 343 dbus->connection_flush(ibus_conn); 344 result = SDL_TRUE; 345 } 346 347 SDL_IBus_SetFocus(SDL_GetKeyboardFocus() != NULL); 348 SDL_IBus_UpdateTextRect(NULL); 349 350 return result; 351} 352 353static SDL_bool 354IBus_CheckConnection(SDL_DBusContext *dbus) 355{ 356 if (!dbus) return SDL_FALSE; 357 358 if (ibus_conn && dbus->connection_get_is_connected(ibus_conn)) { 359 return SDL_TRUE; 360 } 361 362 if (inotify_fd > 0 && inotify_wd > 0) { 363 char buf[1024]; 364 ssize_t readsize = read(inotify_fd, buf, sizeof(buf)); 365 if (readsize > 0) { 366 367 char *p; 368 SDL_bool file_updated = SDL_FALSE; 369 370 for (p = buf; p < buf + readsize; /**/) { 371 struct inotify_event *event = (struct inotify_event*) p; 372 if (event->len > 0) { 373 char *addr_file_no_path = SDL_strrchr(ibus_addr_file, '/'); 374 if (!addr_file_no_path) return SDL_FALSE; 375 376 if (SDL_strcmp(addr_file_no_path + 1, event->name) == 0) { 377 file_updated = SDL_TRUE; 378 break; 379 } 380 } 381 382 p += sizeof(struct inotify_event) + event->len; 383 } 384 385 if (file_updated) { 386 char *addr = IBus_ReadAddressFromFile(ibus_addr_file); 387 if (addr) { 388 SDL_bool result = IBus_SetupConnection(dbus, addr); 389 SDL_free(addr); 390 return result; 391 } 392 } 393 } 394 } 395 396 return SDL_FALSE; 397} 398 399SDL_bool 400SDL_IBus_Init(void) 401{ 402 SDL_bool result = SDL_FALSE; 403 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 404 405 if (dbus) { 406 char *addr_file = IBus_GetDBusAddressFilename(); 407 char *addr; 408 char *addr_file_dir; 409 410 if (!addr_file) { 411 return SDL_FALSE; 412 } 413 414 /* !!! FIXME: if ibus_addr_file != NULL, this will overwrite it and leak (twice!) */ 415 ibus_addr_file = SDL_strdup(addr_file); 416 417 addr = IBus_ReadAddressFromFile(addr_file); 418 if (!addr) { 419 SDL_free(addr_file); 420 return SDL_FALSE; 421 } 422 423 if (inotify_fd < 0) { 424 inotify_fd = inotify_init(); 425 fcntl(inotify_fd, F_SETFL, O_NONBLOCK); 426 } 427 428 addr_file_dir = SDL_strrchr(addr_file, '/'); 429 if (addr_file_dir) { 430 *addr_file_dir = 0; 431 } 432 433 inotify_wd = inotify_add_watch(inotify_fd, addr_file, IN_CREATE | IN_MODIFY); 434 SDL_free(addr_file); 435 436 if (addr) { 437 result = IBus_SetupConnection(dbus, addr); 438 SDL_free(addr); 439 } 440 } 441 442 return result; 443} 444 445void 446SDL_IBus_Quit(void) 447{ 448 SDL_DBusContext *dbus; 449 450 if (input_ctx_path) { 451 SDL_free(input_ctx_path); 452 input_ctx_path = NULL; 453 } 454 455 if (ibus_addr_file) { 456 SDL_free(ibus_addr_file); 457 ibus_addr_file = NULL; 458 } 459 460 dbus = SDL_DBus_GetContext(); 461 462 if (dbus && ibus_conn) { 463 dbus->connection_close(ibus_conn); 464 dbus->connection_unref(ibus_conn); 465 } 466 467 if (inotify_fd > 0 && inotify_wd > 0) { 468 inotify_rm_watch(inotify_fd, inotify_wd); 469 inotify_wd = -1; 470 } 471 472 SDL_DelHintCallback(SDL_HINT_IME_INTERNAL_EDITING, IBus_SetCapabilities, NULL); 473 474 SDL_memset(&ibus_cursor_rect, 0, sizeof(ibus_cursor_rect)); 475} 476 477static void 478IBus_SimpleMessage(const char *method) 479{ 480 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 481 482 if (IBus_CheckConnection(dbus)) { 483 SDL_DBus_CallVoidMethodOnConnection(ibus_conn, IBUS_SERVICE, input_ctx_path, IBUS_INPUT_INTERFACE, method, DBUS_TYPE_INVALID); 484 } 485} 486 487void 488SDL_IBus_SetFocus(SDL_bool focused) 489{ 490 const char *method = focused ? "FocusIn" : "FocusOut"; 491 IBus_SimpleMessage(method); 492} 493 494void 495SDL_IBus_Reset(void) 496{ 497 IBus_SimpleMessage("Reset"); 498} 499 500SDL_bool 501SDL_IBus_ProcessKeyEvent(Uint32 keysym, Uint32 keycode) 502{ 503 Uint32 result = 0; 504 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 505 506 if (IBus_CheckConnection(dbus)) { 507 Uint32 mods = IBus_ModState(); 508 if (!SDL_DBus_CallMethodOnConnection(ibus_conn, IBUS_SERVICE, input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent", 509 DBUS_TYPE_UINT32, &keysym, DBUS_TYPE_UINT32, &keycode, DBUS_TYPE_UINT32, &mods, DBUS_TYPE_INVALID, 510 DBUS_TYPE_BOOLEAN, &result, DBUS_TYPE_INVALID)) { 511 result = 0; 512 } 513 } 514 515 SDL_IBus_UpdateTextRect(NULL); 516 517 return result ? SDL_TRUE : SDL_FALSE; 518} 519 520void 521SDL_IBus_UpdateTextRect(SDL_Rect *rect) 522{ 523 SDL_Window *focused_win; 524 SDL_SysWMinfo info; 525 int x = 0, y = 0; 526 SDL_DBusContext *dbus; 527 528 if (rect) { 529 SDL_memcpy(&ibus_cursor_rect, rect, sizeof(ibus_cursor_rect)); 530 } 531 532 focused_win = SDL_GetKeyboardFocus(); 533 if (!focused_win) { 534 return; 535 } 536 537 SDL_VERSION(&info.version); 538 if (!SDL_GetWindowWMInfo(focused_win, &info)) { 539 return; 540 } 541 542 SDL_GetWindowPosition(focused_win, &x, &y); 543 544#if SDL_VIDEO_DRIVER_X11 545 if (info.subsystem == SDL_SYSWM_X11) { 546 SDL_DisplayData *displaydata = (SDL_DisplayData *) SDL_GetDisplayForWindow(focused_win)->driverdata; 547 548 Display *x_disp = info.info.x11.display; 549 Window x_win = info.info.x11.window; 550 int x_screen = displaydata->screen; 551 Window unused; 552 553 X11_XTranslateCoordinates(x_disp, x_win, RootWindow(x_disp, x_screen), 0, 0, &x, &y, &unused); 554 } 555#endif 556 557 x += ibus_cursor_rect.x; 558 y += ibus_cursor_rect.y; 559 560 dbus = SDL_DBus_GetContext(); 561 562 if (IBus_CheckConnection(dbus)) { 563 SDL_DBus_CallVoidMethodOnConnection(ibus_conn, IBUS_SERVICE, input_ctx_path, IBUS_INPUT_INTERFACE, "SetCursorLocation", 564 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); 565 } 566} 567 568void 569SDL_IBus_PumpEvents(void) 570{ 571 SDL_DBusContext *dbus = SDL_DBus_GetContext(); 572 573 if (IBus_CheckConnection(dbus)) { 574 dbus->connection_read_write(ibus_conn, 0); 575 576 while (dbus->connection_dispatch(ibus_conn) == DBUS_DISPATCH_DATA_REMAINS) { 577 /* Do nothing, actual work happens in IBus_MessageHandler */ 578 } 579 } 580} 581 582#endif 583 584/* vi: set ts=4 sw=4 expandtab: */ 585
[FILE END]
(C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.