Danny Baumann
2008-Feb-08 07:24 UTC
[PATCH] Make outputDeviceForGeometry behave smarter when dealing with overlapping outputs. Currently, the current output is returned if some part of the rectangle is on it; otherwise the output device the rectangle center is on is returned. This works fine for non-overlapping outputs, but with overlapping outputs, the window center may be on multiple outputs, making the determination ambiguous. This patch applies the following strategy instead: - Determine the output the largest part of the rectangle area is on. - If there are multiple outputs with equally large parts, return the smallest output. - Otherwise, return the output that contains the largest part.
This makes scenarios like laptops with external monitors on Xrandr 1.2 work. Those e.g. have a 1280x800 output and a 1280x1024 output, completely overlapping each other. With this patch, outputDeviceForGeometry will return the smaller head for a rectangle that's completely inside the smaller head and the larger head otherwise. --- src/screen.c | 88 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/screen.c b/src/screen.c index ca308df..701a57f 100644 --- a/src/screen.c +++ b/src/screen.c @@ -36,6 +36,7 @@ #include <sys/types.h> #include <unistd.h> #include <assert.h> +#include <limits.h> #include <X11/Xlib.h> #include <X11/Xatom.h> @@ -3953,6 +3954,27 @@ viewportForGeometry (CompScreen *s, } } +static int +rectangleOverlapArea (BOX *rect1, + BOX *rect2) +{ + int left, right, top, bottom; + + /* extents of overlapping rectangle */ + left = MAX (rect1->x1, rect2->x1); + right = MIN (rect1->x2, rect2->x2); + top = MAX (rect1->y1, rect2->y1); + bottom = MIN (rect1->y2, rect2->y2); + + if (left > right || top > bottom) + { + /* no overlap */ + return 0; + } + + return (right - left) * (bottom - top); +} + int outputDeviceForGeometry (CompScreen *s, int x, @@ -3961,26 +3983,64 @@ outputDeviceForGeometry (CompScreen *s, int height, int borderWidth) { - int output = s->currentOutputDev; - int x1, y1, x2, y2; + int *overlapAreas; + int i, highest, seen, highestScore; + BOX geomRect; + + if (s->nOutputDev == 1) + return 0; - width += borderWidth * 2; - height += borderWidth * 2; + overlapAreas = malloc (s->nOutputDev * sizeof (int)); + if (!overlapAreas) + return 0; - x1 = s->outputDev[output].region.extents.x1; - y1 = s->outputDev[output].region.extents.y1; - x2 = s->outputDev[output].region.extents.x2; - y2 = s->outputDev[output].region.extents.y2; + geomRect.x1 = x; + geomRect.y1 = y; + geomRect.x2 = x + width + 2 * borderWidth; + geomRect.y2 = y + height + 2 * borderWidth; - if (x1 >= x + width || - y1 >= y + height || - x2 <= x || - y2 <= y) + /* get amount of overlap on all output devices */ + for (i = 0; i < s->nOutputDev; i++) + overlapAreas[i] = rectangleOverlapArea (&s->outputDev[i].region.extents, + &geomRect); + + /* find head with largest overlap */ + for (i = 0, highest = 0, highestScore = 0; i < s->nOutputDev; i++) + if (overlapAreas[i] > highestScore) + { + highest = i; + highestScore = overlapAreas[i]; + } + + /* look if the highest score is unique */ + for (i = 0, seen = 0; i < s->nOutputDev; i++) + if (overlapAreas[i] == highestScore) + seen++; + + if (seen > 1) { - output = outputDeviceForPoint (s, x + width / 2, y + height / 2); + /* it's not unique, so find the head which is more overlapped by + the window, which in turns means finding the smallest of the + given heads */ + unsigned int currentSize, smallestHead = UINT_MAX; + + for (i = 0, highest = 0; i < s->nOutputDev; i++) + if (overlapAreas[i] == highestScore) + { + BOX *box = &s->outputDev[i].region.extents; + + currentSize = (box->x2 - box->x1) * (box->y2 - box->y1); + if (currentSize < smallestHead) + { + highest = i; + smallestHead = currentSize; + } + } } + + free (overlapAreas); - return output; + return highest; } Bool -- 1.5.3.8 --=-RQaak5NVMWraah6aFMh5--