feat: updated engine version to 4.4-rc1

This commit is contained in:
Sara 2025-02-23 14:38:14 +01:00
parent ee00efde1f
commit 21ba8e33af
5459 changed files with 1128836 additions and 198305 deletions

View file

@ -32,8 +32,12 @@
#ifdef WAYLAND_ENABLED
// FIXME: Does this cause issues with *BSDs?
#ifdef __FreeBSD__
#include <dev/evdev/input-event-codes.h>
#else
// Assume Linux.
#include <linux/input-event-codes.h>
#endif
// For the actual polling thread.
#include <poll.h>
@ -169,12 +173,13 @@ Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_displa
int fds[2];
if (pipe(fds) == 0) {
// This function expects to return a string, so we can only ask for a MIME of
// "text/plain"
zwp_primary_selection_offer_v1_receive(p_offer, p_mime, fds[1]);
// Wait for the compositor to know about the pipe.
wl_display_roundtrip(p_display);
// NOTE: It's important to just flush and not roundtrip here as we would risk
// running some cleanup event, like for example `wl_data_device::leave`. We're
// going to wait for the message anyways as the read will probably block if
// the compositor doesn't read from the other end of the pipe.
wl_display_flush(p_display);
// Close the write end of the pipe, which we don't need and would otherwise
// just stall our next `read`s.
@ -186,15 +191,34 @@ Vector<uint8_t> WaylandThread::_wp_primary_selection_offer_read(struct wl_displa
return Vector<uint8_t>();
}
// Sets up an `InputEventKey` and returns whether it has any meaningful value.
bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref<InputEventKey> p_event, xkb_keycode_t p_keycode, bool p_pressed) {
// TODO: Handle keys that release multiple symbols?
Key keycode = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(p_ss.xkb_state, p_keycode));
Ref<InputEventKey> WaylandThread::_seat_state_get_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed) {
Ref<InputEventKey> event;
ERR_FAIL_NULL_V(p_ss, event);
Key shifted_key = KeyMappingXKB::get_keycode(xkb_state_key_get_one_sym(p_ss->xkb_state, p_keycode));
Key plain_key = Key::NONE;
// NOTE: xkbcommon's API really encourages to apply the modifier state but we
// only want a "plain" symbol so that we can convert it into a godot keycode.
const xkb_keysym_t *syms = nullptr;
int num_sys = xkb_keymap_key_get_syms_by_level(p_ss->xkb_keymap, p_keycode, p_ss->current_layout_index, 0, &syms);
if (num_sys > 0 && syms) {
plain_key = KeyMappingXKB::get_keycode(syms[0]);
}
Key physical_keycode = KeyMappingXKB::get_scancode(p_keycode);
KeyLocation key_location = KeyMappingXKB::get_location(p_keycode);
uint32_t unicode = xkb_state_key_get_utf32(p_ss->xkb_state, p_keycode);
if (physical_keycode == Key::NONE) {
return false;
Key keycode = Key::NONE;
if ((shifted_key & Key::SPECIAL) != Key::NONE || (plain_key & Key::SPECIAL) != Key::NONE) {
keycode = shifted_key;
}
if (keycode == Key::NONE) {
keycode = plain_key;
}
if (keycode == Key::NONE) {
@ -205,40 +229,71 @@ bool WaylandThread::_seat_state_configure_key_event(SeatState &p_ss, Ref<InputEv
keycode -= 'a' - 'A';
}
p_event->set_window_id(DisplayServer::MAIN_WINDOW_ID);
if (physical_keycode == Key::NONE && keycode == Key::NONE && unicode == 0) {
return event;
}
event.instantiate();
event->set_window_id(DisplayServer::MAIN_WINDOW_ID);
// Set all pressed modifiers.
p_event->set_shift_pressed(p_ss.shift_pressed);
p_event->set_ctrl_pressed(p_ss.ctrl_pressed);
p_event->set_alt_pressed(p_ss.alt_pressed);
p_event->set_meta_pressed(p_ss.meta_pressed);
event->set_shift_pressed(p_ss->shift_pressed);
event->set_ctrl_pressed(p_ss->ctrl_pressed);
event->set_alt_pressed(p_ss->alt_pressed);
event->set_meta_pressed(p_ss->meta_pressed);
p_event->set_pressed(p_pressed);
p_event->set_keycode(keycode);
p_event->set_physical_keycode(physical_keycode);
p_event->set_location(key_location);
uint32_t unicode = xkb_state_key_get_utf32(p_ss.xkb_state, p_keycode);
event->set_pressed(p_pressed);
event->set_keycode(keycode);
event->set_physical_keycode(physical_keycode);
event->set_location(key_location);
if (unicode != 0) {
p_event->set_key_label(fix_key_label(unicode, keycode));
event->set_key_label(fix_key_label(unicode, keycode));
} else {
p_event->set_key_label(keycode);
event->set_key_label(keycode);
}
if (p_pressed) {
p_event->set_unicode(fix_unicode(unicode));
event->set_unicode(fix_unicode(unicode));
}
// Taken from DisplayServerX11.
if (p_event->get_keycode() == Key::BACKTAB) {
if (event->get_keycode() == Key::BACKTAB) {
// Make it consistent across platforms.
p_event->set_keycode(Key::TAB);
p_event->set_physical_keycode(Key::TAB);
p_event->set_shift_pressed(true);
event->set_keycode(Key::TAB);
event->set_physical_keycode(Key::TAB);
event->set_shift_pressed(true);
}
return true;
return event;
}
// NOTE: Due to the nature of the way keys are encoded, there's an ambiguity
// regarding "special" keys. In other words: there's no reliable way of
// switching between a special key and a character key if not marking a
// different Godot keycode, even if we're actually using the same XKB raw
// keycode. This means that, during this switch, the old key will get "stuck",
// as it will never receive a release event. This method returns the necessary
// event to fix this if needed.
Ref<InputEventKey> WaylandThread::_seat_state_get_unstuck_key_event(SeatState *p_ss, xkb_keycode_t p_keycode, bool p_pressed, Key p_key) {
Ref<InputEventKey> event;
if (p_pressed) {
Key *old_key = p_ss->pressed_keycodes.getptr(p_keycode);
if (old_key != nullptr && *old_key != p_key) {
print_verbose(vformat("%s and %s have same keycode. Generating release event for %s", keycode_get_string(*old_key), keycode_get_string(p_key), keycode_get_string(*old_key)));
event = _seat_state_get_key_event(p_ss, p_keycode, false);
if (event.is_valid()) {
event->set_keycode(*old_key);
}
}
p_ss->pressed_keycodes[p_keycode] = p_key;
} else {
p_ss->pressed_keycodes.erase(p_keycode);
}
return event;
}
void WaylandThread::_set_current_seat(struct wl_seat *p_seat) {
@ -264,8 +319,6 @@ bool WaylandThread::_load_cursor_theme(int p_cursor_size) {
if (wl_cursor_theme) {
wl_cursor_theme_destroy(wl_cursor_theme);
wl_cursor_theme = nullptr;
current_wl_cursor = nullptr;
}
if (cursor_theme_name.is_empty()) {
@ -356,7 +409,12 @@ void WaylandThread::_update_scale(int p_scale) {
int cursor_size = unscaled_cursor_size * p_scale;
if (_load_cursor_theme(cursor_size)) {
cursor_set_shape(last_cursor_shape);
for (struct wl_seat *wl_seat : registry.wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
seat_state_update_cursor(ss);
}
}
}
@ -370,9 +428,16 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
return;
}
// NOTE: Deprecated.
if (strcmp(interface, zxdg_exporter_v1_interface.name) == 0) {
registry->xdg_exporter = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
registry->xdg_exporter_name = name;
registry->xdg_exporter_v1 = (struct zxdg_exporter_v1 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v1_interface, 1);
registry->xdg_exporter_v1_name = name;
return;
}
if (strcmp(interface, zxdg_exporter_v2_interface.name) == 0) {
registry->xdg_exporter_v2 = (struct zxdg_exporter_v2 *)wl_registry_bind(wl_registry, name, &zxdg_exporter_v2_interface, 1);
registry->xdg_exporter_v2_name = name;
return;
}
@ -493,6 +558,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re
return;
}
if (strcmp(interface, xdg_system_bell_v1_interface.name) == 0) {
registry->xdg_system_bell = (struct xdg_system_bell_v1 *)wl_registry_bind(wl_registry, name, &xdg_system_bell_v1_interface, 1);
registry->xdg_system_bell_name = name;
return;
}
if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
registry->xdg_activation = (struct xdg_activation_v1 *)wl_registry_bind(wl_registry, name, &xdg_activation_v1_interface, 1);
registry->xdg_activation_name = name;
@ -586,13 +657,25 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
if (name == registry->xdg_exporter_name) {
if (registry->xdg_exporter) {
zxdg_exporter_v1_destroy(registry->xdg_exporter);
registry->xdg_exporter = nullptr;
// NOTE: Deprecated.
if (name == registry->xdg_exporter_v1_name) {
if (registry->xdg_exporter_v1) {
zxdg_exporter_v1_destroy(registry->xdg_exporter_v1);
registry->xdg_exporter_v1 = nullptr;
}
registry->xdg_exporter_name = 0;
registry->xdg_exporter_v1_name = 0;
return;
}
if (name == registry->xdg_exporter_v2_name) {
if (registry->xdg_exporter_v2) {
zxdg_exporter_v2_destroy(registry->xdg_exporter_v2);
registry->xdg_exporter_v2 = nullptr;
}
registry->xdg_exporter_v2_name = 0;
return;
}
@ -688,6 +771,17 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry
return;
}
if (name == registry->xdg_system_bell_name) {
if (registry->xdg_system_bell) {
xdg_system_bell_v1_destroy(registry->xdg_system_bell);
registry->xdg_system_bell = nullptr;
}
registry->xdg_system_bell_name = 0;
return;
}
if (name == registry->xdg_activation_name) {
if (registry->xdg_activation) {
xdg_activation_v1_destroy(registry->xdg_activation);
@ -1162,7 +1256,15 @@ void WaylandThread::_xdg_toplevel_on_wm_capabilities(void *data, struct xdg_topl
}
}
void WaylandThread::_xdg_exported_on_exported(void *data, zxdg_exported_v1 *exported, const char *handle) {
// NOTE: Deprecated.
void WaylandThread::_xdg_exported_v1_on_handle(void *data, zxdg_exported_v1 *exported, const char *handle) {
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
ws->exported_handle = vformat("wayland:%s", String::utf8(handle));
}
void WaylandThread::_xdg_exported_v2_on_handle(void *data, zxdg_exported_v2 *exported, const char *handle) {
WindowState *ws = (WindowState *)data;
ERR_FAIL_NULL(ws);
@ -1394,6 +1496,8 @@ void WaylandThread::_wl_pointer_on_leave(void *data, struct wl_pointer *wl_point
ss->pointed_surface = nullptr;
ss->pointer_data_buffer.pressed_button_mask.clear();
Ref<WindowEventMessage> msg;
msg.instantiate();
msg->event = DisplayServer::WINDOW_EVENT_MOUSE_EXIT;
@ -1416,10 +1520,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;
}
@ -1532,7 +1636,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;
@ -1649,7 +1753,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;
}
@ -1741,7 +1845,7 @@ void WaylandThread::_wl_pointer_on_axis_discrete(void *data, struct wl_pointer *
pd.discrete_scroll_vector_120.y = discrete * 120;
}
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
pd.discrete_scroll_vector_120.x = discrete * 120;
}
}
@ -1762,7 +1866,7 @@ void WaylandThread::_wl_pointer_on_axis_value120(void *data, struct wl_pointer *
pd.discrete_scroll_vector_120.y += value120;
}
if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
pd.discrete_scroll_vector_120.x += value120;
}
}
@ -1848,13 +1952,19 @@ void WaylandThread::_wl_keyboard_on_key(void *data, struct wl_keyboard *wl_keybo
ss->repeating_keycode = XKB_KEYCODE_INVALID;
}
Ref<InputEventKey> k;
k.instantiate();
if (!_seat_state_configure_key_event(*ss, k, xkb_keycode, pressed)) {
Ref<InputEventKey> k = _seat_state_get_key_event(ss, xkb_keycode, pressed);
if (k.is_null()) {
return;
}
Ref<InputEventKey> uk = _seat_state_get_unstuck_key_event(ss, xkb_keycode, pressed, k->get_keycode());
if (uk.is_valid()) {
Ref<InputEventMessage> u_msg;
u_msg.instantiate();
u_msg->event = uk;
wayland_thread->push_message(u_msg);
}
Ref<InputEventMessage> msg;
msg.instantiate();
msg->event = k;
@ -2388,9 +2498,9 @@ 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();
}
@ -2520,7 +2630,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);
@ -2681,7 +2791,7 @@ void WaylandThread::_wp_text_input_on_done(void *data, struct zwp_text_input_v3
msg.instantiate();
msg->text = ss->ime_text_commit;
ss->wayland_thread->push_message(msg);
} else if (!ss->ime_text.is_empty()) {
} else {
Ref<IMEUpdateEventMessage> msg;
msg.instantiate();
msg->text = ss->ime_text;
@ -3071,19 +3181,25 @@ void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) {
void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
ERR_FAIL_NULL(p_ss);
WaylandThread *thread = p_ss->wayland_thread;
ERR_FAIL_NULL(p_ss->wayland_thread);
if (p_ss->wl_pointer && p_ss->cursor_surface) {
// NOTE: Those values are valid by default and will hide the cursor when
// unchanged, which happens when both the current custom cursor and the
// current wl_cursor are `nullptr`.
struct wl_buffer *cursor_buffer = nullptr;
uint32_t hotspot_x = 0;
uint32_t hotspot_y = 0;
int scale = 1;
if (!p_ss->wl_pointer || !p_ss->cursor_surface) {
return;
}
CustomCursor *custom_cursor = p_ss->wayland_thread->current_custom_cursor;
struct wl_cursor *wl_cursor = p_ss->wayland_thread->current_wl_cursor;
// NOTE: Those values are valid by default and will hide the cursor when
// unchanged.
struct wl_buffer *cursor_buffer = nullptr;
uint32_t hotspot_x = 0;
uint32_t hotspot_y = 0;
int scale = 1;
if (thread->cursor_visible) {
DisplayServer::CursorShape shape = thread->cursor_shape;
struct CustomCursor *custom_cursor = thread->custom_cursors.getptr(shape);
if (custom_cursor) {
cursor_buffer = custom_cursor->wl_buffer;
@ -3093,7 +3209,13 @@ void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
// We can't really reasonably scale custom cursors, so we'll let the
// compositor do it for us (badly).
scale = 1;
} else if (wl_cursor) {
} else {
struct wl_cursor *wl_cursor = thread->wl_cursors[shape];
if (!wl_cursor) {
return;
}
int frame_idx = 0;
if (wl_cursor->image_count > 1) {
@ -3109,24 +3231,24 @@ void WaylandThread::seat_state_update_cursor(SeatState *p_ss) {
struct wl_cursor_image *wl_cursor_image = wl_cursor->images[frame_idx];
scale = p_ss->wayland_thread->cursor_scale;
scale = thread->cursor_scale;
cursor_buffer = wl_cursor_image_get_buffer(wl_cursor_image);
// As the surface's buffer is scaled (thus the surface is smaller) and the
// hotspot must be expressed in surface-local coordinates, we need to scale
// them down accordingly.
// it down accordingly.
hotspot_x = wl_cursor_image->hotspot_x / scale;
hotspot_y = wl_cursor_image->hotspot_y / scale;
}
wl_pointer_set_cursor(p_ss->wl_pointer, p_ss->pointer_enter_serial, p_ss->cursor_surface, hotspot_x, hotspot_y);
wl_surface_set_buffer_scale(p_ss->cursor_surface, scale);
wl_surface_attach(p_ss->cursor_surface, cursor_buffer, 0, 0);
wl_surface_damage_buffer(p_ss->cursor_surface, 0, 0, INT_MAX, INT_MAX);
wl_surface_commit(p_ss->cursor_surface);
}
wl_pointer_set_cursor(p_ss->wl_pointer, p_ss->pointer_enter_serial, p_ss->cursor_surface, hotspot_x, hotspot_y);
wl_surface_set_buffer_scale(p_ss->cursor_surface, scale);
wl_surface_attach(p_ss->cursor_surface, cursor_buffer, 0, 0);
wl_surface_damage_buffer(p_ss->cursor_surface, 0, 0, INT_MAX, INT_MAX);
wl_surface_commit(p_ss->cursor_surface);
}
void WaylandThread::seat_state_echo_keys(SeatState *p_ss) {
@ -3152,15 +3274,18 @@ void WaylandThread::seat_state_echo_keys(SeatState *p_ss) {
int keys_amount = (ticks_delta / p_ss->repeat_key_delay_msec);
for (int i = 0; i < keys_amount; i++) {
Ref<InputEventKey> k;
k.instantiate();
if (!_seat_state_configure_key_event(*p_ss, k, p_ss->repeating_keycode, true)) {
Ref<InputEventKey> k = _seat_state_get_key_event(p_ss, p_ss->repeating_keycode, true);
if (k.is_null()) {
continue;
}
k->set_echo(true);
Ref<InputEventKey> uk = _seat_state_get_unstuck_key_event(p_ss, p_ss->repeating_keycode, true, k->get_keycode());
if (uk.is_valid()) {
Input::get_singleton()->parse_input_event(uk);
}
Input::get_singleton()->parse_input_event(k);
}
@ -3246,9 +3371,12 @@ 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);
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);
if (registry.xdg_exporter_v2) {
ws.xdg_exported_v2 = zxdg_exporter_v2_export_toplevel(registry.xdg_exporter_v2, ws.wl_surface);
zxdg_exported_v2_add_listener(ws.xdg_exported_v2, &xdg_exported_v2_listener, &ws);
} else if (registry.xdg_exporter_v1) {
ws.xdg_exported_v1 = zxdg_exporter_v1_export(registry.xdg_exporter_v1, ws.wl_surface);
zxdg_exported_v1_add_listener(ws.xdg_exported_v1, &xdg_exported_v1_listener, &ws);
}
wl_surface_commit(ws.wl_surface);
@ -3264,6 +3392,102 @@ struct wl_surface *WaylandThread::window_get_wl_surface(DisplayServer::WindowID
return ws.wl_surface;
}
void WaylandThread::beep() const {
if (registry.xdg_system_bell) {
xdg_system_bell_v1_ring(registry.xdg_system_bell, nullptr);
}
}
void WaylandThread::window_start_drag(DisplayServer::WindowID p_window_id) {
// TODO: Use window IDs for multiwindow support.
WindowState &ws = main_window;
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
if (ss && ws.xdg_toplevel) {
xdg_toplevel_move(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial);
}
#ifdef LIBDECOR_ENABLED
if (ws.libdecor_frame) {
libdecor_frame_move(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial);
}
#endif
}
void WaylandThread::window_start_resize(DisplayServer::WindowResizeEdge p_edge, DisplayServer::WindowID p_window) {
// TODO: Use window IDs for multiwindow support.
WindowState &ws = main_window;
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
if (ss && ws.xdg_toplevel) {
xdg_toplevel_resize_edge edge = XDG_TOPLEVEL_RESIZE_EDGE_NONE;
switch (p_edge) {
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_TOP: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP;
} break;
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT;
} break;
case DisplayServer::WINDOW_EDGE_LEFT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_RIGHT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_RIGHT;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
edge = XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT;
} break;
default:
break;
}
xdg_toplevel_resize(ws.xdg_toplevel, ss->wl_seat, ss->pointer_data.button_serial, edge);
}
#ifdef LIBDECOR_ENABLED
if (ws.libdecor_frame) {
libdecor_resize_edge edge = LIBDECOR_RESIZE_EDGE_NONE;
switch (p_edge) {
case DisplayServer::WINDOW_EDGE_TOP_LEFT: {
edge = LIBDECOR_RESIZE_EDGE_TOP_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_TOP: {
edge = LIBDECOR_RESIZE_EDGE_TOP;
} break;
case DisplayServer::WINDOW_EDGE_TOP_RIGHT: {
edge = LIBDECOR_RESIZE_EDGE_TOP_RIGHT;
} break;
case DisplayServer::WINDOW_EDGE_LEFT: {
edge = LIBDECOR_RESIZE_EDGE_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_RIGHT: {
edge = LIBDECOR_RESIZE_EDGE_RIGHT;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM_LEFT: {
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_LEFT;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM: {
edge = LIBDECOR_RESIZE_EDGE_BOTTOM;
} break;
case DisplayServer::WINDOW_EDGE_BOTTOM_RIGHT: {
edge = LIBDECOR_RESIZE_EDGE_BOTTOM_RIGHT;
} break;
default:
break;
}
libdecor_frame_resize(ws.libdecor_frame, ss->wl_seat, ss->pointer_data.button_serial, edge);
}
#endif
}
void WaylandThread::window_set_max_size(DisplayServer::WindowID p_window_id, const Size2i &p_size) {
// TODO: Use window IDs for multiwindow support.
WindowState &ws = main_window;
@ -3328,7 +3552,8 @@ bool WaylandThread::window_can_set_mode(DisplayServer::WindowID p_window_id, Dis
return ws.can_maximize;
};
case DisplayServer::WINDOW_MODE_FULLSCREEN: {
case DisplayServer::WINDOW_MODE_FULLSCREEN:
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
#ifdef LIBDECOR_ENABLED
if (ws.libdecor_frame) {
return libdecor_frame_has_capability(ws.libdecor_frame, LIBDECOR_ACTION_FULLSCREEN);
@ -3337,13 +3562,6 @@ bool WaylandThread::window_can_set_mode(DisplayServer::WindowID p_window_id, Dis
return ws.can_fullscreen;
};
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
// I'm not really sure but from what I can find Wayland doesn't really have
// the concept of exclusive fullscreen.
// TODO: Discuss whether to fallback to regular fullscreen or not.
return false;
};
}
return false;
@ -3458,7 +3676,8 @@ void WaylandThread::window_try_set_mode(DisplayServer::WindowID p_window_id, Dis
#endif // LIBDECOR_ENABLED
} break;
case DisplayServer::WINDOW_MODE_FULLSCREEN: {
case DisplayServer::WINDOW_MODE_FULLSCREEN:
case DisplayServer::WINDOW_MODE_EXCLUSIVE_FULLSCREEN: {
if (ws.xdg_toplevel) {
xdg_toplevel_set_fullscreen(ws.xdg_toplevel, nullptr);
}
@ -3767,25 +3986,19 @@ Error WaylandThread::init() {
return OK;
}
void WaylandThread::cursor_hide() {
current_wl_cursor = nullptr;
current_custom_cursor = nullptr;
void WaylandThread::cursor_set_visible(bool p_visible) {
cursor_visible = p_visible;
SeatState *ss = wl_seat_get_seat_state(wl_seat_current);
ERR_FAIL_NULL(ss);
seat_state_update_cursor(ss);
for (struct wl_seat *wl_seat : registry.wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
seat_state_update_cursor(ss);
}
}
void WaylandThread::cursor_set_shape(DisplayServer::CursorShape p_cursor_shape) {
if (!wl_cursors[p_cursor_shape]) {
return;
}
// The point of this method is make the current cursor a "plain" shape and, as
// the custom cursor overrides what gets set, we have to clear it too.
current_custom_cursor = nullptr;
current_wl_cursor = wl_cursors[p_cursor_shape];
cursor_shape = p_cursor_shape;
for (struct wl_seat *wl_seat : registry.wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
@ -3793,27 +4006,10 @@ void WaylandThread::cursor_set_shape(DisplayServer::CursorShape p_cursor_shape)
seat_state_update_cursor(ss);
}
last_cursor_shape = p_cursor_shape;
}
void WaylandThread::cursor_set_custom_shape(DisplayServer::CursorShape p_cursor_shape) {
ERR_FAIL_COND(!custom_cursors.has(p_cursor_shape));
current_custom_cursor = &custom_cursors[p_cursor_shape];
for (struct wl_seat *wl_seat : registry.wl_seats) {
SeatState *ss = wl_seat_get_seat_state(wl_seat);
ERR_FAIL_NULL(ss);
seat_state_update_cursor(ss);
}
last_cursor_shape = p_cursor_shape;
}
void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_cursor_shape, Ref<Image> p_image, const Point2i &p_hotspot) {
ERR_FAIL_COND(!p_image.is_valid());
ERR_FAIL_COND(p_image.is_null());
Size2i image_size = p_image->get_size();
@ -3829,23 +4025,21 @@ void WaylandThread::cursor_shape_set_custom_image(DisplayServer::CursorShape p_c
CustomCursor &cursor = custom_cursors[p_cursor_shape];
cursor.hotspot = p_hotspot;
if (cursor.buffer_data) {
// Clean up the old buffer data.
munmap(cursor.buffer_data, cursor.buffer_data_size);
}
// NOTE: From `wl_keyboard`s of version 7 or later, the spec requires the mmap
// operation to be done with MAP_PRIVATE, as "MAP_SHARED may fail". We'll do it
// regardless of global version.
cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0);
if (cursor.wl_buffer) {
// Clean up the old Wayland buffer.
wl_buffer_destroy(cursor.wl_buffer);
}
if (cursor.buffer_data) {
// Clean up the old buffer data.
munmap(cursor.buffer_data, cursor.buffer_data_size);
}
cursor.buffer_data = (uint32_t *)mmap(nullptr, data_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
cursor.buffer_data_size = data_size;
// Create the Wayland buffer.
struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(registry.wl_shm, fd, image_size.height * data_size);
struct wl_shm_pool *wl_shm_pool = wl_shm_create_pool(registry.wl_shm, fd, data_size);
// TODO: Make sure that WL_SHM_FORMAT_ARGB8888 format is supported. It
// technically isn't garaunteed to be supported, but I think that'd be a
// pretty unlikely thing to stumble upon.
@ -3873,8 +4067,6 @@ void WaylandThread::cursor_shape_clear_custom_image(DisplayServer::CursorShape p
CustomCursor cursor = custom_cursors[p_cursor_shape];
custom_cursors.erase(p_cursor_shape);
current_custom_cursor = nullptr;
if (cursor.wl_buffer) {
wl_buffer_destroy(cursor.wl_buffer);
}
@ -3979,6 +4171,7 @@ void WaylandThread::selection_set_text(const String &p_text) {
if (registry.wl_data_device_manager == nullptr) {
DEBUG_LOG_WAYLAND_THREAD("Couldn't set selection, wl_data_device_manager global not available.");
return;
}
if (ss == nullptr) {
@ -3997,10 +4190,10 @@ void WaylandThread::selection_set_text(const String &p_text) {
wl_data_source_add_listener(ss->wl_data_source_selection, &wl_data_source_listener, ss);
wl_data_source_offer(ss->wl_data_source_selection, "text/plain;charset=utf-8");
wl_data_source_offer(ss->wl_data_source_selection, "text/plain");
}
// TODO: Implement a good way of getting the latest serial from the user.
wl_data_device_set_selection(ss->wl_data_device, ss->wl_data_source_selection, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
// TODO: Implement a good way of getting the latest serial from the user.
wl_data_device_set_selection(ss->wl_data_device, ss->wl_data_source_selection, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
}
// Wait for the message to get to the server before continuing, otherwise the
// clipboard update might come with a delay.
@ -4106,6 +4299,11 @@ void WaylandThread::primary_set_text(const String &p_text) {
return;
}
if (ss->wp_primary_selection_device == nullptr) {
DEBUG_LOG_WAYLAND_THREAD("Couldn't set primary selection, seat doesn't have wp_primary_selection_device.");
return;
}
ss->primary_data = p_text.to_utf8_buffer();
if (ss->wp_primary_selection_source == nullptr) {
@ -4113,10 +4311,10 @@ void WaylandThread::primary_set_text(const String &p_text) {
zwp_primary_selection_source_v1_add_listener(ss->wp_primary_selection_source, &wp_primary_selection_source_listener, ss);
zwp_primary_selection_source_v1_offer(ss->wp_primary_selection_source, "text/plain;charset=utf-8");
zwp_primary_selection_source_v1_offer(ss->wp_primary_selection_source, "text/plain");
}
// TODO: Implement a good way of getting the latest serial from the user.
zwp_primary_selection_device_v1_set_selection(ss->wp_primary_selection_device, ss->wp_primary_selection_source, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
// TODO: Implement a good way of getting the latest serial from the user.
zwp_primary_selection_device_v1_set_selection(ss->wp_primary_selection_device, ss->wp_primary_selection_source, MAX(ss->pointer_data.button_serial, ss->last_key_pressed_serial));
}
// Wait for the message to get to the server before continuing, otherwise the
// clipboard update might come with a delay.
@ -4141,6 +4339,16 @@ bool WaylandThread::get_reset_frame() {
// Dispatches events until a frame event is received, a window is reported as
// suspended or the timeout expires.
bool WaylandThread::wait_frame_suspend_ms(int p_timeout) {
// This is a bit of a chicken and egg thing... Looks like the main event loop
// has to call its rightfully forever-blocking poll right in between
// `wl_display_prepare_read` and `wl_display_read`. This means, that it will
// basically be guaranteed to stay stuck in a "prepare read" state, where it
// will block any other attempt at reading the display fd, such as ours. The
// solution? Let's make sure the mutex is locked (it should) and unblock the
// main thread with a roundtrip!
MutexLock mutex_lock(mutex);
wl_display_roundtrip(wl_display);
if (main_window.suspended) {
// The window is suspended! The compositor is telling us _explicitly_ that we
// don't need to draw, without letting us guess through the frame event's
@ -4373,6 +4581,10 @@ void WaylandThread::destroy() {
xdg_activation_v1_destroy(registry.xdg_activation);
}
if (registry.xdg_system_bell) {
xdg_system_bell_v1_destroy(registry.xdg_system_bell);
}
if (registry.xdg_decoration_manager) {
zxdg_decoration_manager_v1_destroy(registry.xdg_decoration_manager);
}
@ -4389,10 +4601,14 @@ void WaylandThread::destroy() {
xdg_wm_base_destroy(registry.xdg_wm_base);
}
if (registry.xdg_exporter) {
zxdg_exporter_v1_destroy(registry.xdg_exporter);
// NOTE: Deprecated.
if (registry.xdg_exporter_v1) {
zxdg_exporter_v1_destroy(registry.xdg_exporter_v1);
}
if (registry.xdg_exporter_v2) {
zxdg_exporter_v2_destroy(registry.xdg_exporter_v2);
}
if (registry.wl_shm) {
wl_shm_destroy(registry.wl_shm);
}