X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Frandr.c;h=512e0d2833f9c5bb8d6ba326e9469a91b598a3da;hb=dbafb3cf23d99eab88ff94e302b5fd7b6994a5b5;hp=69a866ab615d3b3a87edae43fd679992f4d7d8e6;hpb=c87b256200f8281092fbacb09f83164c6ae148b2;p=i3%2Fi3 diff --git a/src/randr.c b/src/randr.c index 69a866ab..512e0d28 100644 --- a/src/randr.c +++ b/src/randr.c @@ -27,7 +27,8 @@ xcb_randr_get_output_primary_reply_t *primary; /* Stores all outputs available in your current session. */ struct outputs_head outputs = TAILQ_HEAD_INITIALIZER(outputs); -static bool randr_disabled = false; +/* This is the output covering the root window */ +static Output *root_output; /* * Get a specific output by its internal X11 id. Used by randr_query_outputs @@ -72,6 +73,21 @@ Output *get_first_output(void) { die("No usable outputs available.\n"); } +/* + * Check whether there are any active outputs (excluding the root output). + * + */ +static bool any_randr_output_active(void) { + Output *output; + + TAILQ_FOREACH(output, &outputs, outputs) { + if (output != root_output && !output->to_be_disabled && output->active) + return true; + } + + return false; +} + /* * Returns the active (!) output which contains the coordinates x, y or NULL * if there is no output which contains these coordinates. @@ -92,6 +108,27 @@ Output *get_output_containing(unsigned int x, unsigned int y) { return NULL; } +/* + * Returns the active output which spans exactly the area specified by + * rect or NULL if there is no output like this. + * + */ +Output *get_output_with_dimensions(Rect rect) { + Output *output; + TAILQ_FOREACH(output, &outputs, outputs) { + if (!output->active) + continue; + DLOG("comparing x=%d y=%d %dx%d with x=%d and y=%d %dx%d\n", + rect.x, rect.y, rect.width, rect.height, + output->rect.x, output->rect.y, output->rect.width, output->rect.height); + if (rect.x == output->rect.x && rect.width == output->rect.width && + rect.y == output->rect.y && rect.height == output->rect.height) + return output; + } + + return NULL; +} + /* * 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. @@ -220,27 +257,20 @@ Output *get_output_next(direction_t direction, Output *current, output_close_far } /* - * Disables RandR support by creating exactly one output with the size of the - * X11 screen. + * Creates an output covering the root window. * */ -void disable_randr(xcb_connection_t *conn) { - DLOG("RandR extension unusable, disabling.\n"); - +Output *create_root_output(xcb_connection_t *conn) { Output *s = scalloc(1, sizeof(Output)); - s->active = true; + s->active = false; s->rect.x = 0; s->rect.y = 0; s->rect.width = root_screen->width_in_pixels; s->rect.height = root_screen->height_in_pixels; s->name = "xroot-0"; - output_init_con(s); - init_ws_for_output(s, output_get_content(s->con)); - TAILQ_INSERT_TAIL(&outputs, s, outputs); - - randr_disabled = true; + return s; } /* @@ -411,7 +441,7 @@ void init_ws_for_output(Output *output, Con *content) { /* In case the workspace we just moved was visible but there was no * other workspace to switch to, we need to initialize the source - * output aswell */ + * output as well */ if (visible && previous == NULL) { LOG("There is no workspace left on \"%s\", re-initializing\n", workspace_out->name); @@ -588,12 +618,13 @@ 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. * + * If no outputs are found use the root window. + * */ void randr_query_outputs(void) { - Output *output, *other, *first; + Output *output, *other; xcb_randr_get_output_primary_cookie_t pcookie; xcb_randr_get_screen_resources_current_cookie_t rcookie; - resources_reply *res; /* timestamp of the configuration so that we get consistent replies to all * requests (if the configuration changes between our different calls) */ @@ -602,9 +633,6 @@ void randr_query_outputs(void) { /* an output is VGA-1, LVDS-1, etc. (usually physical video outputs) */ xcb_randr_output_t *randr_outputs; - if (randr_disabled) - return; - /* Get screen resources (primary output, crtcs, outputs, modes) */ rcookie = xcb_randr_get_screen_resources_current(conn, root); pcookie = xcb_randr_get_output_primary(conn, root); @@ -613,29 +641,41 @@ void randr_query_outputs(void) { ELOG("Could not get RandR primary output\n"); else DLOG("primary output is %08x\n", primary->output); - if ((res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL)) == NULL) { - disable_randr(conn); - return; - } - cts = res->config_timestamp; - int len = xcb_randr_get_screen_resources_current_outputs_length(res); - randr_outputs = xcb_randr_get_screen_resources_current_outputs(res); + resources_reply *res = xcb_randr_get_screen_resources_current_reply(conn, rcookie, NULL); + if (res == NULL) { + ELOG("Could not query screen resources.\n"); + } else { + cts = res->config_timestamp; - /* Request information for each output */ - xcb_randr_get_output_info_cookie_t ocookie[len]; - for (int i = 0; i < len; i++) - ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts); + int len = xcb_randr_get_screen_resources_current_outputs_length(res); + randr_outputs = xcb_randr_get_screen_resources_current_outputs(res); - /* Loop through all outputs available for this X11 screen */ - for (int i = 0; i < len; i++) { - xcb_randr_get_output_info_reply_t *output; + /* Request information for each output */ + xcb_randr_get_output_info_cookie_t ocookie[len]; + for (int i = 0; i < len; i++) + ocookie[i] = xcb_randr_get_output_info(conn, randr_outputs[i], cts); - if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL) - continue; + /* Loop through all outputs available for this X11 screen */ + for (int i = 0; i < len; i++) { + xcb_randr_get_output_info_reply_t *output; - handle_output(conn, randr_outputs[i], output, cts, res); - free(output); + if ((output = xcb_randr_get_output_info_reply(conn, ocookie[i], NULL)) == NULL) + continue; + + handle_output(conn, randr_outputs[i], output, cts, res); + free(output); + } + } + + /* If there's no randr output, enable the output covering the root window. */ + if (any_randr_output_active()) { + DLOG("Active RandR output found. Disabling root output.\n"); + if (root_output->active) + root_output->to_be_disabled = true; + } else { + DLOG("No active RandR output found. Enabling root output.\n"); + root_output->active = true; } /* Check for clones, disable the clones and reduce the mode to the @@ -693,84 +733,7 @@ void randr_query_outputs(void) { * because the user disabled them or because they are clones) */ TAILQ_FOREACH(output, &outputs, outputs) { if (output->to_be_disabled) { - output->active = false; - DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); - - first = get_first_output(); - - /* TODO: refactor the following code into a nice function. maybe - * use an on_destroy callback which is implement differently for - * different container types (CT_CONTENT vs. CT_DOCKAREA)? */ - Con *first_content = output_get_content(first->con); - - if (output->con != NULL) { - /* We need to move the workspaces from the disappearing output to the first output */ - /* 1: Get the con to focus next, if the disappearing ws is focused */ - Con *next = NULL; - if (TAILQ_FIRST(&(croot->focus_head)) == output->con) { - DLOG("This output (%p) was focused! Getting next\n", output->con); - next = focused; - DLOG("next = %p\n", next); - } - - /* 2: iterate through workspaces and re-assign them, fixing the coordinates - * of floating containers as we go */ - Con *current; - Con *old_content = output_get_content(output->con); - while (!TAILQ_EMPTY(&(old_content->nodes_head))) { - current = TAILQ_FIRST(&(old_content->nodes_head)); - if (current != next && TAILQ_EMPTY(&(current->focus_head))) { - /* the workspace is empty and not focused, get rid of it */ - DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); - tree_close(current, DONT_KILL_WINDOW, false, false); - continue; - } - DLOG("Detaching current = %p / %s\n", current, current->name); - con_detach(current); - DLOG("Re-attaching current = %p / %s\n", current, current->name); - con_attach(current, first_content, false); - 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, &(output->con->rect), &(first->con->rect)); - DLOG("Done, next\n"); - } - DLOG("re-attached all workspaces\n"); - - if (next) { - DLOG("now focusing next = %p\n", next); - con_focus(next); - workspace_show(con_get_workspace(next)); - } - - /* 3: move the dock clients to the first output */ - Con *child; - TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) { - if (child->type != CT_DOCKAREA) - continue; - DLOG("Handling dock con %p\n", child); - Con *dock; - while (!TAILQ_EMPTY(&(child->nodes_head))) { - dock = TAILQ_FIRST(&(child->nodes_head)); - Con *nc; - Match *match; - nc = con_for_window(first->con, dock->window, &match); - DLOG("Moving dock client %p to nc %p\n", dock, nc); - con_detach(dock); - DLOG("Re-attaching\n"); - con_attach(dock, nc, false); - DLOG("Done\n"); - } - } - - DLOG("destroying disappearing con %p\n", output->con); - tree_close(output->con, DONT_KILL_WINDOW, true, false); - DLOG("Done. Should be fine now\n"); - output->con = NULL; - } - - output->to_be_disabled = false; - output->changed = false; + randr_disable_output(output); } if (output->changed) { @@ -779,11 +742,6 @@ void randr_query_outputs(void) { } } - if (TAILQ_EMPTY(&outputs)) { - ELOG("No outputs found via RandR, disabling\n"); - disable_randr(conn); - } - /* Just go through each active output and assign one workspace */ TAILQ_FOREACH(output, &outputs, outputs) { if (!output->active) @@ -811,6 +769,94 @@ void randr_query_outputs(void) { FREE(primary); } +/* + * Disables the output and moves its content. + * + */ +void randr_disable_output(Output *output) { + assert(output->to_be_disabled); + + output->active = false; + DLOG("Output %s disabled, re-assigning workspaces/docks\n", output->name); + + Output *first = get_first_output(); + + /* TODO: refactor the following code into a nice function. maybe + * use an on_destroy callback which is implement differently for + * different container types (CT_CONTENT vs. CT_DOCKAREA)? */ + Con *first_content = output_get_content(first->con); + + if (output->con != NULL) { + /* We need to move the workspaces from the disappearing output to the first output */ + /* 1: Get the con to focus next, if the disappearing ws is focused */ + Con *next = NULL; + if (TAILQ_FIRST(&(croot->focus_head)) == output->con) { + DLOG("This output (%p) was focused! Getting next\n", output->con); + next = focused; + DLOG("next = %p\n", next); + } + + /* 2: iterate through workspaces and re-assign them, fixing the coordinates + * of floating containers as we go */ + Con *current; + Con *old_content = output_get_content(output->con); + while (!TAILQ_EMPTY(&(old_content->nodes_head))) { + current = TAILQ_FIRST(&(old_content->nodes_head)); + if (current != next && TAILQ_EMPTY(&(current->focus_head))) { + /* the workspace is empty and not focused, get rid of it */ + DLOG("Getting rid of current = %p / %s (empty, unfocused)\n", current, current->name); + tree_close_internal(current, DONT_KILL_WINDOW, false, false); + continue; + } + DLOG("Detaching current = %p / %s\n", current, current->name); + con_detach(current); + DLOG("Re-attaching current = %p / %s\n", current, current->name); + con_attach(current, first_content, false); + 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, &(output->con->rect), &(first->con->rect)); + } + DLOG("Done, next\n"); + } + DLOG("re-attached all workspaces\n"); + + if (next) { + DLOG("now focusing next = %p\n", next); + con_focus(next); + workspace_show(con_get_workspace(next)); + } + + /* 3: move the dock clients to the first output */ + Con *child; + TAILQ_FOREACH(child, &(output->con->nodes_head), nodes) { + if (child->type != CT_DOCKAREA) + continue; + DLOG("Handling dock con %p\n", child); + Con *dock; + while (!TAILQ_EMPTY(&(child->nodes_head))) { + dock = TAILQ_FIRST(&(child->nodes_head)); + Con *nc; + Match *match; + nc = con_for_window(first->con, dock->window, &match); + DLOG("Moving dock client %p to nc %p\n", dock, nc); + con_detach(dock); + DLOG("Re-attaching\n"); + con_attach(dock, nc, false); + DLOG("Done\n"); + } + } + + DLOG("destroying disappearing con %p\n", output->con); + tree_close_internal(output->con, DONT_KILL_WINDOW, true, false); + DLOG("Done. Should be fine now\n"); + output->con = NULL; + } + + output->to_be_disabled = false; + output->changed = false; +} + /* * We have just established a connection to the X server and need the initial * XRandR information to setup workspaces for each screen. @@ -819,12 +865,20 @@ void randr_query_outputs(void) { void randr_init(int *event_base) { const xcb_query_extension_reply_t *extreply; + root_output = create_root_output(conn); + TAILQ_INSERT_TAIL(&outputs, root_output, outputs); + extreply = xcb_get_extension_data(conn, &xcb_randr_id); if (!extreply->present) { - disable_randr(conn); + DLOG("RandR is not present, activating root output.\n"); + root_output->active = true; + output_init_con(root_output); + init_ws_for_output(root_output, output_get_content(root_output->con)); + return; - } else - randr_query_outputs(); + } + + randr_query_outputs(); if (event_base != NULL) *event_base = extreply->first_event;