foxygit / dotfiles Log in
commits tags

/multi-monitor-struts.patch · 5.57 KB

raw
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);
 			}
 		}
 	}