milsko/src/backend/wayland/interfaces.c

1225 lines
38 KiB
C

#include <Mw/Milsko.h>
#include <sys/mman.h>
#include "../../../external/stb_ds.h"
/* TODO: find out what FreeBSD and such wants us to include */
#ifdef __FreeBSD__
/* XXX: Untested (nishi) */
#include <evdev/input.h>
#else
#include <linux/input.h>
#include <linux/input-event-codes.h>
#endif
#include <sys/poll.h>
static void setup_clipboard(MwLL self, struct wl_seat* wl_seat);
static void setup_zwp_clipboard(MwLL self, struct wl_seat* wl_seat);
static void destroy_clipboard(MwLL self, struct wl_seat* wl_seat);
static void destroy_zwp_clipboard(MwLL self, struct wl_seat* wl_seat);
/* Recursively dispatch a move event to a widget and its children */
static void recursive_dispatch_move(MwLL handle, MwMouse* p) {
MwWidget h = (MwWidget)handle->common.user;
if(h) {
int i;
for(i = 0; i < arrlen(h->children); i++) {
MwLLDispatch(h->children[i]->lowlevel, move, p);
if(arrlen(h->children[i]->children) > 0) {
recursive_dispatch_move(h->children[i]->lowlevel, p);
}
}
}
};
/* `wl_registry.global` callback */
static void new_protocol(void* data, struct wl_registry* registry,
MwU32 name, const char* interface,
MwU32 version) {
MwLL self = data;
(void)version;
(void)registry;
WAYLAND_EVENT_OP_START(self);
wayland_protocol_callback_table_t* cb = shget(self->wayland.wl_protocol_setup_map, interface);
if(cb != NULL) {
shput(self->wayland.wl_protocol_map, interface, cb->setup(name, data, version));
/* we don't care for adding this protocol, we just use it to know if the compositor will let us have transparent surfaces */
} else if(strcmp(interface, "wp_alpha_modifier_v1") == 0) {
self->common.supports_transparency = MwTRUE;
} else {
// printf("unknown interface %s\n", interface);
}
WAYLAND_EVENT_OP_END(self);
};
/* `wl_registry.global_remove` callback */
static void protocol_removed(void* data,
struct wl_registry* registry,
MwU32 name) {
(void)data;
(void)registry;
(void)name;
printf("%d destroyed\n", name);
};
static void wl_offer(void* data,
struct wl_data_offer* wl_data_offer,
const char* mime_type) {
wl_clipboard_device_context_t* self = data;
wl_data_offer_accept(wl_data_offer, self->ll->wayland.clipboard_serial, mime_type);
};
struct wl_data_offer_listener offer_listener = {
.offer = wl_offer,
};
static void wl_data_device_data_offer(void* data,
struct wl_data_device* wl_data_device,
struct wl_data_offer* offer) {
wl_clipboard_device_context_t* self = data;
(void)wl_data_device;
wl_data_offer_add_listener(offer, &offer_listener, data);
self->offer.wl = offer;
};
static void wl_data_device_enter(void* data,
struct wl_data_device* wl_data_device,
uint32_t serial,
struct wl_surface* surface,
wl_fixed_t x,
wl_fixed_t y,
struct wl_data_offer* id) {
MwLL self = data;
(void)wl_data_device;
(void)surface;
(void)x;
(void)y;
(void)id;
WAYLAND_EVENT_OP_START(self);
self->wayland.clipboard_serial = serial;
WAYLAND_EVENT_OP_END(self);
};
static void wl_data_device_leave(void* data,
struct wl_data_device* wl_data_device) {
MwLL self = data;
WAYLAND_EVENT_OP_START(self);
wl_data_device_destroy(wl_data_device);
WAYLAND_EVENT_OP_END(self);
};
static void wl_data_device_motion(void* data,
struct wl_data_device* wl_data_device,
uint32_t time,
wl_fixed_t x,
wl_fixed_t y) {
(void)data;
(void)wl_data_device;
(void)time;
(void)x;
(void)y;
};
static void wl_data_device_drop(void* data,
struct wl_data_device* wl_data_device) {
(void)data;
(void)wl_data_device;
};
static void wl_data_device_selection(void* data,
struct wl_data_device* wl_data_device,
struct wl_data_offer* id) {
(void)data;
(void)wl_data_device;
(void)id;
};
struct wl_data_device_listener wl_data_device_listener = {
.data_offer = wl_data_device_data_offer,
.enter = wl_data_device_enter,
.leave = wl_data_device_leave,
.motion = wl_data_device_motion,
.drop = wl_data_device_drop,
.selection = wl_data_device_selection,
};
static void zwp_primary_selection_device_v1_data_offer(void* data,
struct zwp_primary_selection_device_v1* zwp_primary_selection_device_v1,
struct zwp_primary_selection_offer_v1* offer) {
wl_clipboard_device_context_t* self = data;
(void)zwp_primary_selection_device_v1;
self->offer.zwp = offer;
};
static void zwp_primary_selection_device_v1_selection(void* data,
struct zwp_primary_selection_device_v1* zwp_primary_selection_device_v1,
struct zwp_primary_selection_offer_v1* id) {
(void)data;
(void)zwp_primary_selection_device_v1;
(void)id;
};
struct zwp_primary_selection_device_v1_listener zwp_primary_selection_device_v1_listener = {
.data_offer = zwp_primary_selection_device_v1_data_offer,
.selection = zwp_primary_selection_device_v1_selection,
};
void MwLLWaylandClipboardRead(wl_clipboard_device_context_t* ctx, int clipboard_type) {
int fds[2];
fd_set set;
char* buf = NULL;
struct timeval timeout;
int rc = 0;
if(pipe(fds) != 0) {
return;
}
FD_ZERO(&set);
FD_SET(fds[0], &set);
timeout.tv_sec = 0;
timeout.tv_usec = 100;
if(clipboard_type == MwCLIPBOARD_PRIMARY && ctx->offer.zwp) {
zwp_primary_selection_offer_v1_receive(ctx->offer.zwp, "text/plain", fds[1]);
} else if(ctx->offer.wl) {
wl_data_offer_receive(ctx->offer.wl, "text/plain", fds[1]);
}
close(fds[1]);
MwLLWaylandFlush(ctx->ll);
wl_display_roundtrip(ctx->ll->wayland.display);
while(MwTRUE) {
rc = select(fds[0] + 1, &set, NULL, NULL, &timeout);
if(rc <= 0) {
break;
} else {
char b;
ssize_t n = read(fds[0], &b, sizeof(b));
if(n <= 0) {
break;
}
arrpush(buf, b);
}
}
arrpush(buf, 0);
close(fds[0]);
MwLLDispatch(ctx->ll, clipboard, buf);
arrfree(buf);
ctx->ll->wayland.events_pending = 1;
}
static void wl_data_source_listener_target(void* data,
struct wl_data_source* wl_data_source,
const char* mime_type) {
(void)data;
(void)wl_data_source;
(void)mime_type;
};
static void wl_data_source_listener_send(void* data,
struct wl_data_source* wl_data_source,
const char* mime_type,
int32_t fd) {
MwLL self = data;
(void)wl_data_source;
(void)mime_type;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.clipboard_buffer != NULL) {
write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer));
close(fd);
}
WAYLAND_EVENT_OP_END(self);
};
static void wl_data_source_listener_cancelled(void* data,
struct wl_data_source* wl_data_source);
struct wl_data_source_listener wl_data_source_listener = {
.target = wl_data_source_listener_target,
.send = wl_data_source_listener_send,
.cancelled = wl_data_source_listener_cancelled,
};
static void wl_data_source_listener_cancelled(void* data,
struct wl_data_source* wl_data_source) {
MwLL self = data;
WAYLAND_EVENT_OP_START(self);
if(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);
wl_data_source_offer(self->wayland.clipboard_source.wl, "text/plain");
wl_data_source_offer(self->wayland.clipboard_source.wl, "text/plain;charset=utf-8");
wl_data_source_offer(self->wayland.clipboard_source.wl, "TEXT");
wl_data_source_offer(self->wayland.clipboard_source.wl, "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);
WAYLAND_EVENT_OP_END(self);
};
static void zwp_primary_selection_source_v1_send(void* data,
struct zwp_primary_selection_source_v1* wl_data_source,
const char* mime_type,
int32_t fd) {
MwLL self = data;
(void)wl_data_source;
(void)mime_type;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.clipboard_buffer != NULL) {
write(fd, self->wayland.clipboard_buffer, strlen(self->wayland.clipboard_buffer));
close(fd);
}
WAYLAND_EVENT_OP_END(self);
};
static void zwp_primary_selection_source_v1_cancelled(void* data,
struct zwp_primary_selection_source_v1* wl_data_source) {
MwLL self = data;
(void)wl_data_source;
WAYLAND_EVENT_OP_START(self);
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);
zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "text/plain");
zwp_primary_selection_source_v1_offer(self->wayland.clipboard_source.zwp, "text/plain;charset=utf-8");
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, "UTF8_STRING");
WAYLAND_EVENT_OP_END(self);
};
struct zwp_primary_selection_source_v1_listener zwp_primary_selection_source_v1_listener = {
.send = zwp_primary_selection_source_v1_send,
.cancelled = zwp_primary_selection_source_v1_cancelled,
};
/* wl_data_device_manager setup function */
static wayland_protocol_t* wl_data_device_manager_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->clipboard_manager.wl = wl_registry_bind(wayland->registry, name, &wl_data_device_manager_interface, 2);
wayland->clipboard_source.wl = wl_data_device_manager_create_data_source(wayland->clipboard_manager.wl);
wl_data_source_offer(wayland->clipboard_source.wl, "text/plain");
wl_data_source_offer(wayland->clipboard_source.wl, "text/plain;charset=utf-8");
wl_data_source_offer(wayland->clipboard_source.wl, "TEXT");
wl_data_source_offer(wayland->clipboard_source.wl, "STRING");
wl_data_source_offer(wayland->clipboard_source.wl, "UTF8_STRING");
wl_data_source_add_listener(wayland->clipboard_source.wl, &wl_data_source_listener, wayland);
if(wayland->pointer_seat) {
setup_clipboard((MwLL)wayland, wayland->pointer_seat);
}
return NULL;
}
static void wl_data_device_manager_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)data;
if(wayland->clipboard_manager.wl != NULL) {
wl_data_device_manager_destroy(wayland->clipboard_manager.wl);
wayland->clipboard_manager.wl = NULL;
}
if(wayland->clipboard_source.wl) {
// wl_data_source_destroy(wayland->clipboard_source.wl);
wayland->clipboard_source.wl = NULL;
}
}
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t* zwp_primary_selection_device_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->clipboard_manager.zwp = wl_registry_bind(wayland->registry, name, &zwp_primary_selection_device_manager_v1_interface, 1);
wayland->clipboard_source.zwp = zwp_primary_selection_device_manager_v1_create_source(wayland->clipboard_manager.zwp);
zwp_primary_selection_source_v1_offer(wayland->clipboard_source.zwp, "text/plain");
zwp_primary_selection_source_v1_offer(wayland->clipboard_source.zwp, "text/plain;charset=utf-8");
zwp_primary_selection_source_v1_offer(wayland->clipboard_source.zwp, "TEXT");
zwp_primary_selection_source_v1_offer(wayland->clipboard_source.zwp, "STRING");
zwp_primary_selection_source_v1_offer(wayland->clipboard_source.zwp, "UTF8_STRING");
zwp_primary_selection_source_v1_add_listener(wayland->clipboard_source.zwp, &zwp_primary_selection_source_v1_listener, wayland);
wayland->supports_zwp = MwTRUE;
if(wayland->pointer_seat) {
setup_zwp_clipboard((MwLL)wayland, wayland->pointer_seat);
}
return NULL;
}
static void zwp_primary_selection_device_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t* zwp_pointer_constraints_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->pointer_constraints = wl_registry_bind(wayland->registry, name, &zwp_pointer_constraints_v1_interface, 1);
return NULL;
}
static void zwp_pointer_constraints_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
static struct wl_surface* curSurface = NULL;
/* `wl_pointer.enter` callback */
static void pointer_enter(void* data, struct wl_pointer* wl_pointer, MwU32 serial,
struct wl_surface* surface, wl_fixed_t surface_x,
wl_fixed_t surface_y) {
MwLL self = data;
MwLL topmost_parent = self;
(void)surface_x;
(void)surface_y;
(void)wl_pointer;
WAYLAND_EVENT_OP_START(self);
while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent;
if(self->wayland.framebuffer.surface == surface) {
curSurface = surface;
self->wayland.pointer_serial = serial;
wl_pointer_set_cursor(self->wayland.pointer, self->wayland.pointer_serial, topmost_parent->wayland.cursor.surface, 0, 0);
}
if(self->wayland.backbuffer.surface == surface) {
curSurface = surface;
}
WAYLAND_EVENT_OP_END(self);
};
/* `wl_pointer.leave` callback */
static void pointer_leave(void* data, struct wl_pointer* wl_pointer, MwU32 serial,
struct wl_surface* surface) {
MwLL self = data;
(void)wl_pointer;
(void)serial;
(void)surface;
WAYLAND_EVENT_OP_START(self);
curSurface = NULL;
WAYLAND_EVENT_OP_END(self);
};
static void xdg_borderless_step_mdown(MwLL self, MwMouse p, MwU32 serial) {
if(self->wayland.type == MwLL_WAYLAND_TOPLEVEL && self->wayland.backbuffer.surface == curSurface) {
if(p.point.y >= (self->wayland.wh + CSD_BORDER_FRAME_TOP + CSD_BORDER_FRAME_BOTTOM) - 5) {
if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 5) {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM_RIGHT);
} else {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM);
}
} else if(p.point.y <= 5) {
if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 5) {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_TOP_RIGHT);
} else {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_TOP);
}
} else if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 5) {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_RIGHT);
} else if(p.point.x <= 5) {
xdg_toplevel_resize(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial, XDG_TOPLEVEL_RESIZE_EDGE_LEFT);
} else if(p.point.y <= CSD_BORDER_FRAME_TOP) {
if(p.point.x >= CSD_BORDER_FRAME_LEFT && p.point.x <= CSD_BORDER_FRAME_LEFT + 18) {
} else if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 21) {
} else if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 38) {
} else {
xdg_toplevel_move(self->wayland.toplevel->xdg_top_level, self->wayland.pointer_seat, serial);
}
}
};
}
static void xdg_borderless_step_mup(MwLL self, MwMouse p, MwU32 serial) {
(void)serial;
if(self->wayland.type == MwLL_WAYLAND_TOPLEVEL && self->wayland.backbuffer.surface == curSurface) {
if(p.point.y <= CSD_BORDER_FRAME_TOP) {
if(p.point.x >= CSD_BORDER_FRAME_LEFT && p.point.x <= CSD_BORDER_FRAME_LEFT + 18) {
MwLLDispatch(self, close, NULL);
} else if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 21) {
self->wayland.toplevel->maxim_state = !self->wayland.toplevel->maxim_state;
if(self->wayland.toplevel->maxim_state == 0) {
xdg_toplevel_unset_maximized(self->wayland.toplevel->xdg_top_level);
} else {
xdg_toplevel_set_maximized(self->wayland.toplevel->xdg_top_level);
}
self->wayland.setting_wh = 1;
MwLLWaylandRegionSetup(self);
} else if(p.point.x >= (self->wayland.ww + CSD_BORDER_FRAME_LEFT + CSD_BORDER_FRAME_RIGHT) - 38) {
xdg_toplevel_set_minimized(self->wayland.toplevel->xdg_top_level);
}
}
};
}
static void relative_pointer_motion(void* data,
struct zwp_relative_pointer_v1* pointer,
uint32_t timeHi,
uint32_t timeLo,
wl_fixed_t dx,
wl_fixed_t dy,
wl_fixed_t dxUnaccel,
wl_fixed_t dyUnaccel) {
MwLL self = data;
MwMouse p;
(void)pointer;
(void)timeHi;
(void)timeLo;
(void)dx;
(void)dy;
WAYLAND_EVENT_OP_START(self);
p.point.x = wl_fixed_to_int(dxUnaccel);
p.point.y = wl_fixed_to_int(dyUnaccel);
recursive_dispatch_move(self, &p);
if(self->wayland.locked_pointer) zwp_locked_pointer_v1_set_cursor_position_hint(self->wayland.locked_pointer, 0, CSD_BORDER_FRAME_TOP);
WAYLAND_EVENT_OP_END(self);
}
struct zwp_relative_pointer_v1_listener MwLLWaylandRelativePointerListener =
{
relative_pointer_motion};
/* zwp_primary_selection_device_manager_v1 setup function */
static wayland_protocol_t* zwp_relative_pointer_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->relative_pointer_manager = wl_registry_bind(wayland->registry, name, &zwp_relative_pointer_manager_v1_interface, 1);
return NULL;
}
static void zwp_relative_pointer_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* `wl_pointer.motion` callback */
static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time,
wl_fixed_t surface_x, wl_fixed_t surface_y) {
MwLL self = data;
MwLL topmost_parent = self;
MwMouse p;
MwBool inArea = MwFALSE;
MwLL currentlyHeldWidget = NULL;
(void)time;
(void)wl_pointer;
WAYLAND_EVENT_OP_START(self);
while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent;
currentlyHeldWidget = topmost_parent->wayland.currentlyHeldWidget;
if(self->wayland.framebuffer.surface) {
inArea |= self->wayland.framebuffer.surface == curSurface;
}
if(self->wayland.backbuffer.surface) {
inArea |= self->wayland.backbuffer.surface == curSurface;
}
self->wayland.cur_mouse_pos.x = wl_fixed_to_int(surface_x);
self->wayland.cur_mouse_pos.y = wl_fixed_to_int(surface_y);
if(currentlyHeldWidget != NULL) {
currentlyHeldWidget->wayland.cur_mouse_pos.x = wl_fixed_to_int(surface_x);
currentlyHeldWidget->wayland.cur_mouse_pos.y = wl_fixed_to_int(surface_y);
}
if(inArea) {
p.point = self->wayland.cur_mouse_pos;
MwLLDispatch(self, move, &p);
wl_pointer_set_cursor(self->wayland.pointer, self->wayland.pointer_serial, self->wayland.cursor.surface, 0, 0);
} else if(currentlyHeldWidget == self) {
p.point = currentlyHeldWidget->wayland.cur_mouse_pos;
MwLLDispatch(currentlyHeldWidget, move, &p);
}
WAYLAND_EVENT_OP_END(self);
};
/* `wl_pointer.button` callback */
static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 serial, MwU32 time, MwU32 button, MwU32 state) {
MwLL self = data;
MwMouse p;
MwBool inArea = MwFALSE;
MwLL topmost_parent = self;
(void)wl_pointer;
(void)serial;
(void)time;
WAYLAND_EVENT_OP_START(self);
p.point = self->wayland.cur_mouse_pos;
while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent;
if(self->wayland.framebuffer.surface) {
inArea |= self->wayland.framebuffer.surface == curSurface;
}
if(self->wayland.backbuffer.surface) {
inArea |= self->wayland.backbuffer.surface == curSurface;
}
if(inArea) {
switch(button) {
case BTN_LEFT:
p.button = MwMOUSE_LEFT;
break;
case BTN_MIDDLE:
p.button = MwMOUSE_MIDDLE;
break;
case BTN_RIGHT:
p.button = MwMOUSE_RIGHT;
break;
}
self->wayland.held_down = state == WL_POINTER_BUTTON_STATE_PRESSED;
switch(state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
if(button == BTN_LEFT) {
topmost_parent->wayland.focusedWidget = self;
}
MwLLDispatch(self, down, &p);
topmost_parent->wayland.currentlyHeldWidget = self;
break;
case WL_POINTER_BUTTON_STATE_RELEASED:
if(topmost_parent->wayland.currentlyHeldWidget != NULL) {
MwLLDispatch(topmost_parent->wayland.currentlyHeldWidget, up, &p);
topmost_parent->wayland.currentlyHeldWidget = NULL;
} else {
MwLLDispatch(self, up, &p);
}
break;
}
}
if(!self->wayland.has_decorations && self->wayland.do_csd) {
if(state != WL_POINTER_BUTTON_STATE_RELEASED)
xdg_borderless_step_mdown(self, p, serial);
else
xdg_borderless_step_mup(self, p, serial);
}
WAYLAND_EVENT_OP_END(self);
};
/* `wl_pointer.axis` callback */
static void pointer_axis(void* data, struct wl_pointer* wl_pointer, MwU32 time,
MwU32 axis, wl_fixed_t value) {
(void)data;
(void)wl_pointer;
(void)time;
(void)axis;
(void)value;
};
struct wl_pointer_listener pointer_listener = {
.enter = pointer_enter,
.leave = pointer_leave,
.motion = pointer_motion,
.button = pointer_button,
.axis = pointer_axis,
};
/* `wl_keyboard.keymap` callback */
static void keyboard_keymap(void* data,
struct wl_keyboard* wl_keyboard,
MwU32 format,
int32_t fd,
MwU32 size) {
MwLL self = data;
(void)wl_keyboard;
if(!self->wayland.parent) {
assert(format == WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1);
char* map_shm = (char*)mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0);
assert(map_shm != MAP_FAILED);
struct xkb_keymap* xkb_keymap = xkb_keymap_new_from_string(
self->wayland.xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1,
XKB_KEYMAP_COMPILE_NO_FLAGS);
munmap(map_shm, size);
close(fd);
struct xkb_state* xkb_state = xkb_state_new(xkb_keymap);
self->wayland.xkb_keymap = xkb_keymap;
self->wayland.xkb_state = xkb_state;
} else {
self->wayland.xkb_keymap = self->wayland.parent->wayland.xkb_keymap;
self->wayland.xkb_state = self->wayland.parent->wayland.xkb_state;
}
};
/* `wl_keyboard.enter` callback */
static void keyboard_enter(void* data,
struct wl_keyboard* wl_keyboard,
MwU32 serial,
struct wl_surface* surface,
struct wl_array* keys) {
MwLL self = data;
(void)wl_keyboard;
(void)surface;
(void)keys;
WAYLAND_EVENT_OP_START(self);
self->wayland.keyboard_serial = serial;
MwLLDispatch(self, focus_in, NULL);
WAYLAND_EVENT_OP_END(self);
};
/* `wl_keyboard.leave` callback */
static void keyboard_leave(void* data,
struct wl_keyboard* wl_keyboard,
MwU32 serial,
struct wl_surface* surface) {
MwLL self = data;
(void)wl_keyboard;
(void)serial;
(void)surface;
WAYLAND_EVENT_OP_START(self);
MwLLDispatch(self, focus_out, NULL);
WAYLAND_EVENT_OP_END(self);
};
/* `wl_keyboard.key` callback */
static void keyboard_key(void* data,
struct wl_keyboard* wl_keyboard,
MwU32 serial,
MwU32 _time,
MwU32 key,
MwU32 state) {
MwLL self = data;
MwBool inArea = 0;
MwLL topmost_parent = self;
(void)wl_keyboard;
(void)serial;
(void)_time;
while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.framebuffer.surface) {
inArea |= self->wayland.framebuffer.surface == curSurface;
}
if(self->wayland.backbuffer.surface) {
inArea |= self->wayland.backbuffer.surface == curSurface;
}
inArea |= (self == topmost_parent->wayland.focusedWidget);
if(inArea) {
xkb_layout_index_t layout;
MwU32 levels;
xkb_level_index_t level = 0;
const xkb_keysym_t* syms_out;
MwU32 keycode = key + 8;
MwU64 syms_num;
int i;
if(!self->wayland.xkb_keymap) {
WAYLAND_EVENT_OP_END(self);
return;
}
layout = xkb_state_key_get_layout(self->wayland.xkb_state, keycode);
levels =
xkb_keymap_num_levels_for_key(self->wayland.xkb_keymap, keycode, layout);
if((((self->wayland.mod_state & 1) == 1) || ((self->wayland.mod_state & 2) == 2)) && levels >= 2) {
level = 1;
} else if(levels >= 1) {
level = 0;
}
syms_num = xkb_keymap_key_get_syms_by_level(self->wayland.xkb_keymap, keycode, layout, level, &syms_out);
if(syms_out == NULL) {
WAYLAND_EVENT_OP_END(self);
return;
}
for(i = 0; i < syms_num; i++) {
int sym = syms_out[i];
int key = -1;
switch(sym) {
case XKB_KEY_BackSpace:
key = MwKEY_BACKSPACE;
break;
case XKB_KEY_Left:
key = MwKEY_LEFT;
break;
case XKB_KEY_Right:
key = MwKEY_RIGHT;
break;
case XKB_KEY_Up:
key = MwKEY_UP;
break;
case XKB_KEY_Down:
key = MwKEY_DOWN;
break;
case XKB_KEY_Return:
key = MwKEY_ENTER;
break;
case XKB_KEY_Escape:
key = MwKEY_ESCAPE;
break;
case XKB_KEY_Shift_L:
key = MwKEY_LEFTSHIFT;
break;
case XKB_KEY_Shift_R:
key = MwKEY_RIGHTSHIFT;
break;
case XKB_KEY_Alt_L:
case XKB_KEY_Alt_R:
key = MwKEY_ALT;
break;
case XKB_KEY_Control_L:
case XKB_KEY_Control_R:
key = MwKEY_CONTROL;
break;
default:
if(MwStringIsKeyUTF8(sym)) {
key = sym;
}
}
if((self->wayland.mod_state & 4) == 4) {
key |= MwKEY_CONTROL_FLAG;
}
if((self->wayland.mod_state & 8) == 8) {
key |= MwKEY_ALT_FLAG;
}
if(key != -1) {
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
MwLL topmost_parent = self;
while(topmost_parent->wayland.parent) {
MwLLDispatch(topmost_parent, key, &key);
topmost_parent = topmost_parent->wayland.parent;
}
self->wayland.holding_key = MwTRUE;
self->wayland.last_pressed_key = key;
} else {
MwLLDispatch(self, key_released, &key);
self->wayland.holding_key = MwFALSE;
}
self->wayland.start_time = MwTimeGetTick();
self->wayland.next_elapsed = self->wayland.keyboard_delay;
}
}
}
if(!MwWaylandVulkan) {
MwLLDispatch(self, draw, NULL);
}
WAYLAND_EVENT_OP_END(self);
};
/* `wl_keyboard.modifiers` callback */
static void keyboard_modifiers(void* data,
struct wl_keyboard* wl_keyboard,
MwU32 serial,
MwU32 mods_depressed,
MwU32 mods_latched,
MwU32 mods_locked,
MwU32 group) {
MwLL self = data;
(void)wl_keyboard;
(void)serial;
(void)group;
(void)mods_latched;
WAYLAND_EVENT_OP_START(self);
self->wayland.mod_state = 0;
self->wayland.mod_state |= mods_depressed;
self->wayland.mod_state |= mods_locked;
WAYLAND_EVENT_OP_END(self);
};
static void setup_zwp_clipboard(MwLL self, struct wl_seat* wl_seat) {
wl_clipboard_device_context_t* device_ctx_zwp = NULL;
device_ctx_zwp = malloc(sizeof(wl_clipboard_device_context_t));
memset(device_ctx_zwp, 0, sizeof(wl_clipboard_device_context_t));
device_ctx_zwp->device.zwp = zwp_primary_selection_device_manager_v1_get_device(self->wayland.clipboard_manager.zwp, wl_seat);
device_ctx_zwp->ll = self;
zwp_primary_selection_device_v1_add_listener(device_ctx_zwp->device.zwp, &zwp_primary_selection_device_v1_listener, device_ctx_zwp);
arrpush(self->wayland.clipboard_devices_zwp, device_ctx_zwp);
}
static void setup_clipboard(MwLL self, struct wl_seat* wl_seat) {
wl_clipboard_device_context_t* device_ctx_wl = malloc(sizeof(wl_clipboard_device_context_t));
memset(device_ctx_wl, 0, sizeof(wl_clipboard_device_context_t));
device_ctx_wl->device.wl = wl_data_device_manager_get_data_device(self->wayland.clipboard_manager.wl, wl_seat);
device_ctx_wl->ll = self;
wl_data_device_add_listener(device_ctx_wl->device.wl, &wl_data_device_listener, device_ctx_wl);
arrpush(self->wayland.clipboard_devices_wl, device_ctx_wl);
};
static void keyboard_repeat_info(void* data,
struct wl_keyboard* wl_keyboard,
int32_t rate,
int32_t delay) {
MwLL self = data;
(void)wl_keyboard;
self->wayland.keyboard_rate = rate;
self->wayland.keyboard_delay = delay;
};
struct wl_keyboard_listener keyboard_listener = {
.keymap = keyboard_keymap,
.enter = keyboard_enter,
.leave = keyboard_leave,
.key = keyboard_key,
.modifiers = keyboard_modifiers,
.repeat_info = keyboard_repeat_info,
};
/* `wl_seat.name` callback */
static void
wl_seat_name(void* data, struct wl_seat* wl_seat, const char* name) {
(void)data;
(void)wl_seat;
(void)name;
};
/* `wl_seat.capabilities` callback */
static void wl_seat_capabilities(void* data, struct wl_seat* wl_seat,
MwU32 capabilities) {
MwLL self = data;
WAYLAND_EVENT_OP_START(self);
if(capabilities & WL_SEAT_CAPABILITY_KEYBOARD) {
self->wayland.keyboard = wl_seat_get_keyboard(wl_seat);
wl_keyboard_add_listener(self->wayland.keyboard, &keyboard_listener, data);
}
if(capabilities & WL_SEAT_CAPABILITY_POINTER) {
self->wayland.pointer_seat = wl_seat;
self->wayland.pointer = wl_seat_get_pointer(wl_seat);
wl_pointer_add_listener(self->wayland.pointer, &pointer_listener, data);
}
WAYLAND_EVENT_OP_END(self);
if(self->wayland.clipboard_manager.wl)
setup_clipboard(self, self->wayland.pointer_seat);
if(self->wayland.clipboard_manager.zwp)
setup_zwp_clipboard(self, self->wayland.pointer_seat);
};
static void output_geometry(void* data,
struct wl_output* wl_output,
int32_t x,
int32_t y,
int32_t physical_width,
int32_t physical_height,
int32_t subpixel,
const char* make,
const char* model,
int32_t transform) {
(void)data;
(void)wl_output;
(void)x;
(void)y;
(void)physical_width;
(void)physical_height;
(void)subpixel;
(void)make;
(void)model;
(void)transform;
};
static void output_mode(void* data,
struct wl_output* wl_output,
uint32_t flags,
int32_t width,
int32_t height,
int32_t refresh) {
MwLL self = data;
(void)wl_output;
(void)flags;
(void)refresh;
self->wayland.mw = width;
self->wayland.mh = height;
};
struct wl_output_listener output_listener = {
.geometry = output_geometry,
.mode = output_mode,
};
/* `xdg_wm_base.ping` callback */
void xdg_wm_base_ping(void* data,
struct xdg_wm_base* xdg_wm_base,
MwU32 serial) {
(void)data;
xdg_wm_base_pong(xdg_wm_base, serial);
};
/* wl_seat setup function */
static wayland_protocol_t* wl_seat_setup(MwU32 name, MwLL ll, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
MwU32 ver = (version >= 4) ? 4 : 1;
proto->listener = malloc(sizeof(struct wl_seat_listener));
((struct wl_seat_listener*)proto->listener)->name = wl_seat_name;
((struct wl_seat_listener*)proto->listener)->capabilities = wl_seat_capabilities;
wl_seat_add_listener(wl_registry_bind(ll->wayland.registry, name, &wl_seat_interface, ver), proto->listener, ll);
return proto;
}
static void wl_seat_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
free(data->listener);
free(data);
}
/* wl_output setup function */
static wayland_protocol_t* wl_output_setup(MwU32 name, MwLL ll, MwU32 version) {
(void)version;
ll->wayland.output = wl_registry_bind(ll->wayland.registry, name, &wl_output_interface, 1);
wl_output_add_listener(ll->wayland.output, &output_listener, ll);
return NULL;
}
static void wl_output_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* wl_compositor setup function */
static wayland_protocol_t* wl_compositor_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->compositor = wl_registry_bind(wayland->registry, name, &wl_compositor_interface, 1);
return NULL;
}
static void wl_compositor_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* wl_subcompositor setup function */
static wayland_protocol_t* wl_subcompositor_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
if(wayland->type == MwLL_WAYLAND_TOPLEVEL) {
wayland->toplevel->scompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1);
} else {
wayland->sublevel->subcompositor = wl_registry_bind(wayland->registry, name, &wl_subcompositor_interface, 1);
}
return NULL;
}
static void wl_subcompositor_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)data;
if(wayland->type == MwLL_WAYLAND_TOPLEVEL) {
wl_subcompositor_destroy(wayland->toplevel->scompositor);
} else {
wl_subcompositor_destroy(wayland->sublevel->subcompositor);
}
}
/* xdg_wm_base setup function */
static wayland_protocol_t* xdg_wm_base_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
(void)version;
proto->listener = malloc(sizeof(struct xdg_wm_base_listener));
((struct xdg_wm_base_listener*)proto->listener)->ping = xdg_wm_base_ping;
proto->context = wl_registry_bind(wayland->registry, name, &xdg_wm_base_interface, 1);
xdg_wm_base_add_listener(proto->context, proto->listener, wayland);
return proto;
}
static void xdg_wm_base_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
free(data->listener);
free(data);
}
/* xdg_wm_base setup function */
static wayland_protocol_t* wp_viewporter_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
(void)version;
proto->context = wl_registry_bind(wayland->registry, name, &wp_viewporter_interface, 1);
return proto;
}
static void wp_viewporter_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* zxdg_decoration_manager_v1 setup function */
static wayland_protocol_t* zxdg_decoration_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
zxdg_decoration_manager_v1_context_t* ctx = malloc(sizeof(zxdg_decoration_manager_v1_context_t));
(void)version;
proto->listener = NULL;
ctx->manager = wl_registry_bind(wayland->registry, name, &zxdg_decoration_manager_v1_interface, 1);
;
proto->context = ctx;
return proto;
}
static void zxdg_decoration_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
/* wl_shm setup function */
static wayland_protocol_t* wl_shm_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
(void)version;
wayland->framebuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1);
wayland->backbuffer.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1);
wayland->cursor.shm = wl_registry_bind(wayland->registry, name, &wl_shm_interface, 1);
return NULL;
}
static void wl_shm_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
(void)data;
}
static wayland_protocol_t* xdg_toplevel_icon_manager_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
(void)version;
proto->context = wl_registry_bind(wayland->registry, name, &xdg_toplevel_icon_manager_v1_interface, 1);
proto->listener = NULL;
return proto;
}
static void xdg_toplevel_icon_manager_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
free(data->listener);
free(data);
}
static wayland_protocol_t* zwlr_layer_shell_v1_setup(MwU32 name, struct _MwLLWayland* wayland, MwU32 version) {
wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t));
proto->context = wl_registry_bind(wayland->registry, name, &zwlr_layer_shell_v1_interface, version);
proto->listener = NULL;
return proto;
}
static void zwlr_layer_shell_v1_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) {
(void)wayland;
free(data);
}
/* Function for setting up the callbacks/structs that will be registered upon the relevant interfaces being found. */
void MwLLWaylandSetupCallbacks(struct _MwLLWayland* wayland) {
/* Convience macro for adding the interface functions to the setup map */
#define WL_INTERFACE(interface) \
do { \
wayland_protocol_callback_table_t* cb = malloc(sizeof(wayland_protocol_callback_table_t)); \
cb->setup = (wl_setup_func*)interface##_setup; \
cb->destroy = (wl_destroy_func*)interface##_interface_destroy; \
shput(wayland->wl_protocol_setup_map, interface##_interface.name, cb); \
} while(0);
wayland->registry_listener.global = new_protocol;
wayland->registry_listener.global_remove = protocol_removed;
if(wayland->wl_protocol_map) {
shfree(wayland->wl_protocol_map);
}
if(wayland->wl_protocol_setup_map) {
shfree(wayland->wl_protocol_setup_map);
}
wayland->wl_protocol_setup_map = NULL;
wayland->wl_protocol_map = NULL;
sh_new_arena(wayland->wl_protocol_map);
sh_new_arena(wayland->wl_protocol_setup_map);
WL_INTERFACE(wl_shm);
WL_INTERFACE(wl_compositor);
WL_INTERFACE(wl_seat);
WL_INTERFACE(wl_output);
WL_INTERFACE(wl_data_device_manager);
WL_INTERFACE(zwp_primary_selection_device_manager_v1);
WL_INTERFACE(zwp_pointer_constraints_v1);
WL_INTERFACE(zwp_relative_pointer_manager_v1);
WL_INTERFACE(xdg_wm_base);
WL_INTERFACE(zwlr_layer_shell_v1); /* Only used for layer surface, but we use it always if it's turned into a tool window */
if(wayland->type == MwLL_WAYLAND_TOPLEVEL) {
WL_INTERFACE(wp_viewporter);
WL_INTERFACE(zxdg_decoration_manager_v1);
WL_INTERFACE(xdg_toplevel_icon_manager_v1);
WL_INTERFACE(wl_subcompositor);
} else if(wayland->type == MwLL_WAYLAND_POPUP) {
} else if(wayland->type == MwLL_WAYLAND_LAYER_SURFACE) {
WL_INTERFACE(wp_viewporter);
WL_INTERFACE(wl_subcompositor);
} else {
WL_INTERFACE(wl_subcompositor);
}
#undef WL_INTERFACE
}