several wayland fixes, new util function MwIsUTF8 so that both backends can consistently check if something is a valid utf8
All checks were successful
pyrite-dev/milsko/pipeline/head This commit looks good

This commit is contained in:
IoIxD 2026-03-27 15:14:04 -07:00
commit 4d694bba49
5 changed files with 182 additions and 66 deletions

View file

@ -311,10 +311,6 @@ struct _MwLLWaylandTopLevel {
struct xdg_toplevel_listener xdg_toplevel_listener;
struct xdg_surface_listener xdg_surface_listener;
struct xkb_context* xkb_context;
struct xkb_keymap* xkb_keymap;
struct xkb_state* xkb_state;
MwBool compositor_created;
MwBool xdg_wm_base_created;
MwBool xdg_surface_created;
@ -416,6 +412,10 @@ struct _MwLLWayland {
MwBool has_decorations;
char title[255];
struct xkb_context* xkb_context;
struct xkb_keymap* xkb_keymap;
struct xkb_state* xkb_state;
struct wl_display* display;
struct wl_registry* registry;
struct wl_compositor* compositor;
@ -475,6 +475,8 @@ struct _MwLLWayland {
MwBool force_render;
MwBool did_event_loop_early;
MwBool dispatching_resize;
struct _MwLLWaylandShmBuffer framebuffer;
struct _MwLLWaylandShmBuffer backbuffer;
struct _MwLLWaylandShmBuffer cursor;

View file

@ -41,6 +41,13 @@ MWDECL void MwStringSize(char* out, MwOffset size);
*/
MWDECL void MwStringTime(char* out, time_t t);
/*!
* @brief Check if the given key is UTF8
* @param key Input
* @return whether its utf8
*/
MWDECL MwBool MwIsKeyUTF8(MwU32 key);
#ifdef __cplusplus
}
#endif

View file

@ -104,7 +104,7 @@ static void wl_flush(MwLL handle) {
}
}
/* Recursively dispatch a draw event to a widget and its children */
/* Recursively dispatch a resize event to a widget and its children */
static void recursive_dispatch_resize(MwLL handle) {
MwWidget h = (MwWidget)handle->common.user;
if(h) {
@ -118,6 +118,20 @@ static void recursive_dispatch_resize(MwLL handle) {
}
};
/* Recursively dispatch a draw event to a widget and its children */
static void recursive_dispatch_draw(MwLL handle) {
MwWidget h = (MwWidget)handle->common.user;
if(h) {
int i;
for(i = 0; i < arrlen(h->children); i++) {
MwDispatch(h->children[i], draw);
if(arrlen(h->children[i]->children) > 0) {
recursive_dispatch_draw(h->children[i]->lowlevel);
}
}
}
};
/* Recursively dispatch a key event to a widget and its children */
static void recursive_dispatch_key(MwLL handle, int* k) {
MwWidget h = (MwWidget)handle->common.user;
@ -482,10 +496,13 @@ static void pointer_enter(void* data, struct wl_pointer* wl_pointer, MwU32 seria
/* `wl_pointer.leave` callback */
static void pointer_leave(void* data, struct wl_pointer* wl_pointer, MwU32 serial,
struct wl_surface* surface) {
(void)data;
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(MwLL self, MwLLMouse p, MwU32 serial) {
@ -553,6 +570,7 @@ 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) {
MwLL self = data;
MwLLMouse p;
MwBool inArea = MwFALSE;
(void)wl_pointer;
(void)serial;
@ -562,10 +580,13 @@ static void pointer_button(void* data, struct wl_pointer* wl_pointer, MwU32 seri
p.point = self->wayland.cur_mouse_pos;
// p.point.x > x && p.point.x < x + self->wayland.ww && p.point.y > y && p.point.y < y + self->wayland.wh
if(
self->wayland.framebuffer.surface == curSurface ||
self->wayland.backbuffer.surface == curSurface) {
if(self->wayland.framebuffer.surface) {
inArea |= self->wayland.framebuffer.surface == curSurface;
}
if(self->wayland.backbuffer.surface) {
inArea |= self->wayland.backbuffer.surface == curSurface;
}
if(inArea) {
int i;
switch(button) {
@ -637,22 +658,24 @@ static void keyboard_keymap(void* data,
(void)wl_keyboard;
if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
struct _MwLLWaylandTopLevel* wayland = self->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(
wayland->xkb_context, map_shm, XKB_KEYMAP_FORMAT_TEXT_V1,
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);
wayland->xkb_keymap = xkb_keymap;
wayland->xkb_state = xkb_state;
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;
}
};
@ -700,37 +723,50 @@ static void keyboard_key(void* data,
MwU32 time,
MwU32 key,
MwU32 state) {
MwLL self = data;
MwLL self = data;
MwBool inArea;
(void)wl_keyboard;
(void)serial;
(void)time;
WAYLAND_EVENT_OP_START(self);
if(self->wayland.type == MWLL_WAYLAND_TOPLEVEL) {
struct _MwLLWaylandTopLevel* wayland = self->wayland.toplevel;
xkb_layout_index_t layout;
xkb_level_index_t level;
const xkb_keysym_t* syms_out;
MwU32 keycode = key + 8;
MwU64 syms_num;
int i;
if(self->wayland.framebuffer.surface) {
inArea |= self->wayland.framebuffer.surface == curSurface;
}
if(inArea) {
xkb_layout_index_t layout;
MwU32 levels;
xkb_level_index_t level;
const xkb_keysym_t* syms_out;
MwU32 keycode = key + 8;
MwU64 syms_num;
int i;
if(!wayland->xkb_keymap) {
if(!self->wayland.xkb_keymap) {
return;
}
layout = xkb_state_key_get_layout(wayland->xkb_state, keycode);
level =
xkb_keymap_num_levels_for_key(wayland->xkb_keymap, keycode, layout) - 1;
syms_num = xkb_keymap_key_get_syms_by_level(wayland->xkb_keymap, keycode, layout, level, &syms_out);
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) {
return;
}
for(i = 0; i < syms_num; i++) {
int sym = syms_out[i];
int key;
int sym = syms_out[i];
int key = -1;
const char* name;
switch(sym) {
case XKB_KEY_BackSpace:
key = MwLLKeyBackSpace;
@ -768,8 +804,11 @@ static void keyboard_key(void* data,
key = MwLLKeyControl;
break;
default:
key = sym;
if(MwIsKeyUTF8(sym)) {
key = sym;
}
}
if((self->wayland.mod_state & 4) == 4) {
/* clipboard paste */
if(key == 'V') {
@ -785,10 +824,12 @@ static void keyboard_key(void* data,
key |= MwLLAltMask;
}
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
recursive_dispatch_key(self, &key);
} else {
recursive_dispatch_key_released(self, &key);
if(key != -1) {
if(state == WL_KEYBOARD_KEY_STATE_PRESSED) {
MwLLDispatch(self, key, &key);
} else {
MwLLDispatch(self, key_released, &key);
}
}
}
}
@ -1139,7 +1180,6 @@ static void xdg_toplevel_configure(void* data,
region_setup(self);
MwLLDispatch(self, resize, NULL);
MwLLDispatch(self, draw, NULL);
recursive_dispatch_resize(self);
if(self->wayland.pointer_constrained) {
@ -1165,6 +1205,8 @@ static void xdg_surface_configure(
xdg_surface_ack_configure(xdg_surface, serial);
MwLLDispatch(self, draw, NULL);
if(self->wayland.configured) {
update_buffer(self, &self->wayland.framebuffer);
update_buffer(self, &self->wayland.backbuffer);
@ -1188,6 +1230,7 @@ static void wl_shm_interface_destroy(struct _MwLLWayland* wayland, wayland_proto
}
static void update_buffer(MwLL self, struct _MwLLWaylandShmBuffer* buffer) {
(void)self;
memcpy(buffer->buf, buffer->buf_back, buffer->buf_size);
wl_surface_commit(buffer->surface);
}
@ -1383,14 +1426,13 @@ static void setup_callbacks(struct _MwLLWayland* wayland) {
WL_INTERFACE(wl_data_device_manager);
WL_INTERFACE(zwp_primary_selection_device_manager_v1);
WL_INTERFACE(zwp_pointer_constraints_v1);
WL_INTERFACE(xdg_wm_base);
if(wayland->type == MWLL_WAYLAND_TOPLEVEL) {
WL_INTERFACE(xdg_wm_base);
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) {
WL_INTERFACE(xdg_wm_base);
} else {
WL_INTERFACE(wl_subcompositor);
}
@ -1428,7 +1470,7 @@ static void setup_toplevel(MwLL r, int x, int y) {
r->wayland.has_decorations = MwTRUE;
}
r->wayland.toplevel->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
r->wayland.xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS);
/* Create a wl_surface, a xdg_surface and a xdg_toplevel */
r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor);
@ -1489,11 +1531,11 @@ static void destroy_toplevel(MwLL r) {
xdg_toplevel_destroy(r->wayland.toplevel->xdg_top_level);
xkb_keymap_unref(r->wayland.toplevel->xkb_keymap);
xkb_keymap_unref(r->wayland.xkb_keymap);
xkb_state_unref(r->wayland.toplevel->xkb_state);
xkb_state_unref(r->wayland.xkb_state);
xkb_context_unref(r->wayland.toplevel->xkb_context);
xkb_context_unref(r->wayland.xkb_context);
free(r->wayland.toplevel);
@ -1529,10 +1571,8 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) {
}
r->wayland.framebuffer.surface = wl_compositor_create_surface(compositor);
if(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base))
r->wayland.sublevel->xdg_surface =
xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface);
r->wayland.sublevel->xdg_surface =
xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(r->wayland, xdg_wm_base)->context, r->wayland.framebuffer.surface);
r->wayland.sublevel->subsurface = wl_subcompositor_get_subsurface(r->wayland.sublevel->subcompositor, r->wayland.framebuffer.surface, parent_surface);
@ -1543,6 +1583,9 @@ static void setup_sublevel(MwLL parent, MwLL r, int x, int y) {
wl_subsurface_place_above(r->wayland.sublevel->subsurface, parent->wayland.framebuffer.surface);
}
r->wayland.xkb_keymap = parent->wayland.xkb_keymap;
r->wayland.xkb_state = parent->wayland.xkb_state;
r->wayland.configured = MwTRUE;
}
@ -1630,7 +1673,9 @@ static void setup_popup(MwLL r, int x, int y, MwLL parent) {
r->wayland.popup->xdg_positioner,
r->wayland.x, r->wayland.y, r->wayland.ww, r->wayland.wh);
xdg_surface = topmost_parent->wayland.toplevel->xdg_surface;
xdg_surface = topmost_parent->wayland.toplevel->xdg_surface;
r->wayland.xkb_keymap = topmost_parent->wayland.xkb_keymap;
r->wayland.xkb_state = topmost_parent->wayland.xkb_state;
r->wayland.framebuffer.surface = wl_compositor_create_surface(r->wayland.compositor);
@ -1847,19 +1892,24 @@ static void MwLLSetWHImpl(MwLL handle, int w, int h) {
region_invalidate(handle);
/* Prevent an integer underflow when the w/h is too low */
if((w < 10 || h < 10)) {
handle->wayland.ww = 10;
handle->wayland.wh = 10;
goto refresh;
if((w < 2 || h < 2)) {
handle->wayland.ww = 2;
handle->wayland.wh = 2;
} else {
handle->wayland.ww = w;
handle->wayland.wh = h;
}
handle->wayland.ww = w;
handle->wayland.wh = h;
if(handle->wayland.type == MWLL_WAYLAND_TOPLEVEL && handle->wayland.configured) {
xdg_surface_set_window_geometry(handle->wayland.toplevel->xdg_surface, 0, 0, handle->wayland.ww, handle->wayland.wh);
}
if(handle->wayland.type == MWLL_WAYLAND_SUBLEVEL && handle->wayland.configured && !handle->wayland.dispatching_resize) {
handle->wayland.dispatching_resize = MwTRUE;
MwLLDispatch(handle, resize, NULL);
handle->wayland.dispatching_resize = MwFALSE;
}
if(handle->wayland.type == MWLL_WAYLAND_POPUP) {
destroy_popup(handle);
wl_flush(handle);
@ -2098,9 +2148,7 @@ static void MwLLNextEventImpl(MwLL handle) {
}
if(handle->wayland.force_render) {
// if(!handle->wayland.events_pending) {
MwLLDispatch(handle, draw, NULL);
// }
if(handle->wayland.configured) update_buffer(handle, &handle->wayland.framebuffer);
handle->wayland.force_render = 0;
}
@ -2287,7 +2335,7 @@ static void MwLLSetCursorImpl(MwLL handle, MwCursor* image, MwCursor* mask) {
/* If there's currently a pointer, set it up. (Otherwise, it'll be setup during the pointer enter event) */
if(handle->wayland.pointer != NULL) {
wl_pointer_set_cursor(handle->wayland.pointer, handle->wayland.pointer_serial, handle->wayland.cursor.surface, 0, 0);
// wl_pointer_set_cursor(handle->wayland.pointer, handle->wayland.pointer_serial, handle->wayland.cursor.surface, 0, 0);
}
}
@ -2406,7 +2454,8 @@ static void MwLLGetScreenSizeImpl(MwLL handle, MwRect* rect) {
}
static void MwLLBeginStateChangeImpl(MwLL handle) {
MwLLShow(handle, 0);
(void)handle;
/* no-op */
}
static void MwLLEndStateChangeImpl(MwLL handle) {
@ -2439,9 +2488,8 @@ static void MwLLEndStateChangeImpl(MwLL handle) {
int i;
for(i = 0; i < arrlen(w->children); i++) {
if(w->children[i]->lowlevel->wayland.type == MWLL_WAYLAND_SUBLEVEL) {
MwLL child = w->children[i]->lowlevel;
child->wayland.sublevel->xdg_surface =
xdg_wm_base_get_xdg_surface(WAYLAND_GET_INTERFACE(handle->wayland, xdg_wm_base)->context, handle->wayland.framebuffer.surface);
MwLL child = w->children[i]->lowlevel;
child->wayland.sublevel->xdg_surface = handle->wayland.popup->xdg_surface;
wl_subsurface_destroy(child->wayland.sublevel->subsurface);
wl_flush(handle);
@ -2466,8 +2514,6 @@ static void MwLLEndStateChangeImpl(MwLL handle) {
handle->wayland.detatching = MwFALSE;
}
MwLLShow(handle, 1);
}
static int MwLLWaylandCallInitImpl(void) {

View file

@ -558,8 +558,7 @@ static void MwLLNextEventImpl(MwLL handle) {
strcpy(str, s);
}
/* HACK: this is bad, you can guess why */
if(strlen(str) == 1) {
if(MwIsKeyUTF8(sym)) {
char s = str[0];
if(ev.xkey.state & (ShiftMask | LockMask) && !(ev.xkey.state & (ControlMask | Mod1Mask))) {

View file

@ -37,3 +37,65 @@ void MwStringTime(char* out, time_t t) {
sprintf(out, "%s %2d %02d:%02d %d", months[tm->tm_mon], tm->tm_mday, tm->tm_hour, tm->tm_min, 1900 + tm->tm_year);
}
}
MWDECL MwBool MwIsKeyUTF8(MwU32 key) {
unsigned char bytes[sizeof(MwU32)];
memcpy(bytes, &key, sizeof(MwU32));
int i = 0;
for(i = 0; i < sizeof(MwU32); i++) {
if(( // ASCII
// use bytes[0] <= 0x7F to allow ASCII control characters
bytes[0] == 0x09 ||
bytes[0] == 0x0A ||
bytes[0] == 0x0D ||
(0x20 <= bytes[0] && bytes[0] <= 0x7E))) {
continue;
}
if(( // non-overlong 2-byte
(0xC2 <= bytes[0] && bytes[0] <= 0xDF) &&
(0x80 <= bytes[1] && bytes[1] <= 0xBF))) {
continue;
}
if(( // excluding overlongs
bytes[0] == 0xE0 &&
(0xA0 <= bytes[1] && bytes[1] <= 0xBF) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF)) ||
( // straight 3-byte
((0xE1 <= bytes[0] && bytes[0] <= 0xEC) ||
bytes[0] == 0xEE ||
bytes[0] == 0xEF) &&
(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF)) ||
( // excluding surrogates
bytes[0] == 0xED &&
(0x80 <= bytes[1] && bytes[1] <= 0x9F) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF))) {
continue;
}
if(( // planes 1-3
bytes[0] == 0xF0 &&
(0x90 <= bytes[1] && bytes[1] <= 0xBF) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
(0x80 <= bytes[3] && bytes[3] <= 0xBF)) ||
( // planes 4-15
(0xF1 <= bytes[0] && bytes[0] <= 0xF3) &&
(0x80 <= bytes[1] && bytes[1] <= 0xBF) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
(0x80 <= bytes[3] && bytes[3] <= 0xBF)) ||
( // plane 16
bytes[0] == 0xF4 &&
(0x80 <= bytes[1] && bytes[1] <= 0x8F) &&
(0x80 <= bytes[2] && bytes[2] <= 0xBF) &&
(0x80 <= bytes[3] && bytes[3] <= 0xBF))) {
continue;
}
return 0;
}
return 1;
}