ibus_glfw.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529
  1. //========================================================================
  2. // GLFW 3.4 XKB - www.glfw.org
  3. //------------------------------------------------------------------------
  4. // Copyright (c) 2018 Kovid Goyal <kovid@kovidgoyal.net>
  5. //
  6. // This software is provided 'as-is', without any express or implied
  7. // warranty. In no event will the authors be held liable for any damages
  8. // arising from the use of this software.
  9. //
  10. // Permission is granted to anyone to use this software for any purpose,
  11. // including commercial applications, and to alter it and redistribute it
  12. // freely, subject to the following restrictions:
  13. //
  14. // 1. The origin of this software must not be misrepresented; you must not
  15. // claim that you wrote the original software. If you use this software
  16. // in a product, an acknowledgment in the product documentation would
  17. // be appreciated but is not required.
  18. //
  19. // 2. Altered source versions must be plainly marked as such, and must not
  20. // be misrepresented as being the original software.
  21. //
  22. // 3. This notice may not be removed or altered from any source
  23. // distribution.
  24. //
  25. //========================================================================
  26. /* To test under X11 start IBUS as:
  27. * ibus-daemon -drxR
  28. * Setup the input sources you want with:
  29. * ibus-setup
  30. * Switch to the input source you want to test with:
  31. * ibus engine name
  32. * You can list available engines with:
  33. * ibus list-engine
  34. * Then run kitty as:
  35. * GLFW_IM_MODULE=ibus kitty
  36. */
  37. #define _GNU_SOURCE
  38. #include <stdio.h>
  39. #include <errno.h>
  40. #include <stdlib.h>
  41. #include <string.h>
  42. #include <sys/stat.h>
  43. #include <limits.h>
  44. #include "internal.h"
  45. #include "ibus_glfw.h"
  46. #define debug debug_input
  47. static const char IBUS_SERVICE[] = "org.freedesktop.IBus";
  48. static const char IBUS_PATH[] = "/org/freedesktop/IBus";
  49. static const char IBUS_INTERFACE[] = "org.freedesktop.IBus";
  50. static const char IBUS_INPUT_INTERFACE[] = "org.freedesktop.IBus.InputContext";
  51. enum Capabilities {
  52. IBUS_CAP_PREEDIT_TEXT = 1 << 0,
  53. IBUS_CAP_AUXILIARY_TEXT = 1 << 1,
  54. IBUS_CAP_LOOKUP_TABLE = 1 << 2,
  55. IBUS_CAP_FOCUS = 1 << 3,
  56. IBUS_CAP_PROPERTY = 1 << 4,
  57. IBUS_CAP_SURROUNDING_TEXT = 1 << 5
  58. };
  59. typedef enum
  60. {
  61. IBUS_SHIFT_MASK = 1 << 0,
  62. IBUS_LOCK_MASK = 1 << 1,
  63. IBUS_CONTROL_MASK = 1 << 2,
  64. IBUS_MOD1_MASK = 1 << 3,
  65. IBUS_MOD2_MASK = 1 << 4,
  66. IBUS_MOD3_MASK = 1 << 5,
  67. IBUS_MOD4_MASK = 1 << 6,
  68. IBUS_MOD5_MASK = 1 << 7,
  69. IBUS_BUTTON1_MASK = 1 << 8,
  70. IBUS_BUTTON2_MASK = 1 << 9,
  71. IBUS_BUTTON3_MASK = 1 << 10,
  72. IBUS_BUTTON4_MASK = 1 << 11,
  73. IBUS_BUTTON5_MASK = 1 << 12,
  74. /* The next few modifiers are used by XKB, so we skip to the end.
  75. * Bits 15 - 23 are currently unused. Bit 29 is used internally.
  76. */
  77. /* ibus mask */
  78. IBUS_HANDLED_MASK = 1 << 24,
  79. IBUS_FORWARD_MASK = 1 << 25,
  80. IBUS_IGNORED_MASK = IBUS_FORWARD_MASK,
  81. IBUS_SUPER_MASK = 1 << 26,
  82. IBUS_HYPER_MASK = 1 << 27,
  83. IBUS_META_MASK = 1 << 28,
  84. IBUS_RELEASE_MASK = 1 << 30,
  85. IBUS_MODIFIER_MASK = 0x5f001fff
  86. } IBusModifierType;
  87. static uint32_t
  88. ibus_key_state_from_glfw(unsigned int glfw_modifiers, int action) {
  89. uint32_t ans = action == GLFW_RELEASE ? IBUS_RELEASE_MASK : 0;
  90. #define M(g, i) if(glfw_modifiers & GLFW_MOD_##g) ans |= i
  91. M(SHIFT, IBUS_SHIFT_MASK);
  92. M(CAPS_LOCK, IBUS_LOCK_MASK);
  93. M(CONTROL, IBUS_CONTROL_MASK);
  94. M(ALT, IBUS_MOD1_MASK);
  95. M(NUM_LOCK, IBUS_MOD2_MASK);
  96. M(SUPER, IBUS_MOD4_MASK);
  97. /* To do: figure out how to get super/hyper/meta */
  98. #undef M
  99. return ans;
  100. }
  101. static unsigned int
  102. glfw_modifiers_from_ibus_state(uint32_t ibus_key_state) {
  103. unsigned int ans = 0;
  104. #define M(g, i) if(ibus_key_state & i) ans |= GLFW_MOD_##g
  105. M(SHIFT, IBUS_SHIFT_MASK);
  106. M(CAPS_LOCK, IBUS_LOCK_MASK);
  107. M(CONTROL, IBUS_CONTROL_MASK);
  108. M(ALT, IBUS_MOD1_MASK);
  109. M(NUM_LOCK, IBUS_MOD2_MASK);
  110. M(SUPER, IBUS_MOD4_MASK);
  111. /* To do: figure out how to get super/hyper/meta */
  112. #undef M
  113. return ans;
  114. }
  115. static bool
  116. test_env_var(const char *name, const char *val) {
  117. const char *q = getenv(name);
  118. return (q && strcmp(q, val) == 0) ? true : false;
  119. }
  120. static size_t
  121. GLFW_MIN(size_t a, size_t b) {
  122. return a < b ? a : b;
  123. }
  124. static const char*
  125. get_ibus_text_from_message(DBusMessage *msg) {
  126. /* The message structure is (from dbus-monitor)
  127. variant struct {
  128. string "IBusText"
  129. array [
  130. ]
  131. string "ash "
  132. variant struct {
  133. string "IBusAttrList"
  134. array [
  135. ]
  136. array [
  137. ]
  138. }
  139. }
  140. */
  141. const char *text = NULL;
  142. const char *struct_id = NULL;
  143. DBusMessageIter iter, sub1, sub2;
  144. dbus_message_iter_init(msg, &iter);
  145. if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) return NULL;
  146. dbus_message_iter_recurse(&iter, &sub1);
  147. if (dbus_message_iter_get_arg_type(&sub1) != DBUS_TYPE_STRUCT) return NULL;
  148. dbus_message_iter_recurse(&sub1, &sub2);
  149. if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) return NULL;
  150. dbus_message_iter_get_basic(&sub2, &struct_id);
  151. if (!struct_id || strncmp(struct_id, "IBusText", sizeof("IBusText")) != 0) return NULL;
  152. dbus_message_iter_next(&sub2);
  153. dbus_message_iter_next(&sub2);
  154. if (dbus_message_iter_get_arg_type(&sub2) != DBUS_TYPE_STRING) return NULL;
  155. dbus_message_iter_get_basic(&sub2, &text);
  156. return text;
  157. }
  158. static void
  159. handle_ibus_forward_key_event(DBusMessage *msg) {
  160. uint32_t keysym, keycode, state;
  161. DBusMessageIter iter;
  162. dbus_message_iter_init(msg, &iter);
  163. if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
  164. dbus_message_iter_get_basic(&iter, &keysym);
  165. dbus_message_iter_next(&iter);
  166. if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
  167. dbus_message_iter_get_basic(&iter, &keycode);
  168. dbus_message_iter_next(&iter);
  169. if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32) return;
  170. dbus_message_iter_get_basic(&iter, &state);
  171. int mods = glfw_modifiers_from_ibus_state(state);
  172. debug("IBUS: ForwardKeyEvent: keysym=%x, keycode=%x, state=%x, glfw_mods=%x\n", keysym, keycode, state, mods);
  173. glfw_xkb_forwarded_key_from_ime(keysym, mods);
  174. }
  175. static void
  176. send_text(const char *text, GLFWIMEState ime_state) {
  177. _GLFWwindow *w = _glfwFocusedWindow();
  178. if (w && w->callbacks.keyboard) {
  179. GLFWkeyevent fake_ev = {.action = GLFW_PRESS};
  180. fake_ev.text = text;
  181. fake_ev.ime_state = ime_state;
  182. w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
  183. }
  184. }
  185. // Connection handling {{{
  186. static DBusHandlerResult
  187. message_handler(DBusConnection *conn UNUSED, DBusMessage *msg, void *user_data) {
  188. // To monitor signals from IBUS, use
  189. //  dbus-monitor --address `ibus address` "type='signal',interface='org.freedesktop.IBus.InputContext'"
  190. _GLFWIBUSData *ibus = (_GLFWIBUSData*)user_data;
  191. (void)ibus;
  192. const char *text;
  193. switch(glfw_dbus_match_signal(msg, IBUS_INPUT_INTERFACE, "CommitText", "UpdatePreeditText", "HidePreeditText", "ShowPreeditText", "ForwardKeyEvent", NULL)) {
  194. case 0:
  195. text = get_ibus_text_from_message(msg);
  196. debug("IBUS: CommitText: '%s'\n", text ? text : "(nil)");
  197. send_text(text, GLFW_IME_COMMIT_TEXT);
  198. break;
  199. case 1:
  200. text = get_ibus_text_from_message(msg);
  201. debug("IBUS: UpdatePreeditText: '%s'\n", text ? text : "(nil)");
  202. send_text(text, GLFW_IME_PREEDIT_CHANGED);
  203. break;
  204. case 2:
  205. debug("IBUS: HidePreeditText\n");
  206. send_text("", GLFW_IME_PREEDIT_CHANGED);
  207. break;
  208. case 3:
  209. debug("IBUS: ShowPreeditText\n");
  210. break;
  211. case 4:
  212. handle_ibus_forward_key_event(msg);
  213. break;
  214. }
  215. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  216. }
  217. static DBusHandlerResult
  218. ibus_on_owner_change(DBusConnection* conn UNUSED, DBusMessage* msg, void* user_data) {
  219. if (dbus_message_is_signal(msg, "org.freedesktop.DBus", "NameOwnerChanged")) {
  220. const char* name;
  221. const char* old_owner;
  222. const char* new_owner;
  223. if (!dbus_message_get_args(msg, NULL,
  224. DBUS_TYPE_STRING, &name,
  225. DBUS_TYPE_STRING, &old_owner,
  226. DBUS_TYPE_STRING, &new_owner,
  227. DBUS_TYPE_INVALID
  228. )) {
  229. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  230. }
  231. if (strcmp(name, "org.freedesktop.IBus") != 0) {
  232. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  233. }
  234. _GLFWIBUSData* ibus = (_GLFWIBUSData*) user_data;
  235. ibus->name_owner_changed = true;
  236. return DBUS_HANDLER_RESULT_HANDLED;
  237. }
  238. return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
  239. }
  240. static const char*
  241. get_ibus_address_file_name(void) {
  242. const char *addr;
  243. static char ans[PATH_MAX];
  244. static char display[64] = {0};
  245. addr = getenv("IBUS_ADDRESS");
  246. int offset = 0;
  247. if (addr && addr[0]) {
  248. memcpy(ans, addr, GLFW_MIN(strlen(addr), sizeof(ans)));
  249. return ans;
  250. }
  251. const char* disp_num = NULL;
  252. const char *host = "unix";
  253. // See https://github.com/ibus/ibus/commit/8ce25208c3f4adfd290a032c6aa739d2b7580eb1 for why we need this dance.
  254. const char *de = getenv("WAYLAND_DISPLAY");
  255. if (de) {
  256. disp_num = de;
  257. } else {
  258. const char *de = getenv("DISPLAY");
  259. if (!de || !de[0]) de = ":0.0";
  260. strncpy(display, de, sizeof(display) - 1);
  261. char *dnum = strrchr(display, ':');
  262. if (!dnum) {
  263. _glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as DISPLAY env var has no colon");
  264. return NULL;
  265. }
  266. char *screen_num = strrchr(display, '.');
  267. *dnum = 0;
  268. dnum++;
  269. if (screen_num) *screen_num = 0;
  270. if (*display) host = display;
  271. disp_num = dnum;
  272. }
  273. memset(ans, 0, sizeof(ans));
  274. const char *conf_env = getenv("XDG_CONFIG_HOME");
  275. if (conf_env && conf_env[0]) {
  276. offset = snprintf(ans, sizeof(ans), "%s", conf_env);
  277. } else {
  278. conf_env = getenv("HOME");
  279. if (!conf_env || !conf_env[0]) {
  280. _glfwInputError(GLFW_PLATFORM_ERROR, "Could not get IBUS address file name as no HOME env var is set");
  281. return NULL;
  282. }
  283. offset = snprintf(ans, sizeof(ans), "%s/.config", conf_env);
  284. }
  285. DBusError err;
  286. char *key = dbus_try_get_local_machine_id(&err);
  287. if (!key) {
  288. _glfwInputError(GLFW_PLATFORM_ERROR, "Cannot connect to IBUS as could not get DBUS local machine id with error %s: %s", err.name ? err.name : "", err.message ? err.message : "");
  289. return NULL;
  290. }
  291. snprintf(ans + offset, sizeof(ans) - offset, "/ibus/bus/%s-%s-%s", key, host, disp_num);
  292. dbus_free(key);
  293. return ans;
  294. }
  295. static bool
  296. read_ibus_address(_GLFWIBUSData *ibus) {
  297. static char buf[1024];
  298. struct stat s;
  299. FILE *addr_file = fopen(ibus->address_file_name, "r");
  300. if (!addr_file) {
  301. _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to open IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
  302. return false;
  303. }
  304. int stat_result = fstat(fileno(addr_file), &s);
  305. bool found = false;
  306. while (fgets(buf, sizeof(buf), addr_file)) {
  307. if (strncmp(buf, "IBUS_ADDRESS=", sizeof("IBUS_ADDRESS=")-1) == 0) {
  308. size_t sz = strlen(buf);
  309. if (buf[sz-1] == '\n') buf[sz-1] = 0;
  310. if (buf[sz-2] == '\r') buf[sz-2] = 0;
  311. found = true;
  312. break;
  313. }
  314. }
  315. fclose(addr_file); addr_file = NULL;
  316. if (stat_result != 0) {
  317. _glfwInputError(GLFW_PLATFORM_ERROR, "Failed to stat IBUS address file: %s with error: %s", ibus->address_file_name, strerror(errno));
  318. return false;
  319. }
  320. ibus->address_file_mtime = s.st_mtime;
  321. if (found) {
  322. free((void*)ibus->address);
  323. ibus->address = _glfw_strdup(buf + sizeof("IBUS_ADDRESS=") - 1);
  324. return true;
  325. }
  326. _glfwInputError(GLFW_PLATFORM_ERROR, "Could not find IBUS_ADDRESS in %s", ibus->address_file_name);
  327. return false;
  328. }
  329. void
  330. input_context_created(DBusMessage *msg, const char* errmsg, void *data) {
  331. if (errmsg) {
  332. _glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to create input context with error: %s", errmsg);
  333. return;
  334. }
  335. const char *path = NULL;
  336. if (!glfw_dbus_get_args(msg, "Failed to get IBUS context path from reply", DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) return;
  337. _GLFWIBUSData *ibus = (_GLFWIBUSData*)data;
  338. free((void*)ibus->input_ctx_path);
  339. ibus->input_ctx_path = _glfw_strdup(path);
  340. if (!ibus->input_ctx_path) return;
  341. dbus_bus_add_match(ibus->conn, "type='signal',interface='org.freedesktop.DBus', member='NameOwnerChanged'", NULL);
  342. dbus_connection_add_filter(ibus->conn, ibus_on_owner_change, ibus, free);
  343. dbus_bus_add_match(ibus->conn, "type='signal',interface='org.freedesktop.IBus.InputContext'", NULL);
  344. DBusObjectPathVTable ibus_vtable = {.message_function = message_handler};
  345. dbus_connection_try_register_object_path(ibus->conn, ibus->input_ctx_path, &ibus_vtable, ibus, NULL);
  346. enum Capabilities caps = IBUS_CAP_FOCUS | IBUS_CAP_PREEDIT_TEXT;
  347. if (!glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCapabilities", DBUS_TYPE_UINT32, &caps, DBUS_TYPE_INVALID)) return;
  348. ibus->ok = true;
  349. glfw_ibus_set_focused(ibus, _glfwFocusedWindow() != NULL);
  350. glfw_ibus_set_cursor_geometry(ibus, 0, 0, 0, 0);
  351. debug("Connected to IBUS daemon for IME input management\n");
  352. }
  353. static bool
  354. setup_connection(_GLFWIBUSData *ibus) {
  355. const char *client_name = "GLFW_Application";
  356. const char *address_file_name = get_ibus_address_file_name();
  357. ibus->ok = false;
  358. if (!address_file_name) return false;
  359. free((void*)ibus->address_file_name);
  360. ibus->address_file_name = _glfw_strdup(address_file_name);
  361. if (!read_ibus_address(ibus)) return false;
  362. if (ibus->conn) {
  363. glfw_dbus_close_connection(ibus->conn);
  364. ibus->conn = NULL;
  365. }
  366. debug("Connecting to IBUS daemon @ %s for IME input management\n", ibus->address);
  367. ibus->conn = glfw_dbus_connect_to(ibus->address, "Failed to connect to the IBUS daemon, with error", "ibus", true);
  368. if (!ibus->conn) return false;
  369. free((void*)ibus->input_ctx_path); ibus->input_ctx_path = NULL;
  370. if (!glfw_dbus_call_method_with_reply(
  371. ibus->conn, IBUS_SERVICE, IBUS_PATH, IBUS_INTERFACE, "CreateInputContext", DBUS_TIMEOUT_USE_DEFAULT, input_context_created, ibus,
  372. DBUS_TYPE_STRING, &client_name, DBUS_TYPE_INVALID)) {
  373. return false;
  374. }
  375. return true;
  376. }
  377. void
  378. glfw_connect_to_ibus(_GLFWIBUSData *ibus) {
  379. if (ibus->inited) return;
  380. if (!test_env_var("GLFW_IM_MODULE", "ibus")) return;
  381. ibus->inited = true;
  382. ibus->name_owner_changed = false;
  383. setup_connection(ibus);
  384. }
  385. void
  386. glfw_ibus_terminate(_GLFWIBUSData *ibus) {
  387. if (ibus->conn) {
  388. glfw_dbus_close_connection(ibus->conn);
  389. ibus->conn = NULL;
  390. }
  391. #define F(x) if (ibus->x) { free((void*)ibus->x); ibus->x = NULL; }
  392. F(input_ctx_path);
  393. F(address);
  394. F(address_file_name);
  395. #undef F
  396. ibus->ok = false;
  397. }
  398. static bool
  399. check_connection(_GLFWIBUSData *ibus) {
  400. if (!ibus->inited) return false;
  401. if (ibus->conn && dbus_connection_get_is_connected(ibus->conn) && !ibus->name_owner_changed) return ibus->ok;
  402. struct stat s;
  403. ibus->name_owner_changed = false;
  404. if (stat(ibus->address_file_name, &s) != 0 || s.st_mtime != ibus->address_file_mtime) {
  405. if (!read_ibus_address(ibus)) return false;
  406. return setup_connection(ibus);
  407. }
  408. return false;
  409. }
  410. void
  411. glfw_ibus_dispatch(_GLFWIBUSData *ibus) {
  412. if (ibus->conn) glfw_dbus_dispatch(ibus->conn);
  413. }
  414. // }}}
  415. static void
  416. simple_message(_GLFWIBUSData *ibus, const char *method) {
  417. if (check_connection(ibus)) {
  418. glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, method, DBUS_TYPE_INVALID);
  419. }
  420. }
  421. void
  422. glfw_ibus_set_focused(_GLFWIBUSData *ibus, bool focused) {
  423. simple_message(ibus, focused ? "FocusIn" : "FocusOut");
  424. }
  425. void
  426. glfw_ibus_set_cursor_geometry(_GLFWIBUSData *ibus, int x, int y, int w, int h) {
  427. if (check_connection(ibus)) {
  428. glfw_dbus_call_method_no_reply(ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "SetCursorLocation",
  429. DBUS_TYPE_INT32, &x, DBUS_TYPE_INT32, &y, DBUS_TYPE_INT32, &w, DBUS_TYPE_INT32, &h, DBUS_TYPE_INVALID);
  430. }
  431. }
  432. void
  433. key_event_processed(DBusMessage *msg, const char* errmsg, void *data) {
  434. uint32_t handled = 0;
  435. _GLFWIBUSKeyEvent *ev = (_GLFWIBUSKeyEvent*)data;
  436. // Restore key's text from the text embedded in the structure.
  437. ev->glfw_ev.text = ev->__embedded_text;
  438. bool is_release = ev->glfw_ev.action == GLFW_RELEASE;
  439. bool failed = false;
  440. if (errmsg) {
  441. _glfwInputError(GLFW_PLATFORM_ERROR, "IBUS: Failed to process key with error: %s", errmsg);
  442. failed = true;
  443. } else {
  444. glfw_dbus_get_args(msg, "Failed to get IBUS handled key from reply", DBUS_TYPE_BOOLEAN, &handled, DBUS_TYPE_INVALID);
  445. debug("IBUS processed native_key: 0x%x release: %d handled: %u\n", ev->glfw_ev.native_key, is_release, handled);
  446. }
  447. glfw_xkb_key_from_ime(ev, handled ? true : false, failed);
  448. free(ev);
  449. }
  450. bool
  451. ibus_process_key(const _GLFWIBUSKeyEvent *ev_, _GLFWIBUSData *ibus) {
  452. if (!check_connection(ibus)) return false;
  453. _GLFWIBUSKeyEvent *ev = calloc(1, sizeof(_GLFWIBUSKeyEvent));
  454. if (!ev) return false;
  455. memcpy(ev, ev_, sizeof(_GLFWIBUSKeyEvent));
  456. // Put the key's text in a field IN the structure, for proper serialization.
  457. if (ev->glfw_ev.text) strncpy(ev->__embedded_text, ev->glfw_ev.text, sizeof(ev->__embedded_text) - 1);
  458. ev->glfw_ev.text = NULL;
  459. uint32_t state = ibus_key_state_from_glfw(ev->glfw_ev.mods, ev->glfw_ev.action);
  460. if (!glfw_dbus_call_method_with_reply(
  461. ibus->conn, IBUS_SERVICE, ibus->input_ctx_path, IBUS_INPUT_INTERFACE, "ProcessKeyEvent",
  462. 3000, key_event_processed, ev,
  463. DBUS_TYPE_UINT32, &ev->ibus_keysym, DBUS_TYPE_UINT32, &ev->ibus_keycode, DBUS_TYPE_UINT32,
  464. &state, DBUS_TYPE_INVALID)) {
  465. free(ev);
  466. return false;
  467. }
  468. return true;
  469. }