Merge pull request #118554 from deralmas/wl/complex-pointer-frames

Wayland: Handle complex pointer event frames
This commit is contained in:
Thaddeus Crews
2026-04-14 07:11:03 -05:00
2 changed files with 39 additions and 15 deletions
+35 -15
View File
@@ -1916,7 +1916,6 @@ void WaylandThread::_wl_pointer_on_leave(void *data, struct wl_pointer *wl_point
DisplayServerEnums::WindowID id = pd.pointed_id;
pd.pointed_id = DisplayServerEnums::INVALID_WINDOW_ID;
pd.pressed_button_mask.clear();
DEBUG_LOG_WAYLAND_THREAD(vformat("Pointer left window %d.", id));
@@ -2025,14 +2024,19 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
PointerData &old_pd = ss->pointer_data;
PointerData &pd = ss->pointer_data_buffer;
WindowState *ws = nullptr;
if (pd.pointed_id != old_pd.pointed_id) {
if (old_pd.pointed_id != DisplayServerEnums::INVALID_WINDOW_ID) {
pd.pressed_button_mask.clear();
Ref<WindowEventMessage> msg;
msg.instantiate();
msg->id = old_pd.pointed_id;
msg->event = DisplayServerEnums::WINDOW_EVENT_MOUSE_EXIT;
wayland_thread->push_message(msg);
DEBUG_LOG_WAYLAND_THREAD(vformat("Notifying mouse exit to window %d", msg->id));
}
if (pd.pointed_id != DisplayServerEnums::INVALID_WINDOW_ID) {
@@ -2040,24 +2044,32 @@ void WaylandThread::_wl_pointer_on_frame(void *data, struct wl_pointer *wl_point
msg.instantiate();
msg->id = pd.pointed_id;
msg->event = DisplayServerEnums::WINDOW_EVENT_MOUSE_ENTER;
DEBUG_LOG_WAYLAND_THREAD(vformat("Notifying mouse enter to window %d", msg->id));
wayland_thread->push_message(msg);
}
// According to the spec, compositors SHOULD emit both leave and enter events
// in one frame. Given that the frame event groups logically related events,
// it makes sense that all other events outside `enter` are related to the OLD
// surface. Additionally, some compositors (e.g. sway) emit other events
// alongside a leave event in one single frame, further confirming this
// behavior.
if (wayland_thread->window_exists(old_pd.pointed_id)) {
ws = wayland_thread->window_get_state(old_pd.pointed_id);
if (ws == nullptr) {
// Not ERR_FAIL_* as we still want to fall through.
ERR_PRINT("Invalid window userdata.");
}
}
}
WindowState *ws = nullptr;
// NOTE: At least on sway, with wl_pointer version 5 or greater,
// wl_pointer::leave might be emitted with other events (like
// wl_pointer::button) within the same wl_pointer::frame. Because of this, we
// need to account for when the currently pointed window might be invalid
// (third-party or even none) and fall back to the old one.
if (pd.pointed_id != DisplayServerEnums::INVALID_WINDOW_ID) {
ws = ss->wayland_thread->window_get_state(pd.pointed_id);
ERR_FAIL_NULL(ws);
} else if (old_pd.pointed_id != DisplayServerEnums::INVALID_WINDOW_ID) {
ws = ss->wayland_thread->window_get_state(old_pd.pointed_id);
ERR_FAIL_NULL(ws);
if (ws == nullptr && wayland_thread->window_exists(pd.pointed_id)) {
ws = wayland_thread->window_get_state(pd.pointed_id);
if (ws == nullptr) {
// Not ERR_FAIL_* as we still want to fall through.
ERR_PRINT("Invalid window userdata.");
}
}
if (ws == nullptr) {
@@ -4313,6 +4325,14 @@ void WaylandThread::window_destroy(DisplayServerEnums::WindowID p_window_id) {
windows.erase(p_window_id);
}
bool WaylandThread::window_exists(DisplayServerEnums::WindowID p_window_id) {
if (p_window_id == DisplayServerEnums::INVALID_WINDOW_ID) {
return false;
}
return windows.has(p_window_id);
}
struct wl_surface *WaylandThread::window_get_wl_surface(DisplayServerEnums::WindowID p_window_id) const {
const WindowState *ws = windows.getptr(p_window_id);
if (ws) {
@@ -1231,6 +1231,10 @@ public:
void window_create_popup(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowID p_parent_id, Rect2i p_rect);
void window_destroy(DisplayServerEnums::WindowID p_window_Id);
// Checks if a window exists for this ID (NOT if its data is valid). Useful to
// detect deleted windows.
bool window_exists(DisplayServerEnums::WindowID p_window_id);
void window_set_parent(DisplayServerEnums::WindowID p_window_id, DisplayServerEnums::WindowID p_parent_id);
struct wl_surface *window_get_wl_surface(DisplayServerEnums::WindowID p_window_id) const;