diff options
Diffstat (limited to 'platform/linuxbsd/wayland/wayland_thread.cpp')
-rw-r--r-- | platform/linuxbsd/wayland/wayland_thread.cpp | 291 |
1 files changed, 240 insertions, 51 deletions
diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index aabf1abdda..ab13105d18 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(); @@ -927,7 +968,6 @@ void WaylandThread::_frame_wl_callback_on_done(void *data, struct wl_callback *w ws->frame_callback = wl_surface_frame(ws->wl_surface), wl_callback_add_listener(ws->frame_callback, &frame_wl_callback_listener, ws); - wl_surface_commit(ws->wl_surface); if (ws->wl_surface && ws->buffer_scale_changed) { // NOTE: We're only now setting the buffer scale as the idea is to get this @@ -939,11 +979,6 @@ void WaylandThread::_frame_wl_callback_on_done(void *data, struct wl_callback *w // rendering if needed. wl_surface_set_buffer_scale(ws->wl_surface, window_state_get_preferred_buffer_scale(ws)); } - - // NOTE: Remember to set here also other buffer-dependent states (e.g. opaque - // region) if used, to be as close as possible to an atomic surface update. - // Ideally we'd only have one surface commit, but it's not really doable given - // the current state of things. } void WaylandThread::_wl_surface_on_leave(void *data, struct wl_surface *wl_surface, struct wl_output *wl_output) { @@ -1228,23 +1263,25 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat // Pointer handling. if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - ss->cursor_surface = wl_compositor_create_surface(ss->registry->wl_compositor); - wl_surface_commit(ss->cursor_surface); + if (!ss->wl_pointer) { + ss->cursor_surface = wl_compositor_create_surface(ss->registry->wl_compositor); + wl_surface_commit(ss->cursor_surface); - ss->wl_pointer = wl_seat_get_pointer(wl_seat); - wl_pointer_add_listener(ss->wl_pointer, &wl_pointer_listener, ss); + ss->wl_pointer = wl_seat_get_pointer(wl_seat); + wl_pointer_add_listener(ss->wl_pointer, &wl_pointer_listener, ss); - if (ss->registry->wp_relative_pointer_manager) { - ss->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(ss->registry->wp_relative_pointer_manager, ss->wl_pointer); - zwp_relative_pointer_v1_add_listener(ss->wp_relative_pointer, &wp_relative_pointer_listener, ss); - } + if (ss->registry->wp_relative_pointer_manager) { + ss->wp_relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(ss->registry->wp_relative_pointer_manager, ss->wl_pointer); + zwp_relative_pointer_v1_add_listener(ss->wp_relative_pointer, &wp_relative_pointer_listener, ss); + } - if (ss->registry->wp_pointer_gestures) { - ss->wp_pointer_gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(ss->registry->wp_pointer_gestures, ss->wl_pointer); - zwp_pointer_gesture_pinch_v1_add_listener(ss->wp_pointer_gesture_pinch, &wp_pointer_gesture_pinch_listener, ss); - } + if (ss->registry->wp_pointer_gestures) { + ss->wp_pointer_gesture_pinch = zwp_pointer_gestures_v1_get_pinch_gesture(ss->registry->wp_pointer_gestures, ss->wl_pointer); + zwp_pointer_gesture_pinch_v1_add_listener(ss->wp_pointer_gesture_pinch, &wp_pointer_gesture_pinch_listener, ss); + } - // TODO: Constrain new pointers if the global mouse mode is constrained. + // TODO: Constrain new pointers if the global mouse mode is constrained. + } } else { if (ss->cursor_frame_callback) { // Just in case. I got bitten by weird race-like conditions already. @@ -1282,11 +1319,13 @@ void WaylandThread::_wl_seat_on_capabilities(void *data, struct wl_seat *wl_seat // Keyboard handling. if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - ss->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - ERR_FAIL_NULL(ss->xkb_context); + if (!ss->wl_keyboard) { + ss->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + ERR_FAIL_NULL(ss->xkb_context); - ss->wl_keyboard = wl_seat_get_keyboard(wl_seat); - wl_keyboard_add_listener(ss->wl_keyboard, &wl_keyboard_listener, ss); + ss->wl_keyboard = wl_seat_get_keyboard(wl_seat); + wl_keyboard_add_listener(ss->wl_keyboard, &wl_keyboard_listener, ss); + } } else { if (ss->xkb_context) { xkb_context_unref(ss->xkb_context); @@ -1377,10 +1416,10 @@ void WaylandThread::_wl_pointer_on_motion(void *data, struct wl_pointer *wl_poin PointerData &pd = ss->pointer_data_buffer; // TODO: Scale only when sending the Wayland message. - pd.position.x = wl_fixed_to_int(surface_x); - pd.position.y = wl_fixed_to_int(surface_y); + pd.position.x = wl_fixed_to_double(surface_x); + pd.position.y = wl_fixed_to_double(surface_y); - pd.position = scale_vector2i(pd.position, window_state_get_scale_factor(ws)); + pd.position *= window_state_get_scale_factor(ws); pd.motion_time = time; } @@ -1493,7 +1532,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point mm->set_position(pd.position); mm->set_global_position(pd.position); - Vector2i pos_delta = pd.position - old_pd.position; + Vector2 pos_delta = pd.position - old_pd.position; if (old_pd.relative_motion_time != pd.relative_motion_time) { uint32_t time_delta = pd.relative_motion_time - old_pd.relative_motion_time; @@ -1610,7 +1649,7 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point // We have to set the last position pressed here as we can't take for // granted what the individual events might have seen due to them not having - // a garaunteed order. + // a guaranteed order. if (mb->is_pressed()) { pd.last_pressed_position = pd.position; } @@ -2012,11 +2051,21 @@ void WaylandThread::_wp_relative_pointer_on_relative_motion(void *data, struct z SeatState *ss = (SeatState *)data; ERR_FAIL_NULL(ss); + if (!ss->pointed_surface) { + // We're probably on a decoration or some other third-party thing. + return; + } + PointerData &pd = ss->pointer_data_buffer; + WindowState *ws = wl_surface_get_window_state(ss->pointed_surface); + ERR_FAIL_NULL(ws); + pd.relative_motion.x = wl_fixed_to_double(dx); pd.relative_motion.y = wl_fixed_to_double(dy); + pd.relative_motion *= window_state_get_scale_factor(ws); + pd.relative_motion_time = uptime_lo; } @@ -2209,13 +2258,11 @@ void WaylandThread::_wp_tablet_tool_on_done(void *data, struct zwp_tablet_tool_v void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } SeatState *ss = wl_seat_get_seat_state(ts->wl_seat); - if (!ss) { return; } @@ -2235,14 +2282,17 @@ void WaylandThread::_wp_tablet_tool_on_removed(void *data, struct zwp_tablet_too } void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, struct zwp_tablet_v2 *tablet, struct wl_surface *surface) { - TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); + if (!surface || !wl_proxy_is_godot((struct wl_proxy *)surface)) { + // We're probably on a decoration or something. + return; + } + TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); if (!ts) { return; } SeatState *ss = wl_seat_get_seat_state(ts->wl_seat); - if (!ss) { return; } @@ -2264,13 +2314,12 @@ void WaylandThread::_wp_tablet_tool_on_proximity_in(void *data, struct zwp_table void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - - if (!ts) { + if (!ts || !ts->data_pending.proximal_surface) { + // Not our stuff, we don't care. return; } SeatState *ss = wl_seat_get_seat_state(ts->wl_seat); - if (!ss) { return; } @@ -2291,7 +2340,6 @@ void WaylandThread::_wp_tablet_tool_on_proximity_out(void *data, struct zwp_tabl void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } @@ -2309,7 +2357,6 @@ void WaylandThread::_wp_tablet_tool_on_down(void *data, struct zwp_tablet_tool_v void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } @@ -2325,11 +2372,15 @@ void WaylandThread::_wp_tablet_tool_on_up(void *data, struct zwp_tablet_tool_v2 void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t x, wl_fixed_t y) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } + if (!ts->data_pending.proximal_surface) { + // We're probably on a decoration or some other third-party thing. + return; + } + WindowState *ws = wl_surface_get_window_state(ts->data_pending.proximal_surface); ERR_FAIL_NULL(ws); @@ -2337,16 +2388,15 @@ void WaylandThread::_wp_tablet_tool_on_motion(void *data, struct zwp_tablet_tool double scale_factor = window_state_get_scale_factor(ws); - td.position.x = wl_fixed_to_int(x); - td.position.y = wl_fixed_to_int(y); - td.position = scale_vector2i(td.position, scale_factor); + td.position.x = wl_fixed_to_double(x); + td.position.y = wl_fixed_to_double(y); + td.position *= scale_factor; td.motion_time = OS::get_singleton()->get_ticks_msec(); } void WaylandThread::_wp_tablet_tool_on_pressure(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t pressure) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } @@ -2360,7 +2410,6 @@ void WaylandThread::_wp_tablet_tool_on_distance(void *data, struct zwp_tablet_to void WaylandThread::_wp_tablet_tool_on_tilt(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, wl_fixed_t tilt_x, wl_fixed_t tilt_y) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } @@ -2385,7 +2434,6 @@ void WaylandThread::_wp_tablet_tool_on_wheel(void *data, struct zwp_tablet_tool_ void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t serial, uint32_t button, uint32_t state) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } @@ -2421,13 +2469,11 @@ void WaylandThread::_wp_tablet_tool_on_button(void *data, struct zwp_tablet_tool void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_v2 *wp_tablet_tool_v2, uint32_t time) { TabletToolState *ts = wp_tablet_tool_get_state(wp_tablet_tool_v2); - if (!ts) { return; } SeatState *ss = wl_seat_get_seat_state(ts->wl_seat); - if (!ss) { return; } @@ -2474,7 +2520,7 @@ void WaylandThread::_wp_tablet_tool_on_frame(void *data, struct zwp_tablet_tool_ mm->set_relative(td.position - old_td.position); mm->set_relative_screen_position(mm->get_relative()); - Vector2i pos_delta = td.position - old_td.position; + Vector2 pos_delta = td.position - old_td.position; uint32_t time_delta = td.motion_time - old_td.motion_time; mm->set_velocity((Vector2)pos_delta / time_delta); @@ -2535,6 +2581,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); @@ -3088,15 +3246,13 @@ void WaylandThread::window_create(DisplayServer::WindowID p_window_id, int p_wid ws.frame_callback = wl_surface_frame(ws.wl_surface); wl_callback_add_listener(ws.frame_callback, &frame_wl_callback_listener, &ws); - // NOTE: This commit is only called once to start the whole frame callback - // "loop". - wl_surface_commit(ws.wl_surface); - if (registry.xdg_exporter) { ws.xdg_exported = zxdg_exporter_v1_export(registry.xdg_exporter, ws.wl_surface); zxdg_exported_v1_add_listener(ws.xdg_exported, &xdg_exported_listener, &ws); } + wl_surface_commit(ws.wl_surface); + // Wait for the surface to be configured before continuing. wl_display_roundtrip(wl_display); } @@ -3729,6 +3885,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); @@ -3938,6 +4123,10 @@ void WaylandThread::primary_set_text(const String &p_text) { wl_display_roundtrip(wl_display); } +void WaylandThread::commit_surfaces() { + wl_surface_commit(main_window.wl_surface); +} + void WaylandThread::set_frame() { frame = true; } |