wl_text_input.c 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. /*
  2. * wl_text_input.c
  3. * Copyright (C) 2021 Kovid Goyal <kovid at kovidgoyal.net>
  4. *
  5. * Distributed under terms of the GPL3 license.
  6. */
  7. #include "wl_text_input.h"
  8. #include "internal.h"
  9. #include "wayland-text-input-unstable-v3-client-protocol.h"
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #define debug(...) if (_glfw.hints.init.debugKeyboard) printf(__VA_ARGS__);
  13. static struct zwp_text_input_v3* text_input;
  14. static struct zwp_text_input_manager_v3* text_input_manager;
  15. static char *pending_pre_edit = NULL;
  16. static char *current_pre_edit = NULL;
  17. static char *pending_commit = NULL;
  18. static int last_cursor_left = 0, last_cursor_top = 0, last_cursor_width = 0, last_cursor_height = 0;
  19. uint32_t commit_serial = 0;
  20. static void commit(void) {
  21. if (text_input) {
  22. zwp_text_input_v3_commit (text_input);
  23. commit_serial++;
  24. }
  25. }
  26. static void
  27. text_input_enter(void *data UNUSED, struct zwp_text_input_v3 *txt_input, struct wl_surface *surface UNUSED) {
  28. debug("text-input: enter event\n");
  29. if (txt_input) {
  30. zwp_text_input_v3_enable(txt_input);
  31. zwp_text_input_v3_set_content_type(txt_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
  32. commit();
  33. }
  34. }
  35. static void
  36. text_input_leave(void *data UNUSED, struct zwp_text_input_v3 *txt_input, struct wl_surface *surface UNUSED) {
  37. debug("text-input: leave event\n");
  38. if (txt_input) {
  39. zwp_text_input_v3_disable(txt_input);
  40. commit();
  41. }
  42. }
  43. static void
  44. send_text(const char *text, GLFWIMEState ime_state) {
  45. _GLFWwindow *w = _glfwFocusedWindow();
  46. if (w && w->callbacks.keyboard) {
  47. GLFWkeyevent fake_ev = {.action = text ? GLFW_PRESS : GLFW_RELEASE};
  48. fake_ev.text = text;
  49. fake_ev.ime_state = ime_state;
  50. w->callbacks.keyboard((GLFWwindow*) w, &fake_ev);
  51. }
  52. }
  53. static void
  54. text_input_preedit_string(
  55. void *data UNUSED,
  56. struct zwp_text_input_v3 *txt_input UNUSED,
  57. const char *text,
  58. int32_t cursor_begin,
  59. int32_t cursor_end
  60. ) {
  61. debug("text-input: preedit_string event: text: %s cursor_begin: %d cursor_end: %d\n", text, cursor_begin, cursor_end);
  62. free(pending_pre_edit);
  63. pending_pre_edit = text ? _glfw_strdup(text) : NULL;
  64. }
  65. static void
  66. text_input_commit_string(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, const char *text) {
  67. debug("text-input: commit_string event: text: %s\n", text);
  68. free(pending_commit);
  69. pending_commit = text ? _glfw_strdup(text) : NULL;
  70. }
  71. static void
  72. text_input_delete_surrounding_text(
  73. void *data UNUSED,
  74. struct zwp_text_input_v3 *txt_input UNUSED,
  75. uint32_t before_length,
  76. uint32_t after_length) {
  77. debug("text-input: delete_surrounding_text event: before_length: %u after_length: %u\n", before_length, after_length);
  78. }
  79. static void
  80. text_input_done(void *data UNUSED, struct zwp_text_input_v3 *txt_input UNUSED, uint32_t serial) {
  81. debug("text-input: done event: serial: %u current_commit_serial: %u\n", serial, commit_serial);
  82. if (serial != commit_serial) {
  83. if (serial > commit_serial) _glfwInputError(GLFW_PLATFORM_ERROR, "Wayland: text_input_done serial mismatch, expected=%u got=%u\n", commit_serial, serial);
  84. return;
  85. }
  86. if ((pending_pre_edit == NULL && current_pre_edit == NULL) ||
  87. (pending_pre_edit && current_pre_edit && strcmp(pending_pre_edit, current_pre_edit) == 0)) {
  88. free(pending_pre_edit); pending_pre_edit = NULL;
  89. } else {
  90. free(current_pre_edit);
  91. current_pre_edit = pending_pre_edit;
  92. pending_pre_edit = NULL;
  93. if (current_pre_edit) {
  94. send_text(current_pre_edit, GLFW_IME_PREEDIT_CHANGED);
  95. } else {
  96. // Clear pre-edit text
  97. send_text(NULL, GLFW_IME_WAYLAND_DONE_EVENT);
  98. }
  99. }
  100. if (pending_commit) {
  101. send_text(pending_commit, GLFW_IME_COMMIT_TEXT);
  102. free(pending_commit); pending_commit = NULL;
  103. }
  104. }
  105. void
  106. _glfwWaylandBindTextInput(struct wl_registry* registry, uint32_t name) {
  107. if (!text_input_manager) text_input_manager = wl_registry_bind(registry, name, &zwp_text_input_manager_v3_interface, 1);
  108. }
  109. void
  110. _glfwWaylandInitTextInput(void) {
  111. static const struct zwp_text_input_v3_listener text_input_listener = {
  112. .enter = text_input_enter,
  113. .leave = text_input_leave,
  114. .preedit_string = text_input_preedit_string,
  115. .commit_string = text_input_commit_string,
  116. .delete_surrounding_text = text_input_delete_surrounding_text,
  117. .done = text_input_done,
  118. };
  119. if (!text_input) {
  120. if (text_input_manager && _glfw.wl.seat) {
  121. text_input = zwp_text_input_manager_v3_get_text_input(
  122. text_input_manager, _glfw.wl.seat);
  123. if (text_input) zwp_text_input_v3_add_listener(text_input, &text_input_listener, NULL);
  124. }
  125. }
  126. }
  127. void
  128. _glfwWaylandDestroyTextInput(void) {
  129. if (text_input) zwp_text_input_v3_destroy(text_input);
  130. if (text_input_manager) zwp_text_input_manager_v3_destroy(text_input_manager);
  131. text_input = NULL; text_input_manager = NULL;
  132. free(pending_pre_edit); pending_pre_edit = NULL;
  133. free(current_pre_edit); current_pre_edit = NULL;
  134. free(pending_commit); pending_commit = NULL;
  135. }
  136. void
  137. _glfwPlatformUpdateIMEState(_GLFWwindow *w, const GLFWIMEUpdateEvent *ev) {
  138. if (!text_input) return;
  139. switch(ev->type) {
  140. case GLFW_IME_UPDATE_FOCUS:
  141. debug("\ntext-input: updating IME focus state, focused: %d\n", ev->focused);
  142. if (ev->focused) {
  143. zwp_text_input_v3_enable(text_input);
  144. zwp_text_input_v3_set_content_type(text_input, ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE, ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL);
  145. } else {
  146. free(pending_pre_edit); pending_pre_edit = NULL;
  147. if (current_pre_edit) {
  148. // Clear pre-edit text
  149. send_text(NULL, GLFW_IME_PREEDIT_CHANGED);
  150. free(current_pre_edit); current_pre_edit = NULL;
  151. }
  152. if (pending_commit) {
  153. free(pending_commit); pending_commit = NULL;
  154. }
  155. zwp_text_input_v3_disable(text_input);
  156. }
  157. commit();
  158. break;
  159. case GLFW_IME_UPDATE_CURSOR_POSITION: {
  160. const float scale = _glfwWaylandWindowScale(w);
  161. #define s(x) (int)roundf((x) / scale)
  162. const int left = s(ev->cursor.left), top = s(ev->cursor.top), width = s(ev->cursor.width), height = s(ev->cursor.height);
  163. #undef s
  164. if (left != last_cursor_left || top != last_cursor_top || width != last_cursor_width || height != last_cursor_height) {
  165. last_cursor_left = left;
  166. last_cursor_top = top;
  167. last_cursor_width = width;
  168. last_cursor_height = height;
  169. debug("\ntext-input: updating cursor position: left=%d top=%d width=%d height=%d\n", left, top, width, height);
  170. zwp_text_input_v3_set_cursor_rectangle(text_input, left, top, width, height);
  171. commit();
  172. }
  173. }
  174. break;
  175. }
  176. }