summaryrefslogtreecommitdiffstats
path: root/platform/linuxbsd/wayland/wayland_thread.cpp
diff options
context:
space:
mode:
authorbruvzg <7645683+bruvzg@users.noreply.github.com>2024-06-11 12:24:54 +0300
committerbruvzg <7645683+bruvzg@users.noreply.github.com>2024-06-11 12:24:54 +0300
commitbe25e60f619dc38e4e8b4b4fa1da777fd0a0ec6b (patch)
tree5852f73198e8df3bf98801ea1d716e92ef881283 /platform/linuxbsd/wayland/wayland_thread.cpp
parent62a056aa56ca4471c035e43741c88e4a22d81802 (diff)
downloadredot-engine-be25e60f619dc38e4e8b4b4fa1da777fd0a0ec6b.tar.gz
[Wayland] Implement IME support.
Diffstat (limited to 'platform/linuxbsd/wayland/wayland_thread.cpp')
-rw-r--r--platform/linuxbsd/wayland/wayland_thread.cpp182
1 files changed, 182 insertions, 0 deletions
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp
index aabf1abdda..63a8db07df 100644
--- a/platform/linuxbsd/wayland/wayland_thread.cpp
+++ b/platform/linuxbsd/wayland/wayland_thread.cpp
@@ -448,6 +448,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
zwp_tablet_seat_v2_add_listener(ss->wp_tablet_seat, &wp_tablet_seat_listener, ss);
}
+ if (!ss->wp_text_input && registry->wp_text_input_manager) {
+ // IME.
+ ss->wp_text_input = zwp_text_input_manager_v3_get_text_input(registry->wp_text_input_manager, wl_seat);
+ zwp_text_input_v3_add_listener(ss->wp_text_input, &wp_text_input_listener, ss);
+ }
+
registry->wl_seats.push_back(wl_seat);
wl_seat_add_listener(wl_seat, &wl_seat_listener, ss);
@@ -547,6 +553,22 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
return;
}
+
+ if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
+ registry->wp_text_input_manager = (struct zwp_text_input_manager_v3 *)wl_registry_bind(wl_registry, name, &zwp_text_input_manager_v3_interface, 1);
+ registry->wp_text_input_manager_name = name;
+
+ // This global creates some seat data. Let's do that for the ones already available.
+ for (struct wl_seat *wl_seat : registry->wl_seats) {
+ SeatState *ss = wl_seat_get_seat_state(wl_seat);
+ ERR_FAIL_NULL(ss);
+
+ ss->wp_text_input = zwp_text_input_manager_v3_get_text_input(registry->wp_text_input_manager, wl_seat);
+ zwp_text_input_v3_add_listener(ss->wp_text_input, &wp_text_input_listener, ss);
+ }
+
+ return;
+ }
}
void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name) {
@@ -825,6 +847,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
+ if (name == registry->wp_text_input_manager_name) {
+ if (registry->wp_text_input_manager) {
+ zwp_text_input_manager_v3_destroy(registry->wp_text_input_manager);
+ registry->wp_text_input_manager = nullptr;
+ }
+
+ registry->wp_text_input_manager_name = 0;
+
+ for (struct wl_seat *wl_seat : registry->wl_seats) {
+ SeatState *ss = wl_seat_get_seat_state(wl_seat);
+ ERR_FAIL_NULL(ss);
+
+ zwp_text_input_v3_destroy(ss->wp_text_input);
+ ss->wp_text_input = nullptr;
+ }
+
+ return;
+ }
+
{
// Iterate through all of the seats to find if any got removed.
List<struct wl_seat *>::Element *E = registry->wl_seats.front();
@@ -2535,6 +2576,118 @@ void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_
old_td = td;
}
+void WaylandThread::_wp_text_input_on_enter(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface) {
+ SeatState *ss = (SeatState *)data;
+ if (!ss) {
+ return;
+ }
+
+ ss->ime_enabled = true;
+}
+
+void WaylandThread::_wp_text_input_on_leave(void *data, struct zwp_text_input_v3 *wp_text_input_v3, struct wl_surface *surface) {
+ SeatState *ss = (SeatState *)data;
+ if (!ss) {
+ return;
+ }
+
+ ss->ime_enabled = false;
+ ss->ime_active = false;
+ ss->ime_text = String();
+ ss->ime_text_commit = String();
+ ss->ime_cursor = Vector2i();
+
+ Ref<IMEUpdateEventMessage> msg;
+ msg.instantiate();
+ msg->text = String();
+ msg->selection = Vector2i();
+ ss->wayland_thread->push_message(msg);
+}
+
+void WaylandThread::_wp_text_input_on_preedit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text, int32_t cursor_begin, int32_t cursor_end) {
+ SeatState *ss = (SeatState *)data;
+ if (!ss) {
+ return;
+ }
+
+ ss->ime_text = String::utf8(text);
+
+ // Convert cursor positions from UTF-8 to UTF-32 offset.
+ int32_t cursor_begin_utf32 = 0;
+ int32_t cursor_end_utf32 = 0;
+ for (int i = 0; i < ss->ime_text.length(); i++) {
+ uint32_t c = ss->ime_text[i];
+ if (c <= 0x7f) { // 7 bits.
+ cursor_begin -= 1;
+ cursor_end -= 1;
+ } else if (c <= 0x7ff) { // 11 bits
+ cursor_begin -= 2;
+ cursor_end -= 2;
+ } else if (c <= 0xffff) { // 16 bits
+ cursor_begin -= 3;
+ cursor_end -= 3;
+ } else if (c <= 0x001fffff) { // 21 bits
+ cursor_begin -= 4;
+ cursor_end -= 4;
+ } else if (c <= 0x03ffffff) { // 26 bits
+ cursor_begin -= 5;
+ cursor_end -= 5;
+ } else if (c <= 0x7fffffff) { // 31 bits
+ cursor_begin -= 6;
+ cursor_end -= 6;
+ } else {
+ cursor_begin -= 1;
+ cursor_end -= 1;
+ }
+ if (cursor_begin == 0) {
+ cursor_begin_utf32 = i + 1;
+ }
+ if (cursor_end == 0) {
+ cursor_end_utf32 = i + 1;
+ }
+ if (cursor_begin <= 0 && cursor_end <= 0) {
+ break;
+ }
+ }
+ ss->ime_cursor = Vector2i(cursor_begin_utf32, cursor_end_utf32 - cursor_begin_utf32);
+}
+
+void WaylandThread::_wp_text_input_on_commit_string(void *data, struct zwp_text_input_v3 *wp_text_input_v3, const char *text) {
+ SeatState *ss = (SeatState *)data;
+ if (!ss) {
+ return;
+ }
+
+ ss->ime_text_commit = String::utf8(text);
+}
+
+void WaylandThread::_wp_text_input_on_delete_surrounding_text(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t before_length, uint32_t after_length) {
+ // Not implemented.
+}
+
+void WaylandThread::_wp_text_input_on_done(void *data, struct zwp_text_input_v3 *wp_text_input_v3, uint32_t serial) {
+ SeatState *ss = (SeatState *)data;
+ if (!ss) {
+ return;
+ }
+
+ if (!ss->ime_text_commit.is_empty()) {
+ Ref<IMECommitEventMessage> msg;
+ msg.instantiate();
+ msg->text = ss->ime_text_commit;
+ ss->wayland_thread->push_message(msg);
+ } else if (!ss->ime_text.is_empty()) {
+ Ref<IMEUpdateEventMessage> msg;
+ msg.instantiate();
+ msg->text = ss->ime_text;
+ msg->selection = ss->ime_cursor;
+ ss->wayland_thread->push_message(msg);
+ }
+ ss->ime_text = String();
+ ss->ime_text_commit = String();
+ ss->ime_cursor = Vector2i();
+}
+
void WaylandThread::_xdg_activation_token_on_done(void *data, struct xdg_activation_token_v1 *xdg_activation_token, const char *token) {
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
@@ -3729,6 +3882,35 @@ void WaylandThread::cursor_shape_clear_custom_image(DisplayServer::CursorShape p
}
}
+void WaylandThread::window_set_ime_active(const bool p_active, DisplayServer::WindowID p_window_id) {
+ SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
+
+ if (ss && ss->wp_text_input && ss->ime_enabled) {
+ if (p_active) {
+ ss->ime_active = true;
+ zwp_text_input_v3_enable(ss->wp_text_input);
+ zwp_text_input_v3_set_cursor_rectangle(ss->wp_text_input, ss->ime_rect.position.x, ss->ime_rect.position.y, ss->ime_rect.size.x, ss->ime_rect.size.y);
+ } else {
+ ss->ime_active = false;
+ ss->ime_text = String();
+ ss->ime_text_commit = String();
+ ss->ime_cursor = Vector2i();
+ zwp_text_input_v3_disable(ss->wp_text_input);
+ }
+ zwp_text_input_v3_commit(ss->wp_text_input);
+ }
+}
+
+void WaylandThread::window_set_ime_position(const Point2i &p_pos, DisplayServer::WindowID p_window_id) {
+ SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
+
+ if (ss && ss->wp_text_input && ss->ime_enabled) {
+ ss->ime_rect = Rect2i(p_pos, Size2i(1, 10));
+ zwp_text_input_v3_set_cursor_rectangle(ss->wp_text_input, ss->ime_rect.position.x, ss->ime_rect.position.y, ss->ime_rect.size.x, ss->ime_rect.size.y);
+ zwp_text_input_v3_commit(ss->wp_text_input);
+ }
+}
+
int WaylandThread::keyboard_get_layout_count() const {
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);