X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Frandr.c;h=1aef9c9c620c59af800c4a1e972f6dd6cbb0aecb;hb=bb22e232ad2e2ae6289e907ece0afe5c66faa23c;hp=8b6ba1d9ac52a13a1e87423330fc50de038a0710;hpb=5257a1268f3b0f0f6ca06f9638b362f311ce3fcb;p=i3%2Fi3 diff --git a/src/randr.c b/src/randr.c index 8b6ba1d9..1aef9c9c 100644 --- a/src/randr.c +++ b/src/randr.c @@ -69,7 +69,7 @@ Output *get_first_output(void) { if (output->active) return output; - return NULL; + die("No usable outputs available.\n"); } /* @@ -93,97 +93,131 @@ Output *get_output_containing(int x, int y) { } /* - * Gets the output which is the last one in the given direction, for example - * the output on the most bottom when direction == D_DOWN, the output most - * right when direction == D_RIGHT and so on. - * - * This function always returns a output. + * In contained_by_output, we check if any active output contains part of the container. + * We do this by checking if the output rect is intersected by the Rect. + * This is the 2-dimensional counterpart of get_output_containing. + * Since we don't actually need the outputs intersected by the given Rect (There could + * be many), we just return true or false for convenience. * */ -Output *get_output_most(direction_t direction, Output *current) { - Output *output, *candidate = NULL; - int position = 0; +bool contained_by_output(Rect rect){ + Output *output; + int lx = rect.x, uy = rect.y; + int rx = rect.x + rect.width, by = rect.y + rect.height; 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); - } + DLOG("comparing x=%d y=%d with x=%d and y=%d width %d height %d\n", + rect.x, rect.y, output->rect.x, output->rect.y, output->rect.width, output->rect.height); + if (rx >= (int)output->rect.x && lx <= (int)(output->rect.x + output->rect.width) && + by >= (int)output->rect.y && uy <= (int)(output->rect.y + output->rect.height)) + return true; } + return false; - assert(candidate != NULL); +} - return candidate; +/* + * Like get_output_next with close_far == CLOSEST_OUTPUT, but wraps. + * + * For example if get_output_next(D_DOWN, x, FARTHEST_OUTPUT) = NULL, then + * get_output_next_wrap(D_DOWN, x) will return the topmost output. + * + * This function always returns a output: if no active outputs can be found, + * current itself is returned. + * + */ +Output *get_output_next_wrap(direction_t direction, Output *current) { + Output *best = get_output_next(direction, current, CLOSEST_OUTPUT); + /* If no output can be found, wrap */ + if (!best) { + direction_t opposite; + if (direction == D_RIGHT) + opposite = D_LEFT; + else if (direction == D_LEFT) + opposite = D_RIGHT; + else if (direction == D_DOWN) + opposite = D_UP; + else + opposite = D_DOWN; + best = get_output_next(opposite, 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. * + * If close_far == CLOSEST_OUTPUT, then the output next to the current one will + * selected. If close_far == FARTHEST_OUTPUT, the output which is the last one + * in the given direction will be selected. + * + * NULL will be returned when no active outputs are present in the direction + * specified (note that “current” counts as such an output). + * */ -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 doesn’t overlap the other one’s 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 doesn’t overlap the other one’s 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; } /* @@ -660,8 +694,7 @@ void randr_query_outputs(void) { output->active = false; DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); - if ((first = get_first_output()) == NULL) - die("No usable outputs available\n"); + first = get_first_output(); /* TODO: refactor the following code into a nice function. maybe * use an on_destroy callback which is implement differently for @@ -749,6 +782,9 @@ void randr_query_outputs(void) { disable_randr(conn); } + /* Verifies that there is at least one active output as a side-effect. */ + get_first_output(); + /* Just go through each active output and assign one workspace */ TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active)