X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Frandr.c;h=267d6e41bf307628c058653325d94100888c7ad4;hb=625401d1628757a67a2ab4eeaa68be965683889c;hp=1c01fdd5e35891904d1761b55dbbff2b4c02b0a7;hpb=2daa8d422ae63d55e4b952070e8e894c0963618f;p=i3%2Fi3 diff --git a/src/randr.c b/src/randr.c index 1c01fdd5..267d6e41 100644 --- a/src/randr.c +++ b/src/randr.c @@ -1,3 +1,5 @@ +#undef I3__FILE__ +#define I3__FILE__ "randr.c" /* * vim:ts=4:sw=4:expandtab * @@ -60,14 +62,14 @@ Output *get_output_by_name(const char *name) { * Returns the first output which is active. * */ -Output *get_first_output() { +Output *get_first_output(void) { Output *output; TAILQ_FOREACH(output, &outputs, outputs) if (output->active) return output; - return NULL; + die("No usable outputs available.\n"); } /* @@ -99,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 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; } /* @@ -256,7 +245,6 @@ void output_init_con(Output *output) { Con *topdock = con_new(NULL, NULL); topdock->type = CT_DOCKAREA; topdock->layout = L_DOCKAREA; - topdock->orientation = VERT; /* this container swallows dock clients */ Match *match = scalloc(sizeof(Match)); match_init(match); @@ -278,6 +266,7 @@ void output_init_con(Output *output) { DLOG("adding main content container\n"); Con *content = con_new(NULL, NULL); content->type = CT_CON; + content->layout = L_SPLITH; FREE(content->name); content->name = sstrdup("content"); @@ -290,7 +279,6 @@ void output_init_con(Output *output) { Con *bottomdock = con_new(NULL, NULL); bottomdock->type = CT_DOCKAREA; bottomdock->layout = L_DOCKAREA; - bottomdock->orientation = VERT; /* this container swallows dock clients */ match = scalloc(sizeof(Match)); match_init(match); @@ -319,8 +307,6 @@ void output_init_con(Output *output) { * */ void init_ws_for_output(Output *output, Con *content) { - char *name; - /* go through all assignments and move the existing workspaces to this output */ struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { @@ -361,6 +347,19 @@ void init_ws_for_output(Output *output, Con *content) { workspace_show(previous); } + /* Render the output on which the workspace was to get correct Rects. + * Then, we need to work with the "content" container, since we cannot + * be sure that the workspace itself was rendered at all (in case it’s + * invisible, it won’t be rendered). */ + render_con(workspace_out, false); + Con *ws_out_content = output_get_content(workspace_out); + + Con *floating_con; + TAILQ_FOREACH(floating_con, &(workspace->floating_head), floating_windows) + /* NB: We use output->con here because content is not yet rendered, + * so it has a rect of {0, 0, 0, 0}. */ + floating_fix_coordinates(floating_con, &(ws_out_content->rect), &(output->con->rect)); + con_detach(workspace); con_attach(workspace, content, false); @@ -404,116 +403,7 @@ void init_ws_for_output(Output *output, Con *content) { /* if there is still no workspace, we create the first free workspace */ DLOG("Now adding a workspace\n"); - - /* add a workspace to this output */ - Con *out, *current; - bool exists = true; - Con *ws = con_new(NULL, NULL); - ws->type = CT_WORKSPACE; - - /* try the configured workspace bindings first to find a free name */ - Binding *bind; - TAILQ_FOREACH(bind, bindings, bindings) { - DLOG("binding with command %s\n", bind->command); - if (strlen(bind->command) < strlen("workspace ") || - strncasecmp(bind->command, "workspace", strlen("workspace")) != 0) - continue; - DLOG("relevant command = %s\n", bind->command); - char *target = bind->command + strlen("workspace "); - /* We check if this is the workspace - * next/prev/next_on_output/prev_on_output/back_and_forth command. - * Beware: The workspace names "next", "prev", "next_on_output", - * "prev_on_output" and "back_and_forth" are OK, so we check before - * stripping the double quotes */ - if (strncasecmp(target, "next", strlen("next")) == 0 || - strncasecmp(target, "prev", strlen("prev")) == 0 || - strncasecmp(target, "next_on_output", strlen("next_on_output")) == 0 || - strncasecmp(target, "prev_on_output", strlen("prev_on_output")) == 0 || - strncasecmp(target, "back_and_forth", strlen("back_and_forth")) == 0) - continue; - if (*target == '"') - target++; - FREE(ws->name); - ws->name = strdup(target); - if (ws->name[strlen(ws->name)-1] == '"') - ws->name[strlen(ws->name)-1] = '\0'; - DLOG("trying name *%s*\n", ws->name); - - /* Ensure that this workspace is not assigned to a different output — - * otherwise we would create it, then move it over to its output, then - * find a new workspace, etc… */ - bool assigned = false; - TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (strcmp(assignment->name, ws->name) != 0 || - strcmp(assignment->output, output->name) == 0) - continue; - - assigned = true; - break; - } - - if (assigned) - continue; - - current = NULL; - TAILQ_FOREACH(out, &(croot->nodes_head), nodes) - GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name)); - - exists = (current != NULL); - if (!exists) { - /* Set ->num to the number of the workspace, if the name actually - * is a number or starts with a number */ - char *endptr = NULL; - long parsed_num = strtol(ws->name, &endptr, 10); - if (parsed_num == LONG_MIN || - parsed_num == LONG_MAX || - parsed_num < 0 || - endptr == ws->name) - ws->num = -1; - else ws->num = parsed_num; - LOG("Used number %d for workspace with name %s\n", ws->num, ws->name); - - break; - } - } - - if (exists) { - /* get the next unused workspace number */ - DLOG("Getting next unused workspace by number\n"); - int c = 0; - while (exists) { - c++; - - FREE(ws->name); - sasprintf(&(ws->name), "%d", c); - - current = NULL; - TAILQ_FOREACH(out, &(croot->nodes_head), nodes) - GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name)); - exists = (current != NULL); - - DLOG("result for ws %s / %d: exists = %d\n", ws->name, c, exists); - } - ws->num = c; - } - con_attach(ws, content, false); - - sasprintf(&name, "[i3 con] workspace %s", ws->name); - x_set_name(ws, name); - free(name); - - ws->fullscreen_mode = CF_OUTPUT; - - /* If default_orientation is set to NO_ORIENTATION we determine - * orientation depending on output resolution. */ - if (config.default_orientation == NO_ORIENTATION) { - ws->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ; - DLOG("Auto orientation. Workspace size set to (%d,%d), setting orientation to %d.\n", - output->rect.width, output->rect.height, ws->orientation); - } else { - ws->orientation = config.default_orientation; - } - + Con *ws = create_workspace_on_output(output, content); /* TODO: Set focus in main.c */ con_focus(ws); @@ -558,11 +448,12 @@ static void output_change_mode(xcb_connection_t *conn, Output *output) { if (con_num_children(workspace) > 1) continue; - workspace->orientation = (output->rect.height > output->rect.width) ? VERT : HORIZ; - DLOG("Setting workspace [%d,%s]'s orientation to %d.\n", workspace->num, workspace->name, workspace->orientation); + workspace->layout = (output->rect.height > output->rect.width) ? L_SPLITV : L_SPLITH; + DLOG("Setting workspace [%d,%s]'s layout to %d.\n", workspace->num, workspace->name, workspace->layout); if ((child = TAILQ_FIRST(&(workspace->nodes_head)))) { - child->orientation = workspace->orientation; - DLOG("Setting child [%d,%s]'s orientation to %d.\n", child->num, child->name, child->orientation); + if (child->layout == L_SPLITV || child->layout == L_SPLITH) + child->layout = workspace->layout; + DLOG("Setting child [%d,%s]'s layout to %d.\n", child->num, child->name, child->layout); } } } @@ -650,7 +541,7 @@ static void handle_output(xcb_connection_t *conn, xcb_randr_output_t id, * (Re-)queries the outputs via RandR and stores them in the list of outputs. * */ -void randr_query_outputs() { +void randr_query_outputs(void) { Output *output, *other, *first; xcb_randr_get_output_primary_cookie_t pcookie; xcb_randr_get_screen_resources_current_cookie_t rcookie; @@ -756,8 +647,7 @@ void randr_query_outputs() { 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 @@ -793,7 +683,7 @@ void randr_query_outputs() { DLOG("Fixing the coordinates of floating containers\n"); Con *floating_con; TAILQ_FOREACH(floating_con, &(current->floating_head), floating_windows) - floating_fix_coordinates(floating_con, &(old_content->rect), &(first_content->rect)); + floating_fix_coordinates(floating_con, &(output->con->rect), &(first->con->rect)); DLOG("Done, next\n"); } DLOG("re-attached all workspaces\n"); @@ -845,6 +735,9 @@ void randr_query_outputs() { 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)