diff --git a/src/sxwm.c b/src/sxwm.c
index 55fe97b..eab858a 100644
--- a/src/sxwm.c
+++ b/src/sxwm.c
@@ -3160,14 +3276,6 @@ void update_struts(void)
long right = str[1];
long top = str[2];
long bottom = str[3];
- long left_start_y = str[4];
- long left_end_y = str[5];
- long right_start_y = str[6];
- long right_end_y = str[7];
- long top_start_x = str[8];
- long top_end_x = str[9];
- long bot_start_x = str[10];
- long bot_end_x = str[11];
XFree(str);
@@ -3175,79 +3283,76 @@ void update_struts(void)
if (!left && !right && !top && !bottom)
continue;
- for (int m = 0; m < n_mons; m++) {
- int mx = mons[m].x;
- int my = mons[m].y;
- int mw = mons[m].w;
- int mh = mons[m].h;
-
- /* strip monitors whose vertical span dostn intersect */
- if (left > 0) {
- long span_start = left_start_y;
- long span_end = left_end_y;
- if (span_end >= my && span_start <= my + mh - 1) {
- /*
- left is distance from root left edge to reserved area
- to map to mon, the portion is:
- reserve_left = MAX(0, left - mx)
- */
- int reserve = (int)MAX(0, left - mx);
- if (reserve > 0)
- mons[m].reserve_left = MAX(mons[m].reserve_left, reserve);
+ /*
+ _NET_WM_STRUT(_PARTIAL) values are all measured from the
+ absolute edges of the root window, not from the panel's own
+ monitor. That's fine for single-monitor setups, but falls
+ apart once monitors differ in size/offset: a panel sitting on
+ a monitor that isn't flush with the desktop's top-left corner
+ still reports e.g. "top" as its root-relative position, which
+ can dwarf (or even exceed) the height of a smaller monitor
+ that happens to sit at the actual top of the desktop. Anchor
+ the reservation to the panel's own monitor -- found via the
+ panel window's own geometry -- instead of trusting the
+ x/y-span fields to disambiguate that for us.
+ */
+ XWindowAttributes dock_wa;
+ int dock_mon = -1;
+ if (XGetWindowAttributes(dpy, w, &dock_wa)) {
+ int dcx = dock_wa.x + dock_wa.width / 2;
+ int dcy = dock_wa.y + dock_wa.height / 2;
+ for (int m = 0; m < n_mons; m++) {
+ if (dcx >= mons[m].x && dcx < mons[m].x + mons[m].w &&
+ dcy >= mons[m].y && dcy < mons[m].y + mons[m].h) {
+ dock_mon = m;
+ break;
}
}
+ }
- if (right > 0) {
- long span_start = right_start_y;
- long span_end = right_end_y;
- if (span_end >= my && span_start <= my + mh - 1) {
- /*
- right is distance from root right edge to reserved area:
- right edge = screen_w
- mons right edge = mx + mw
- amount that cuts into monitor = MAX(0, (screen_w - right) - mx)
- */
- int global_reserved_left = screen_w - (int)right;
- int overlap = (mx + mw) - global_reserved_left;
- int reserve = MAX(0, overlap);
- if (reserve > 0)
- mons[m].reserve_right = MAX(mons[m].reserve_right, reserve);
- }
- }
+ /*
+ a dock whose centre falls outside every current monitor is
+ orphaned -- almost always because the monitor it was drawn
+ for got hotplugged away (e.g. a per-monitor bar drawn for
+ eDP, left in place after the lid closes and eDP is turned
+ off). Falling back to monitor 0 here would attribute a
+ stale, unrelated reservation to whatever monitor survived,
+ which can eat most of its height. Drop the strut instead.
+ */
+ if (dock_mon < 0)
+ continue;
- if (top > 0) {
- long span_start = top_start_x;
- long span_end = top_end_x;
- if (span_end >= mx && span_start <= mx + mw - 1) {
- /*
- top is distance from root top to reserved area
- mons top is at my, amount eaten:
- reserve_top = MAX(0, top - my)
- */
- int reserve = (int)MAX(0, top - my);
- if (reserve > 0)
- mons[m].reserve_top = MAX(mons[m].reserve_top, reserve);
- }
- }
+ int mx = mons[dock_mon].x;
+ int my = mons[dock_mon].y;
+ int mw = mons[dock_mon].w;
+ int mh = mons[dock_mon].h;
- if (bottom > 0) {
- long span_start = bot_start_x;
- long span_end = bot_end_x;
- if (span_end >= mx && span_start <= mx + mw - 1) {
- /*
- bottom is distance from root bottom to reserved area
- global_reserved_top = screen_h - bottom;
- overlap to mon:
- overlap = (my + mh) - global_reserved_top;
- reserve_bottom = MAX(0, overlap)
- */
- int global_reserved_top = screen_h - (int)bottom;
- int overlap = (my + mh) - global_reserved_top;
- int reserve = MAX(0, overlap);
- if (reserve > 0)
- mons[m].reserve_bottom = MAX(mons[m].reserve_bottom, reserve);
- }
- }
+ if (left > 0) {
+ int reserve = (int)MIN(mw, MAX(0, left - mx));
+ if (reserve > 0)
+ mons[dock_mon].reserve_left = MAX(mons[dock_mon].reserve_left, reserve);
+ }
+
+ if (right > 0) {
+ int global_reserved_left = screen_w - (int)right;
+ int overlap = (mx + mw) - global_reserved_left;
+ int reserve = MIN(mw, MAX(0, overlap));
+ if (reserve > 0)
+ mons[dock_mon].reserve_right = MAX(mons[dock_mon].reserve_right, reserve);
+ }
+
+ if (top > 0) {
+ int reserve = (int)MIN(mh, MAX(0, top - my));
+ if (reserve > 0)
+ mons[dock_mon].reserve_top = MAX(mons[dock_mon].reserve_top, reserve);
+ }
+
+ if (bottom > 0) {
+ int global_reserved_top = screen_h - (int)bottom;
+ int overlap = (my + mh) - global_reserved_top;
+ int reserve = MIN(mh, MAX(0, overlap));
+ if (reserve > 0)
+ mons[dock_mon].reserve_bottom = MAX(mons[dock_mon].reserve_bottom, reserve);
}
}
}