[Wayland] Implement IME support.

This commit is contained in:
bruvzg
2024-06-11 12:24:54 +03:00
parent 62a056aa56
commit be25e60f61
8 changed files with 751 additions and 4 deletions

View File

@ -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);