z-index system for wayland
All checks were successful
pyrite-dev/milsko/pipeline/head This commit looks good

fixes clicks penetrating to widgets behind one another and allows MwLLRaiseImpl to be implemented
This commit is contained in:
IoIxD 2026-09-27 05:46:28 +09:00 • committed by IoI_xD
commit e1824cffa0
3 changed files with 122 additions and 106 deletions

View file

@ -341,6 +341,9 @@ struct _MwLLWayland {
MwBool is_toplevel; MwBool is_toplevel;
/* Stacking order among sibling sublevels. Higher values are drawn on top and get the pointer first. */
MwI32 z_index;
MwBool is_clipping; MwBool is_clipping;
MwRect clip; MwRect clip;
@ -505,6 +508,11 @@ void MwLLWaylandClipboardRead(wl_clipboard_device_context_t* ctx, int clipboard_
/* Flush Wayland events */ /* Flush Wayland events */
void MwLLWaylandFlush(MwLL handle); void MwLLWaylandFlush(MwLL handle);
/* Get the sublevel children of a widget ordered from lowest to highest z-index (ties keep creation order). Free the result with arrfree. */
MwLL* MwLLWaylandSublevelsByZIndex(MwLL handle);
/* Set a sublevel's z-index and schedule a redraw so the new stacking order is shown. */
void MwLLWaylandSetZIndex(MwLL handle, MwI32 z_index);
/* recursively dispatch the key event to focused widgets. */ /* recursively dispatch the key event to focused widgets. */
void MwLLRecursiveKeyDispatch(MwLL self, int* k, MwBool down); void MwLLRecursiveKeyDispatch(MwLL self, int* k, MwBool down);

View file

@ -721,33 +721,6 @@ static void pointer_motion(void* data, struct wl_pointer* wl_pointer, MwU32 time
} }
}; };
static void recursive_dispatch_mouse_down(MwLL handle, MwMouse* p) {
MwWidget h = (MwWidget)handle->common.internal;
MwLLDispatch(handle, down, p);
if(h) {
int i;
for(i = 0; i < arrlen(h->children); i++) {
MwLLDispatch(h->children[i]->lowlevel, down, p);
if(arrlen(h->children[i]->children) > 0) {
recursive_dispatch_mouse_down(h->children[i]->lowlevel, p);
}
}
}
};
static void recursive_dispatch_mouse_up(MwLL handle, MwMouse* p) {
MwWidget h = (MwWidget)handle->common.internal;
MwLLDispatch(handle, up, p);
if(h) {
int i;
for(i = 0; i < arrlen(h->children); i++) {
MwLLDispatch(h->children[i]->lowlevel, up, p);
if(arrlen(h->children[i]->children) > 0) {
recursive_dispatch_mouse_up(h->children[i]->lowlevel, p);
}
}
}
};
static MwBool hit_detect(MwLL child, MwLL* _topmost_parent, MwPoint* _point, MwPoint* _relative_mouse_pos, MwPoint* _absolute_pos) { static MwBool hit_detect(MwLL child, MwLL* _topmost_parent, MwPoint* _point, MwPoint* _relative_mouse_pos, MwPoint* _absolute_pos) {
MwLL topmost_parent = child; MwLL topmost_parent = child;
MwPoint point; MwPoint point;
@ -761,9 +734,7 @@ static MwBool hit_detect(MwLL child, MwLL* _topmost_parent, MwPoint* _point, MwP
if(topmost_parent) { if(topmost_parent) {
/* if the topmost parent is a popup/subwindow then its x/y is irrelevant to us. */ /* if the topmost parent is a popup/subwindow then its x/y is irrelevant to us. */
if(topmost_parent->wayland.type != MwLL_WAYLAND_POPUP && topmost_parent->wayland.type != MwLL_WAYLAND_TOPLEVEL) { if(topmost_parent->wayland.type != MwLL_WAYLAND_POPUP && topmost_parent->wayland.type != MwLL_WAYLAND_TOPLEVEL) {
if(topmost_parent->wayland.x > 0)
absolute_pos.x += topmost_parent->wayland.x; absolute_pos.x += topmost_parent->wayland.x;
if(topmost_parent->wayland.y > 0)
absolute_pos.y += topmost_parent->wayland.y; absolute_pos.y += topmost_parent->wayland.y;
} }
} }
@ -783,22 +754,42 @@ static MwBool hit_detect(MwLL child, MwLL* _topmost_parent, MwPoint* _point, MwP
point.y >= absolute_pos.y && point.y <= absolute_pos.y + child->wayland.wh; point.y >= absolute_pos.y && point.y <= absolute_pos.y + child->wayland.wh;
} }
static void mouse_dispatch(MwLL self, MwMouse p, MwU32 state) { /* deepest, topmost-z sublevel under the pointer, or self if none */
switch(state) { static MwLL find_target(MwLL self, MwPoint* local) {
case WL_POINTER_BUTTON_STATE_PRESSED: MwLL* children = MwLLWaylandSublevelsByZIndex(self);
MwLLDispatch(self, down, &p); MwLL target = self;
break; int i;
case WL_POINTER_BUTTON_STATE_RELEASED: for(i = arrlen(children) - 1; i >= 0; i--) {
MwLLDispatch(self, up, &p); MwLL tp;
MwPoint pt, rel, abs;
if(hit_detect(children[i], &tp, &pt, &rel, &abs)) {
*local = rel;
target = find_target(children[i], local);
break; break;
} }
}
arrfree(children);
return target;
}
if(self->common.internal) { static void mouse_dispatch(MwLL self, MwMouse p, MwU32 state) {
MwWidget w = self->common.internal; MwLL target = find_target(self, &p.point);
switch(state) {
case WL_POINTER_BUTTON_STATE_PRESSED:
MwLLDispatch(target, down, &p);
break;
case WL_POINTER_BUTTON_STATE_RELEASED:
MwLLDispatch(target, up, &p);
return;
}
{
/* walk from the top of the stack down so that only the highest z-index sublevel under the pointer gets the click */
MwLL* children = MwLLWaylandSublevelsByZIndex(self);
int i, n; int i, n;
for(i = 0; i < arrlen(w->children); i++) { for(i = arrlen(children) - 1; i >= 0; i--) {
if(w->children[i]->lowlevel->wayland.type == MwLL_WAYLAND_SUBLEVEL) { MwLL child = children[i];
MwLL child = w->children[i]->lowlevel;
MwLL topmost_parent = child; MwLL topmost_parent = child;
MwPoint point; MwPoint point;
MwPoint relative_mouse_pos; MwPoint relative_mouse_pos;
@ -827,9 +818,10 @@ static void mouse_dispatch(MwLL self, MwMouse p, MwU32 state) {
} }
mouse_dispatch(child, p, state); mouse_dispatch(child, p, state);
break;
} }
} }
} arrfree(children);
} }
} }

View file

@ -13,17 +13,48 @@ MwBool MwWaylandVulkan = MwFALSE;
MwBool MwWaylandCairoOnly = MwFALSE; MwBool MwWaylandCairoOnly = MwFALSE;
void MwLLWaylandChildrenIterate(MwLL handle, void (*func)(MwLL handle, MwLL child)) { MwLL* MwLLWaylandSublevelsByZIndex(MwLL handle) {
MwLL* r = NULL;
if(handle->common.internal) { if(handle->common.internal) {
MwWidget w = handle->common.internal; MwWidget w = handle->common.internal;
int i; int i, j;
for(i = 0; i < arrlen(w->children); 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; MwLL child = w->children[i]->lowlevel;
func(handle, child); if(child->wayland.type != MwLL_WAYLAND_SUBLEVEL) continue;
/* insertion sort, stable so that equal z-indexes keep their creation order */
arrput(r, child);
for(j = arrlen(r) - 1; j > 0 && r[j - 1]->wayland.z_index > child->wayland.z_index; j--) {
r[j] = r[j - 1];
}
r[j] = child;
} }
} }
return r;
}
void MwLLWaylandSetZIndex(MwLL handle, MwI32 z_index) {
MwLL root = handle;
if(handle->wayland.z_index == z_index) return;
handle->wayland.z_index = z_index;
/* sublevels are composited by their root, so that's what needs to redraw */
while(root->wayland.type == MwLL_WAYLAND_SUBLEVEL && root->wayland.parent) {
root = root->wayland.parent;
} }
root->wayland.do_cascading_draw = MwTRUE;
root->wayland.events_pending = 1;
}
/* Iterate over the sublevel children of a widget, from the bottom of the stack to the top */
void MwLLWaylandChildrenIterate(MwLL handle, void (*func)(MwLL handle, MwLL child)) {
MwLL* children = MwLLWaylandSublevelsByZIndex(handle);
int i;
for(i = 0; i < arrlen(children); i++) {
func(handle, children[i]);
}
arrfree(children);
} }
static int event_loop(MwLL handle); static int event_loop(MwLL handle);
@ -813,35 +844,21 @@ static void clip(MwLL handle) {
cy = 0; cy = 0;
mx = toplevel->wayland.ww; mx = toplevel->wayland.ww;
my = toplevel->wayland.wh; my = toplevel->wayland.wh;
// ws is traversed result /* ws is traversed result */
// ws[ws.length - 1] is ignored bc it's toplevel /* ws[arrlen(ws) - 1] is the toplevel, which is the origin */
for(i = arrlen(ws) - 2; i >= 0; i--) { for(i = arrlen(ws) - 2; i >= 0; i--) {
int j;
int l = 0;
x += ws[i]->wayland.x; x += ws[i]->wayland.x;
y += ws[i]->wayland.y; y += ws[i]->wayland.y;
for(j = i - 1; j >= 0; j--) { cx = MAX(cx, x);
if(ws[j]->wayland.x > cx) l = 1; cy = MAX(cy, y);
if(ws[j]->wayland.y > cy) l = 1; mx = MIN(mx, x + (int)ws[i]->wayland.ww);
if(l) break; my = MIN(my, y + (int)ws[i]->wayland.wh);
} }
if(!l) {
cx = MAX(cx, ws[i]->wayland.x);
cy = MAX(cy, ws[i]->wayland.y);
}
mx = MIN(mx, x + ws[i]->wayland.ww);
my = MIN(my, y + ws[i]->wayland.wh);
}
if(mx < cx) mx = cx;
if(my < cy) my = cy;
arrfree(ws); arrfree(ws);
/* fully hidden, clip to an empty rectangle */
if(mx < cx) mx = cx; if(mx < cx) mx = cx;
if(my < cy) my = cy; if(my < cy) my = cy;
@ -1937,21 +1954,20 @@ static MwBool MwLLDoModernImpl(MwLL handle) {
} }
static void MwLLRaiseImpl(MwLL handle) { static void MwLLRaiseImpl(MwLL handle) {
(void)handle; MwLL* siblings;
/* int n;
if(handle->wayland.type == MwLL_WAYLAND_SUBLEVEL) { if(handle->wayland.type != MwLL_WAYLAND_SUBLEVEL || !handle->wayland.parent) {
MwLL topmost_parent = handle; return;
int children_num;
while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent;
children_num = arrlen(((MwWidget)topmost_parent->common.internal)->children);
if(children_num > 1) {
printf("%d\n", children_num);
MwWidget last_child = ((MwWidget)topmost_parent->common.internal)->children[children_num - 1];
wl_subsurface_place_above(handle->wayland.sublevel->subsurface, last_child->lowlevel->wayland.framebuffer.surface);
} }
}*/
/* put it one above whichever sibling is currently on top */
siblings = MwLLWaylandSublevelsByZIndex(handle->wayland.parent);
n = arrlen(siblings);
if(n > 0 && siblings[n - 1] != handle) {
MwLLWaylandSetZIndex(handle, siblings[n - 1]->wayland.z_index + 1);
}
arrfree(siblings);
} }
static void MwLLClipImpl(MwLL handle, MwRect* rect) { static void MwLLClipImpl(MwLL handle, MwRect* rect) {