diff --git a/include/Mw/LowLevel/Wayland.h b/include/Mw/LowLevel/Wayland.h index 607ff2ca..01f772c3 100644 --- a/include/Mw/LowLevel/Wayland.h +++ b/include/Mw/LowLevel/Wayland.h @@ -341,6 +341,9 @@ struct _MwLLWayland { 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; MwRect clip; @@ -505,6 +508,11 @@ void MwLLWaylandClipboardRead(wl_clipboard_device_context_t* ctx, int clipboard_ /* Flush Wayland events */ 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. */ void MwLLRecursiveKeyDispatch(MwLL self, int* k, MwBool down); diff --git a/src/backend/wayland/interfaces.c b/src/backend/wayland/interfaces.c index cd6a1b4b..2740c6f3 100644 --- a/src/backend/wayland/interfaces.c +++ b/src/backend/wayland/interfaces.c @@ -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) { MwLL topmost_parent = child; MwPoint point; @@ -761,10 +734,8 @@ static MwBool hit_detect(MwLL child, MwLL* _topmost_parent, MwPoint* _point, MwP if(topmost_parent) { /* 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.x > 0) - absolute_pos.x += topmost_parent->wayland.x; - if(topmost_parent->wayland.y > 0) - absolute_pos.y += topmost_parent->wayland.y; + absolute_pos.x += topmost_parent->wayland.x; + absolute_pos.y += topmost_parent->wayland.y; } } } @@ -783,53 +754,74 @@ 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; } +/* deepest, topmost-z sublevel under the pointer, or self if none */ +static MwLL find_target(MwLL self, MwPoint* local) { + MwLL* children = MwLLWaylandSublevelsByZIndex(self); + MwLL target = self; + int i; + for(i = arrlen(children) - 1; i >= 0; i--) { + MwLL tp; + MwPoint pt, rel, abs; + if(hit_detect(children[i], &tp, &pt, &rel, &abs)) { + *local = rel; + target = find_target(children[i], local); + break; + } + } + arrfree(children); + return target; +} + static void mouse_dispatch(MwLL self, MwMouse p, MwU32 state) { + MwLL target = find_target(self, &p.point); + switch(state) { case WL_POINTER_BUTTON_STATE_PRESSED: - MwLLDispatch(self, down, &p); + MwLLDispatch(target, down, &p); break; case WL_POINTER_BUTTON_STATE_RELEASED: - MwLLDispatch(self, up, &p); - break; + MwLLDispatch(target, up, &p); + return; } - if(self->common.internal) { - MwWidget w = self->common.internal; - int i, n; - 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 topmost_parent = child; - MwPoint point; - MwPoint relative_mouse_pos; - MwPoint absolute_pos; + { + /* 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; + for(i = arrlen(children) - 1; i >= 0; i--) { + MwLL child = children[i]; + MwLL topmost_parent = child; + MwPoint point; + MwPoint relative_mouse_pos; + MwPoint absolute_pos; - if(hit_detect(child, &topmost_parent, &point, &relative_mouse_pos, &absolute_pos)) { - int idx = -1; - for(n = 0; n < arrlen(topmost_parent->wayland.currentlyHeldWidgets); n++) { - if(topmost_parent->wayland.currentlyHeldWidgets[n] == child) { - idx = n; - } + if(hit_detect(child, &topmost_parent, &point, &relative_mouse_pos, &absolute_pos)) { + int idx = -1; + for(n = 0; n < arrlen(topmost_parent->wayland.currentlyHeldWidgets); n++) { + if(topmost_parent->wayland.currentlyHeldWidgets[n] == child) { + idx = n; } - p.point = relative_mouse_pos; - - switch(state) { - case WL_POINTER_BUTTON_STATE_PRESSED: - if(idx == -1) { - arrpush(topmost_parent->wayland.currentlyHeldWidgets, child); - } - break; - case WL_POINTER_BUTTON_STATE_RELEASED: - if(idx != -1) { - arrdel(topmost_parent->wayland.currentlyHeldWidgets, idx); - } - break; - } - - mouse_dispatch(child, p, state); } + p.point = relative_mouse_pos; + + switch(state) { + case WL_POINTER_BUTTON_STATE_PRESSED: + if(idx == -1) { + arrpush(topmost_parent->wayland.currentlyHeldWidgets, child); + } + break; + case WL_POINTER_BUTTON_STATE_RELEASED: + if(idx != -1) { + arrdel(topmost_parent->wayland.currentlyHeldWidgets, idx); + } + break; + } + + mouse_dispatch(child, p, state); + break; } } + arrfree(children); } } diff --git a/src/backend/wayland/wayland.c b/src/backend/wayland/wayland.c index e2e96378..d23594bf 100644 --- a/src/backend/wayland/wayland.c +++ b/src/backend/wayland/wayland.c @@ -13,17 +13,48 @@ MwBool MwWaylandVulkan = 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) { MwWidget w = handle->common.internal; - int i; + int i, j; for(i = 0; i < arrlen(w->children); i++) { - if(w->children[i]->lowlevel->wayland.type == MwLL_WAYLAND_SUBLEVEL) { - MwLL child = w->children[i]->lowlevel; - func(handle, child); + MwLL child = w->children[i]->lowlevel; + 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); @@ -813,35 +844,21 @@ static void clip(MwLL handle) { cy = 0; mx = toplevel->wayland.ww; my = toplevel->wayland.wh; - // ws is traversed result - // ws[ws.length - 1] is ignored bc it's toplevel + /* ws is traversed result */ + /* ws[arrlen(ws) - 1] is the toplevel, which is the origin */ for(i = arrlen(ws) - 2; i >= 0; i--) { - int j; - int l = 0; - x += ws[i]->wayland.x; y += ws[i]->wayland.y; - for(j = i - 1; j >= 0; j--) { - if(ws[j]->wayland.x > cx) l = 1; - if(ws[j]->wayland.y > cy) l = 1; - if(l) break; - } - - 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); + cx = MAX(cx, x); + cy = MAX(cy, y); + mx = MIN(mx, x + (int)ws[i]->wayland.ww); + my = MIN(my, y + (int)ws[i]->wayland.wh); } - if(mx < cx) mx = cx; - if(my < cy) my = cy; - arrfree(ws); + /* fully hidden, clip to an empty rectangle */ if(mx < cx) mx = cx; if(my < cy) my = cy; @@ -1937,21 +1954,20 @@ static MwBool MwLLDoModernImpl(MwLL handle) { } static void MwLLRaiseImpl(MwLL handle) { - (void)handle; - /* + MwLL* siblings; + int n; - if(handle->wayland.type == MwLL_WAYLAND_SUBLEVEL) { - MwLL topmost_parent = handle; - int children_num; - while(topmost_parent->wayland.parent) topmost_parent = topmost_parent->wayland.parent; - children_num = arrlen(((MwWidget)topmost_parent->common.internal)->children); + if(handle->wayland.type != MwLL_WAYLAND_SUBLEVEL || !handle->wayland.parent) { + return; + } - 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) {