mirror of
https://github.com/kovidgoyal/kitty.git
synced 2026-09-06 17:58:39 +00:00
Wayland: fix crash when an output global is removed
surfaceHandleEnter() and surfaceHandleLeave() passed the event's output argument straight to wl_output_get_user_data(). Wayland delivers object arguments as NULL when the object was destroyed client-side before the event was dispatched, even for arguments declared non-nullable, so this segfaults whenever a wl_output global goes away while a window is open. Easily reproduced with a hardware KVM or a monitor input switch. Also fixes two pre-existing problems in surfaceHandleLeave(): - The removal loop stopped at monitorsCount - 1, so it never compared the last entry, and the trailing unconditional window->wl.monitors[--window->wl.monitorsCount] = NULL dropped the last monitor whether or not it was the one that was left. A leave for a monitor not in the list therefore evicted an unrelated one. - That same statement ran even when monitorsCount was 0, writing to monitors[-1] and leaving the count negative. Both are replaced with the remove_i_from_array() loop already used for this array in registryHandleGlobalRemove(). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
parent
b71ef95ec7
commit
2dbe5df2d8
1 changed files with 16 additions and 6 deletions
22
glfw/wl_window.c
vendored
22
glfw/wl_window.c
vendored
|
|
@ -549,7 +549,15 @@ static const struct zxdg_toplevel_decoration_v1_listener xdgDecorationListener =
|
|||
static void
|
||||
surfaceHandleEnter(void *data, struct wl_surface *surface UNUSED, struct wl_output *output) {
|
||||
_GLFWwindow *window = data;
|
||||
// output is NULL when the wl_output global it refers to was removed before
|
||||
// this event was dispatched. Wayland delivers destroyed objects as NULL even
|
||||
// for arguments declared non-nullable, so this must be handled.
|
||||
// See https://wayland.freedesktop.org/docs/html/apc.html
|
||||
if (!output) return;
|
||||
_GLFWmonitor *monitor = wl_output_get_user_data(output);
|
||||
// never store NULL in window->wl.monitors as checkScaleChange() dereferences
|
||||
// every entry unconditionally
|
||||
if (!monitor) return;
|
||||
|
||||
if (window->wl.monitorsCount + 1 > window->wl.monitorsSize) {
|
||||
++window->wl.monitorsSize;
|
||||
|
|
@ -567,15 +575,17 @@ surfaceHandleEnter(void *data, struct wl_surface *surface UNUSED, struct wl_outp
|
|||
static void
|
||||
surfaceHandleLeave(void *data, struct wl_surface *surface UNUSED, struct wl_output *output) {
|
||||
_GLFWwindow *window = data;
|
||||
// output is NULL when the wl_output global it refers to was removed before
|
||||
// this event was dispatched, see the comment in surfaceHandleEnter(). In that
|
||||
// case registryHandleGlobalRemove() has already dropped the monitor from
|
||||
// window->wl.monitors, so there is nothing left to do here.
|
||||
if (!output) return;
|
||||
_GLFWmonitor *monitor = wl_output_get_user_data(output);
|
||||
bool found;
|
||||
int i;
|
||||
if (!monitor) return;
|
||||
|
||||
for (i = 0, found = false; i < window->wl.monitorsCount - 1; ++i) {
|
||||
if (monitor == window->wl.monitors[i]) found = true;
|
||||
if (found) window->wl.monitors[i] = window->wl.monitors[i + 1];
|
||||
for (int i = window->wl.monitorsCount - 1; i >= 0; i--) {
|
||||
if (window->wl.monitors[i] == monitor) { remove_i_from_array(window->wl.monitors, i, window->wl.monitorsCount); }
|
||||
}
|
||||
window->wl.monitors[--window->wl.monitorsCount] = NULL;
|
||||
|
||||
if (checkScaleChange(window)) {
|
||||
debug("Scale changed to %.3f for window %llu in surfaceHandleLeave\n", _glfwWaylandWindowScale(window), window->id);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue