]> git.sur5r.net Git - i3/i3/blobdiff - src/randr.c
Merge branch 'master' into next
[i3/i3] / src / randr.c
index 8b6ba1d9ac52a13a1e87423330fc50de038a0710..1aef9c9c620c59af800c4a1e972f6dd6cbb0aecb 100644 (file)
@@ -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)