all repos — openbox @ d68116c9c9b2c9a9925d872141eff8e301ef3bb3

openbox fork - make it a bit more like ryudo

Pick the monitor most relevant to a rectangle more cleverly.

When monitors overlap (this happens with cloning), we were choosing a monitor
to associate with a window, for maximization for example, somewhat arbitrarily.

Now we have a more clever algorithm that considers the configured primary
monitor first, and that does not prefer monitors based on their sizes, but only
how much of the window is in the monitor, excluding parts that were claimed
by another monitor already.
Dana Jansens danakj@orodu.net
commit

d68116c9c9b2c9a9925d872141eff8e301ef3bb3

parent

dc69fe7cedd679bf38c1be25130ed502af8ad4c4

2 files changed, 129 insertions(+), 11 deletions(-)

jump to
M openbox/geom.hopenbox/geom.h

@@ -65,6 +65,8 @@ #define RECT_TOP(r) ((r).y)

#define RECT_RIGHT(r) ((r).x + (r).width - 1) #define RECT_BOTTOM(r) ((r).y + (r).height - 1) +#define RECT_AREA(r) ((r).width * (r).height) + #define RECT_SET_POINT(r, nx, ny) \ (r).x = (nx), (r).y = (ny) #define RECT_SET_SIZE(r, w, h) \
M openbox/screen.copenbox/screen.c

@@ -1369,6 +1369,14 @@ r = MAX(r, (*xin_areas)[i].x + (*xin_areas)[i].width - 1);

b = MAX(b, (*xin_areas)[i].y + (*xin_areas)[i].height - 1); } RECT_SET((*xin_areas)[*nxin], l, t, r - l + 1, b - t + 1); + + for (i = 0; i < *nxin; ++i) + ob_debug("Monitor %d @ %d,%d %dx%d\n", i, + (*xin_areas)[i].x, (*xin_areas)[i].y, + (*xin_areas)[i].width, (*xin_areas)[i].height); + ob_debug("Full desktop @ %d,%d %dx%d\n", + (*xin_areas)[i].x, (*xin_areas)[i].y, + (*xin_areas)[i].width, (*xin_areas)[i].height); } void screen_update_areas(void)

@@ -1628,27 +1636,135 @@ a->height = b - t + 1;

return a; } +typedef struct { + Rect r; + gboolean subtract; +} RectArithmetic; + guint screen_find_monitor(const Rect *search) { guint i; guint most = screen_num_monitors; - guint mostv = 0; + guint mostpx = 0; + GSList *counted = NULL; + + /* we want to count the number of pixels search has on each monitor, but not + double count. so if a pixel is counted on monitor A then we should not + count it again on monitor B. in the end we want to return the monitor + that had the most pixels counted under this scheme. + + this assumes that monitors earlier in the list are more desirable to be + considered the search area's monitor. we try the configured primary + monitor first, so it gets the highest preference. + + if we have counted an area A, then we want to subtract the intersection + of A with the area on future monitors. + but now consider if we count an area B that intersects A. we want to + subtract the area B from that counted on future monitors, but not + subtract the intersection of A and B twice! so we would add the + intersection of A and B back, to account for it being subtracted both + for A and B. + + this is the idea behind the algorithm. we always subtract the full area + for monitor M intersected with the search area. we'll call that AREA. + but then we go through the list |counted| and for each rectangle in + the list that is being subtracted from future monitors, we insert a + request to add back the intersection of the subtracted rect with AREA. + vice versa for a rect in |counted| that is getting added back. + */ + + if (config_primary_monitor_index < screen_num_monitors) { + const Rect *monitor; + Rect on_current_monitor; + glong area; - for (i = 0; i < screen_num_monitors; ++i) { - const Rect *area = screen_physical_area_monitor(i); - if (RECT_INTERSECTS_RECT(*area, *search)) { - Rect r; - guint v; + monitor = screen_physical_area_monitor(config_primary_monitor_index); - RECT_SET_INTERSECTION(r, *area, *search); - v = r.width * r.height; + if (RECT_INTERSECTS_RECT(*monitor, *search)) { + RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search); + area = RECT_AREA(on_current_monitor); - if (v > mostv) { - mostv = v; - most = i; + if (area > mostpx) { + mostpx = area; + most = config_primary_monitor_index; + } + + /* add the intersection rect on the current monitor to the + counted list. that's easy for the first one, we just mark it for + subtraction */ + { + RectArithmetic *ra = g_slice_new(RectArithmetic); + ra->r = on_current_monitor; + ra->subtract = TRUE; + counted = g_slist_prepend(counted, ra); } } } + + for (i = 0; i < screen_num_monitors; ++i) { + const Rect *monitor; + Rect on_current_monitor; + glong area; + GSList *it; + + monitor = screen_physical_area_monitor(i); + + if (i == config_primary_monitor_index) + continue; /* already did this one */ + if (!RECT_INTERSECTS_RECT(*monitor, *search)) + continue; /* nothing to see here */ + + RECT_SET_INTERSECTION(on_current_monitor, *monitor, *search); + area = RECT_AREA(on_current_monitor); + + /* remove pixels we already counted on any previous monitors. */ + for (it = counted; it; it = g_slist_next(it)) { + RectArithmetic *ra = it->data; + Rect intersection; + + RECT_SET_INTERSECTION(intersection, ra->r, *search); + if (ra->subtract) area -= RECT_AREA(intersection); + else area += RECT_AREA(intersection); + } + + if (area > mostpx) { + mostpx = area; + most = i; + } + + /* add the intersection rect on the current monitor I to the counted + list. + but now we need to compensate for every rectangle R already in the + counted list, and add a new rect R' that is the intersection of + R and I, but with the reverse subtraction/addition operation. + */ + for (it = counted; it; it = g_slist_next(it)) { + RectArithmetic *saved = it->data; + + if (!RECT_INTERSECTS_RECT(saved->r, on_current_monitor)) + continue; + /* we are going to subtract our rect from future monitors, but + part of it may already be being subtracted/added, so compensate + to not double add/subtract. */ + RectArithmetic *reverse = g_slice_new(RectArithmetic); + RECT_SET_INTERSECTION(reverse->r, saved->r, on_current_monitor); + reverse->subtract = !saved->subtract; + /* prepend so we can continue thru the list uninterupted */ + counted = g_slist_prepend(counted, reverse); + } + { + RectArithmetic *ra = g_slice_new(RectArithmetic); + ra->r = on_current_monitor; + ra->subtract = TRUE; + counted = g_slist_prepend(counted, ra); + } + } + + while (counted) { + g_slice_free(RectArithmetic, counted->data); + counted = g_slist_delete_link(counted, counted); + } + return most < screen_num_monitors ? most : screen_monitor_primary(FALSE); }