wayland: ensure every event callback uses the mutex

This commit is contained in:
IoIxD 2026-01-04 11:40:34 -07:00
commit 12f279e83a

View file

@ -24,6 +24,21 @@
#include <linux/input-event-codes.h> #include <linux/input-event-codes.h>
#endif #endif
/* Standard procedure before most event callbacks in Wayland ("most" because the setup ones don't need this). Wait for the Mutex to be freed, if we're deadlocking for longer then a quarter of a second then do nothing with the event. */
#define WAYLAND_EVENT_OP_START(self) \
do { \
struct timespec t; \
t.tv_sec = 0; \
t.tv_nsec = 250; \
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { \
/*printf("deadlock at line %d\n", __LINE__);*/ \
return; \
}; \
} while(0);
/* Footer for WAYLAND_EVENT_OP_START */
#define WAYLAND_EVENT_OP_END(self) pthread_mutex_unlock(&self->wayland.eventsMutex);
/* Setup the framebuffer with the saved width/height */ /* Setup the framebuffer with the saved width/height */
static void framebuffer_setup(struct _MwLLWayland* wayland); static void framebuffer_setup(struct _MwLLWayland* wayland);
/* Destroy the framebuffer */ /* Destroy the framebuffer */
@ -41,16 +56,20 @@ static void region_invalidate(MwLL handle);
static void new_protocol(void* data, struct wl_registry* registry, static void new_protocol(void* data, struct wl_registry* registry,
MwU32 name, const char* interface, MwU32 name, const char* interface,
MwU32 version) { MwU32 version) {
struct _MwLLWayland* wayland = data; MwLL self = data;
wayland_protocol_callback_table_t* cb = shget(wayland->wl_protocol_setup_map, interface); WAYLAND_EVENT_OP_START(self);
wayland_protocol_callback_table_t* cb = shget(self->wayland.wl_protocol_setup_map, interface);
if(cb != NULL) { if(cb != NULL) {
char* inter = malloc(strlen(interface) + 1); char* inter = malloc(strlen(interface) + 1);
strcpy(inter, interface); strcpy(inter, interface);
shput(wayland->wl_protocol_map, inter, cb->setup(name, data)); shput(self->wayland.wl_protocol_map, inter, cb->setup(name, data));
} else { } else {
/* printf("unknown interface %s\n", interface); */ /* printf("unknown interface %s\n", interface); */
} }
WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_registry.global_remove` callback */ /* `wl_registry.global_remove` callback */
@ -108,11 +127,20 @@ static void wl_data_device_enter(void* data,
wl_fixed_t y, wl_fixed_t y,
struct wl_data_offer* id) { struct wl_data_offer* id) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
self->wayland.clipboard_serial = serial; self->wayland.clipboard_serial = serial;
WAYLAND_EVENT_OP_END(self);
}; };
static void wl_data_device_leave(void* data, static void wl_data_device_leave(void* data,
struct wl_data_device* wl_data_device) { struct wl_data_device* wl_data_device) {
MwLL self = data;
WAYLAND_EVENT_OP_START(self);
wl_data_device_destroy(wl_data_device); wl_data_device_destroy(wl_data_device);
WAYLAND_EVENT_OP_END(self);
}; };
static void wl_data_device_motion(void* data, static void wl_data_device_motion(void* data,
struct wl_data_device* wl_data_device, struct wl_data_device* wl_data_device,
@ -213,12 +241,15 @@ static void wl_data_source_listener_send(void* data,
const char* mime_type, const char* mime_type,
int32_t fd) { int32_t fd) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.clipboard_buffer != NULL) { if(self->wayland.clipboard_buffer != NULL) {
write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer)); write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer));
close(fd); close(fd);
self->wayland.events_pending = 1; self->wayland.events_pending = 1;
} }
WAYLAND_EVENT_OP_END(self);
}; };
static void wl_data_source_listener_cancelled(void* data, static void wl_data_source_listener_cancelled(void* data,
struct wl_data_source* wl_data_source); struct wl_data_source* wl_data_source);
@ -233,6 +264,8 @@ static void wl_data_source_listener_cancelled(void* data,
struct wl_data_source* wl_data_source) { struct wl_data_source* wl_data_source) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
wl_data_source_destroy(wl_data_source); wl_data_source_destroy(wl_data_source);
self->wayland.clipboard_source.wl = wl_data_device_manager_create_data_source(self->wayland.clipboard_manager.wl); self->wayland.clipboard_source.wl = wl_data_device_manager_create_data_source(self->wayland.clipboard_manager.wl);
@ -244,6 +277,8 @@ static void wl_data_source_listener_cancelled(void* data,
wl_data_source_offer(self->wayland.clipboard_source.wl, "UTF8_STRING"); wl_data_source_offer(self->wayland.clipboard_source.wl, "UTF8_STRING");
wl_data_source_add_listener(self->wayland.clipboard_source.wl, &wl_data_source_listener, self); wl_data_source_add_listener(self->wayland.clipboard_source.wl, &wl_data_source_listener, self);
WAYLAND_EVENT_OP_END(self);
}; };
static void zwp_primary_selection_source_v1_send(void* data, static void zwp_primary_selection_source_v1_send(void* data,
@ -251,17 +286,24 @@ static void zwp_primary_selection_source_v1_send(void* data,
const char* mime_type, const char* mime_type,
int32_t fd) { int32_t fd) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.clipboard_buffer != NULL) { if(self->wayland.clipboard_buffer != NULL) {
write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer)); write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer));
close(fd); close(fd);
self->wayland.events_pending = 1; self->wayland.events_pending = 1;
} }
WAYLAND_EVENT_OP_END(self);
}; };
static void zwp_primary_selection_source_v1_cancelled(void* data, static void zwp_primary_selection_source_v1_cancelled(void* data,
struct zwp_primary_selection_source_v1* wl_data_source) { struct zwp_primary_selection_source_v1* wl_data_source) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
zwp_primary_selection_source_v1_destroy(self->wayland.clipboard_source.zwp); zwp_primary_selection_source_v1_destroy(self->wayland.clipboard_source.zwp);
self->wayland.clipboard_source.zwp = zwp_primary_selection_device_manager_v1_create_source(self->wayland.clipboard_manager.zwp); self->wayland.clipboard_source.zwp = zwp_primary_selection_device_manager_v1_create_source(self->wayland.clipboard_manager.zwp);
@ -271,6 +313,8 @@ static void zwp_primary_selection_source_v1_cancelled(void* data,
zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "TEXT"); zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "TEXT");
zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "STRING"); zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "STRING");
zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "UTF8_STRING"); zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "UTF8_STRING");
WAYLAND_EVENT_OP_END(self);
}; };
struct zwp_primary_selection_source_v1_listener zwp_primary_selection_source_v1_listener = { struct zwp_primary_selection_source_v1_listener zwp_primary_selection_source_v1_listener = {
@ -327,13 +371,7 @@ static wayland_protocol_t* zwp_primary_selection_device_manager_v1_setup(MwU32 n
} }
static void zwp_primary_selection_device_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { static void zwp_primary_selection_device_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
return;
zwp_primary_selection_device_manager_v1_destroy(wayland->clipboard_manager.zwp); zwp_primary_selection_device_manager_v1_destroy(wayland->clipboard_manager.zwp);
zwp_primary_selection_source_v1_destroy(wayland->clipboard_source.zwp);
free(data->listener);
} }
/* `wl_pointer.enter` callback */ /* `wl_pointer.enter` callback */
@ -341,17 +379,14 @@ static void pointer_enter(void* data, struct wl_pointer* wl_pointer, MwU32 seria
struct wl_surface* surface, wl_fixed_t surface_x, struct wl_surface* surface, wl_fixed_t surface_x,
wl_fixed_t surface_y) { wl_fixed_t surface_y) {
MwLL self = data; MwLL self = data;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
self->wayland.pointer_serial = serial; self->wayland.pointer_serial = serial;
wl_pointer_set_cursor(wl_pointer, serial, self->wayland.cursor.surface, 0, 0); wl_pointer_set_cursor(wl_pointer, serial, self->wayland.cursor.surface, 0, 0);
pthread_mutex_unlock(&self->wayland.eventsMutex); WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_pointer.leave` callback */ /* `wl_pointer.leave` callback */
@ -365,12 +400,8 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time
MwLL self = data; MwLL self = data;
MwLLMouse p; MwLLMouse p;
struct timespec t; WAYLAND_EVENT_OP_START(self);
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) {
return;
};
self->wayland.cur_mouse_pos.x = wl_fixed_to_double(surface_x); self->wayland.cur_mouse_pos.x = wl_fixed_to_double(surface_x);
self->wayland.cur_mouse_pos.y = wl_fixed_to_double(surface_y); self->wayland.cur_mouse_pos.y = wl_fixed_to_double(surface_y);
@ -378,7 +409,8 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time
MwLLDispatch(self, move, &p); MwLLDispatch(self, move, &p);
self->wayland.events_pending += 1; self->wayland.events_pending += 1;
pthread_mutex_unlock(&self->wayland.eventsMutex);
WAYLAND_EVENT_OP_END(self);
/*timed_redraw(self, time, 50, &self->wayland.cooldown_timer);*/ /*timed_redraw(self, time, 50, &self->wayland.cooldown_timer);*/
}; };
@ -387,12 +419,8 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time
static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 serial, MwU32 time, MwU32 button, MwU32 state) { static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 serial, MwU32 time, MwU32 button, MwU32 state) {
MwLL self = data; MwLL self = data;
MwLLMouse p; MwLLMouse p;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
p.point = self->wayland.cur_mouse_pos; p.point = self->wayland.cur_mouse_pos;
if(p.point.x > self->wayland.x && p.point.x < self->wayland.x + self->wayland.ww && p.point.y > self->wayland.y && p.point.y < self->wayland.y + self->wayland.wh) { if(p.point.x > self->wayland.x && p.point.x < self->wayland.x + self->wayland.ww && p.point.y > self->wayland.y && p.point.y < self->wayland.y + self->wayland.wh) {
@ -425,7 +453,8 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri
MwLLDispatch(self, draw, NULL); MwLLDispatch(self, draw, NULL);
self->wayland.events_pending += 1; self->wayland.events_pending += 1;
} }
pthread_mutex_unlock(&self->wayland.eventsMutex);
WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_pointer.axis` callback */ /* `wl_pointer.axis` callback */
@ -447,6 +476,7 @@ static void keyboard_keymap(void* data,
int32_t fd, int32_t fd,
MwU32 size) { MwU32 size) {
MwLL self = data; MwLL self = data;
if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) { if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
struct _MwLLWaylandTopLevel* wayland = self->wayland.toplevel; struct _MwLLWaylandTopLevel* wayland = self->wayland.toplevel;
assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1); assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
@ -474,18 +504,15 @@ static void keyboard_enter(void* data,
struct wl_surface* surface, struct wl_surface* surface,
struct wl_array* keys) { struct wl_array* keys) {
MwLL self = data; MwLL self = data;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
self->wayland.keyboard_serial = serial; self->wayland.keyboard_serial = serial;
MwLLDispatch(self, focus_in, NULL); MwLLDispatch(self, focus_in, NULL);
self->wayland.events_pending += 1; self->wayland.events_pending += 1;
pthread_mutex_unlock(&self->wayland.eventsMutex);
WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_keyboard.leave` callback */ /* `wl_keyboard.leave` callback */
@ -494,17 +521,13 @@ static void keyboard_leave(void* data,
MwU32 serial, MwU32 serial,
struct wl_surface* surface) { struct wl_surface* surface) {
MwLL self = data; MwLL self = data;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
MwLLDispatch(self, focus_out, NULL); MwLLDispatch(self, focus_out, NULL);
self->wayland.events_pending += 1; self->wayland.events_pending += 1;
pthread_mutex_unlock(&self->wayland.eventsMutex); WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_keyboard.key` callback */ /* `wl_keyboard.key` callback */
@ -515,12 +538,9 @@ static void keyboard_key(void* data,
MwU32 key, MwU32 key,
MwU32 state) { MwU32 state) {
MwLL self = data; MwLL self = data;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) { if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
struct _MwLLWaylandTopLevel* wayland = self->wayland.toplevel; struct _MwLLWaylandTopLevel* wayland = self->wayland.toplevel;
xkb_layout_index_t layout; xkb_layout_index_t layout;
@ -611,7 +631,7 @@ static void keyboard_key(void* data,
self->wayland.events_pending += 1; self->wayland.events_pending += 1;
} }
pthread_mutex_unlock(&self->wayland.eventsMutex); WAYLAND_EVENT_OP_END(self);
}; };
/* `wl_keyboard.modifiers` callback */ /* `wl_keyboard.modifiers` callback */
@ -623,18 +643,14 @@ static void keyboard_modifiers(void* data,
MwU32 mods_locked, MwU32 mods_locked,
MwU32 group) { MwU32 group) {
MwLL self = data; MwLL self = data;
struct timespec t;
t.tv_nsec = 100;
if(pthread_mutex_timedlock(&self->wayland.eventsMutex, &t) != 0) { WAYLAND_EVENT_OP_START(self);
return;
};
self->wayland.mod_state = 0; self->wayland.mod_state = 0;
self->wayland.mod_state |= mods_depressed; self->wayland.mod_state |= mods_depressed;
self->wayland.mod_state |= mods_locked; self->wayland.mod_state |= mods_locked;
pthread_mutex_unlock(&self->wayland.eventsMutex); WAYLAND_EVENT_OP_END(self);
}; };
struct wl_keyboard_listener keyboard_listener = { struct wl_keyboard_listener keyboard_listener = {
@ -655,6 +671,8 @@ static void wl_seat_capabilities(void* data, struct wl_seat* wl_seat,
MwLL self = data; MwLL self = data;
wl_clipboard_device_context_t* device_ctx = malloc(sizeof(wl_clipboard_device_context_t)); wl_clipboard_device_context_t* device_ctx = malloc(sizeof(wl_clipboard_device_context_t));
WAYLAND_EVENT_OP_START(self);
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
self->wayland.keyboard = wl_seat_get_keyboard(wl_seat); self->wayland.keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(self->wayland.keyboard, &keyboard_listener, data); wl_keyboard_add_listener(self->wayland.keyboard, &keyboard_listener, data);
@ -682,6 +700,8 @@ static void wl_seat_capabilities(void* data, struct wl_seat* wl_seat,
arrpush(self->wayland.clipboard_devices, device_ctx); arrpush(self->wayland.clipboard_devices, device_ctx);
} }
WAYLAND_EVENT_OP_END(self);
}; };
static void output_geometry(void* data, static void output_geometry(void* data,
@ -849,13 +869,6 @@ static void xdg_toplevel_configure(void* data,
MwLLForceRender(self); MwLLForceRender(self);
/*if(!self->wayland.egl_setup) {
self->wayland.egl_setup = egl_setup(self, self->wayland.x, self->wayland.y, width, height);
} else {
wl_egl_window_resize(self->wayland.egl_window_native, width, height, 0, 0);
egl_resetup(self);
}*/
return; return;
}; };
@ -869,7 +882,9 @@ static void decoration_configure(
static void xdg_toplevel_close( static void xdg_toplevel_close(
void* data, struct xdg_toplevel* xdg_toplevel) { void* data, struct xdg_toplevel* xdg_toplevel) {
MwLL self = data; MwLL self = data;
WAYLAND_EVENT_OP_START(self);
MwLLDispatch(self, close, NULL); MwLLDispatch(self, close, NULL);
WAYLAND_EVENT_OP_END(self);
}; };
/* `xdg_surface.configure` callback */ /* `xdg_surface.configure` callback */
@ -1279,9 +1294,7 @@ static void setup_popup(MwLL r) {
xdg_positioner_set_size(r->wayland.popup->xdg_positioner, r->wayland.ww, r->wayland.wh); xdg_positioner_set_size(r->wayland.popup->xdg_positioner, r->wayland.ww, r->wayland.wh);
xdg_positioner_set_anchor_rect( xdg_positioner_set_anchor_rect(
r->wayland.popup->xdg_positioner, r->wayland.popup->xdg_positioner, 0, 0,
topmost_parent->wayland.cur_mouse_pos.x,
topmost_parent->wayland.cur_mouse_pos.y,
1, 1); 1, 1);
xdg_surface = topmost_parent->wayland.toplevel->xdg_surface; xdg_surface = topmost_parent->wayland.toplevel->xdg_surface;
@ -1363,9 +1376,16 @@ static MwLL MwLLCreateImpl(MwLL parent, int x, int y, int width, int height) {
static void MwLLDestroyImpl(MwLL handle) { static void MwLLDestroyImpl(MwLL handle) {
int i; int i;
struct timeval tv; struct timeval tv;
struct timespec t = {
.tv_sec = 0,
.tv_nsec = 100,
};
int select_ret; int select_ret;
pthread_mutex_lock(&handle->wayland.eventsMutex); if(pthread_mutex_timedlock(&handle->wayland.eventsMutex, &t) != 0) {
printf("WARNING: Couldn't call MwLLDestroyImpl due to deadlock.\n");
return;
};
MwLLDestroyCommon(handle); MwLLDestroyCommon(handle);
@ -1411,7 +1431,7 @@ static void MwLLDestroyImpl(MwLL handle) {
/* sleep long enough that any active attempts to wait for the mutex will have given up and we can safely unlock/destroy it */ /* sleep long enough that any active attempts to wait for the mutex will have given up and we can safely unlock/destroy it */
tv.tv_sec = 0; tv.tv_sec = 0;
tv.tv_usec = 250; tv.tv_usec = 500;
do { do {
select_ret = select(1, NULL, NULL, NULL, &tv); select_ret = select(1, NULL, NULL, NULL, &tv);
} while((select_ret == -1) && (errno == EINTR)); } while((select_ret == -1) && (errno == EINTR));