Make get_output_next() work with non-aligned RandR setups (+test) (Thanks Feh, swh, Moritz)

A good visualization of the new algorithm is this:

           +--------+
           |        |
+--------+=|   S1   |========================
|        | |        |
|   S0   | +--------+
|        |         +--------+
+--------+=========|        |================
                   |   S2   | +--------+
                   |        | |        |
                   +--------+ |   S3   |
                              |        |
                              +--------+

When focus is on S0, 'focus output right' will first match S1 (the
closest output which overlaps in the highlighted area), then S2, but not
S3 (since S3 does not overlap into the highlighted area).

fixes #669
fixes #771
This commit is contained in:
Michael Stapelberg
2012-09-22 16:49:37 +02:00
parent 89ca48be20
commit 19883108a9
5 changed files with 242 additions and 76 deletions

View File

@ -101,89 +101,76 @@ Output *get_output_containing(int x, int y) {
*
*/
Output *get_output_most(direction_t direction, Output *current) {
Output *output, *candidate = NULL;
int position = 0;
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
/* Repeated calls of WIN determine the winner of the comparison */
#define WIN(variable, condition) \
if (variable condition) { \
candidate = output; \
position = variable; \
} \
break;
if (((direction == D_UP) || (direction == D_DOWN)) &&
(current->rect.x != output->rect.x))
continue;
if (((direction == D_LEFT) || (direction == D_RIGHT)) &&
(current->rect.y != output->rect.y))
continue;
switch (direction) {
case D_UP:
WIN(output->rect.y, <= position);
case D_DOWN:
WIN(output->rect.y, >= position);
case D_LEFT:
WIN(output->rect.x, <= position);
case D_RIGHT:
WIN(output->rect.x, >= position);
}
}
assert(candidate != NULL);
return candidate;
Output *best = get_output_next(direction, current, FARTHEST_OUTPUT);
if (!best)
best = current;
DLOG("current = %s, best = %s\n", current->name, best->name);
return best;
}
/*
* Gets the output which is the next one in the given direction.
*
*/
Output *get_output_next(direction_t direction, Output *current) {
Output *output, *candidate = NULL;
Output *get_output_next(direction_t direction, Output *current, output_close_far_t close_far) {
Rect *cur = &(current->rect),
*other;
Output *output,
*best = NULL;
TAILQ_FOREACH(output, &outputs, outputs) {
if (!output->active)
continue;
if (((direction == D_UP) || (direction == D_DOWN)) &&
(current->rect.x != output->rect.x))
other = &(output->rect);
if ((direction == D_RIGHT && other->x > cur->x) ||
(direction == D_LEFT && other->x < cur->x)) {
/* Skip the output when it doesnt overlap the other ones y
* coordinate at all. */
if ((other->y + other->height) < cur->y &&
(cur->y + cur->height) < other->y)
continue;
} else if ((direction == D_DOWN && other->y > cur->y) ||
(direction == D_UP && other->y < cur->y)) {
/* Skip the output when it doesnt overlap the other ones x
* coordinate at all. */
if ((other->x + other->width) < cur->x &&
(cur->x + cur->width) < other->x)
continue;
} else
continue;
if (((direction == D_LEFT) || (direction == D_RIGHT)) &&
(current->rect.y != output->rect.y))
/* No candidate yet? Start with this one. */
if (!best) {
best = output;
continue;
}
switch (direction) {
case D_UP:
if (output->rect.y < current->rect.y &&
(!candidate || output->rect.y < candidate->rect.y))
candidate = output;
break;
case D_DOWN:
if (output->rect.y > current->rect.y &&
(!candidate || output->rect.y > candidate->rect.y))
candidate = output;
break;
case D_LEFT:
if (output->rect.x < current->rect.x &&
(!candidate || output->rect.x > candidate->rect.x))
candidate = output;
break;
case D_RIGHT:
if (output->rect.x > current->rect.x &&
(!candidate || output->rect.x < candidate->rect.x))
candidate = output;
break;
if (close_far == CLOSEST_OUTPUT) {
/* Is this output better (closer to the current output) than our
* current best bet? */
if ((direction == D_RIGHT && other->x < best->rect.x) ||
(direction == D_LEFT && other->x > best->rect.x) ||
(direction == D_DOWN && other->y < best->rect.y) ||
(direction == D_UP && other->y > best->rect.y)) {
best = output;
continue;
}
} else {
/* Is this output better (farther to the current output) than our
* current best bet? */
if ((direction == D_RIGHT && other->x > best->rect.x) ||
(direction == D_LEFT && other->x < best->rect.x) ||
(direction == D_DOWN && other->y > best->rect.y) ||
(direction == D_UP && other->y < best->rect.y)) {
best = output;
continue;
}
}
}
return candidate;
DLOG("current = %s, best = %s\n", current->name, (best ? best->name : "NULL"));
return best;
}
/*