#include #include #include "../../../external/stb_ds.h" /* TODO: find out what FreeBSD and such wants us to include */ #ifdef __FreeBSD__ /* XXX: Untested (nishi) */ #include #else #include #include #endif #include static void setup_clipboard(MwLL self, struct wl_seat* wl_seat); static void setup_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)); /* 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) { 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) { 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) { 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) { 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) { 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) { 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.type == MwLL_WAYLAND_TOPLEVEL) { 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; } } else { MwLLDispatch(self, key_released, &key); } } } } 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); }; struct wl_keyboard_listener keyboard_listener = { .keymap = keyboard_keymap, .enter = keyboard_enter, .leave = keyboard_leave, .key = keyboard_key, .modifiers = keyboard_modifiers, }; /* `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) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); 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, 1), proto->listener, ll); return proto; } static void wl_seat_interface_destroy(struct _MwLLWayland* wayland, wayland_protocol_t* data) { (void)wayland; free(data->listener); } /* wl_output setup function */ static wayland_protocol_t* wl_output_setup(MwU32 name, MwLL ll) { 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) { 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) { 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; wl_subcompositor_destroy(wayland->sublevel->subcompositor); } /* xdg_wm_base setup function */ static wayland_protocol_t* xdg_wm_base_setup(MwU32 name, struct _MwLLWayland* wayland) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); 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); } /* xdg_wm_base setup function */ static wayland_protocol_t* wp_viewporter_setup(MwU32 name, struct _MwLLWayland* wayland) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); 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) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); zxdg_decoration_manager_v1_context_t* ctx = malloc(sizeof(zxdg_decoration_manager_v1_context_t)); 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) { 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) { wayland_protocol_t* proto = malloc(sizeof(wayland_protocol_t)); 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); } /* 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; 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); 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 { WL_INTERFACE(wl_subcompositor); } #undef WL_INTERFACE }