diff --git a/include/data.h b/include/data.h index 1165da99..c408d3f6 100644 --- a/include/data.h +++ b/include/data.h @@ -117,6 +117,17 @@ struct deco_render_params { xcb_font_t font; }; +/** + * Stores which workspace (by name) goes to which output. + * + */ +struct Workspace_Assignment { + char *name; + char *output; + + TAILQ_ENTRY(Workspace_Assignment) ws_assignments; +}; + struct Ignore_Event { int sequence; int response_type; diff --git a/include/i3.h b/include/i3.h index 2f18ce70..a18cd6bc 100644 --- a/include/i3.h +++ b/include/i3.h @@ -27,6 +27,7 @@ extern int xkb_current_group; extern TAILQ_HEAD(bindings_head, Binding) *bindings; extern TAILQ_HEAD(autostarts_head, Autostart) autostarts; extern TAILQ_HEAD(assignments_head, Match) assignments; +extern TAILQ_HEAD(ws_assignments_head, Workspace_Assignment) ws_assignments; extern SLIST_HEAD(stack_wins_head, Stack_Window) stack_wins; extern uint8_t root_depth; extern bool xcursor_supported, xkb_supported; diff --git a/src/cfgparse.y b/src/cfgparse.y index 61b67687..a5e0f0ba 100644 --- a/src/cfgparse.y +++ b/src/cfgparse.y @@ -517,14 +517,18 @@ workspace: if (ws_num < 1) { DLOG("Invalid workspace assignment, workspace number %d out of range\n", ws_num); } else { -#if 0 - Workspace *ws = workspace_get(ws_num - 1); - ws->preferred_output = $7; - if ($8 != NULL) { - workspace_set_name(ws, $8); - free($8); + char *ws_name = NULL; + if ($8 == NULL) { + asprintf(&ws_name, "%d", ws_num); + } else { + ws_name = $8; } -#endif + + DLOG("Should assign workspace %s to output %s\n", ws_name, $7); + struct Workspace_Assignment *assignment = scalloc(sizeof(struct Workspace_Assignment)); + assignment->name = ws_name; + assignment->output = $7; + TAILQ_INSERT_TAIL(&ws_assignments, assignment, ws_assignments); } } | TOKWORKSPACE WHITESPACE NUMBER WHITESPACE workspace_name diff --git a/src/main.c b/src/main.c index 91bd5d68..c079d8dc 100644 --- a/src/main.c +++ b/src/main.c @@ -33,6 +33,10 @@ struct autostarts_head autostarts = TAILQ_HEAD_INITIALIZER(autostarts); /* The list of assignments */ struct assignments_head assignments = TAILQ_HEAD_INITIALIZER(assignments); +/* The list of workspace assignments (which workspace should end up on which + * output) */ +struct ws_assignments_head ws_assignments = TAILQ_HEAD_INITIALIZER(ws_assignments); + /* We hope that those are supported and set them to true */ bool xcursor_supported = true; bool xkb_supported = true; diff --git a/src/randr.c b/src/randr.c index b61e3090..af0af334 100644 --- a/src/randr.c +++ b/src/randr.c @@ -223,9 +223,6 @@ void disable_randr(xcb_connection_t *conn) { * Initializes a CT_OUTPUT Con (searches existing ones from inplace restart * before) to use for the given Output. * - * XXX: for assignments, we probably need to move workspace creation from here - * to after the loop in randr_query_outputs(). - * */ void output_init_con(Output *output) { Con *con = NULL, *current; @@ -316,7 +313,81 @@ void output_init_con(Output *output) { FREE(name); DLOG("attaching\n"); con_attach(bottomdock, con, false); +} +/* + * Initializes at least one workspace for this output, trying the following + * steps until there is at least one workspace: + * + * • Move existing workspaces, which are assigned to be on the given output, to + * the output. + * • Create the first assigned workspace for this output. + * • Create the first unused workspace. + * + */ +static 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) { + if (strcmp(assignment->output, output->name) != 0) + continue; + + /* check if this workspace actually exists */ + Con *workspace = NULL, *out; + TAILQ_FOREACH(out, &(croot->nodes_head), nodes) + GREP_FIRST(workspace, output_get_content(out), + !strcasecmp(child->name, assignment->name)); + if (workspace == NULL) + continue; + + /* check that this workspace is not already attached (that means the + * user configured this assignment twice) */ + Con *workspace_out = con_get_output(workspace); + if (workspace_out == output->con) { + LOG("Workspace \"%s\" assigned to output \"%s\", but it is already " + "there. Do you have two assignment directives for the same " + "workspace in your configuration file?\n", + workspace->name, output->name); + continue; + } + + /* if so, move it over */ + LOG("Moving workspace \"%s\" from output \"%s\" to \"%s\" due to assignment\n", + workspace->name, workspace_out->name, output->name); + DLOG("Detaching workspace = %p / %s\n", workspace, workspace->name); + con_detach(workspace); + DLOG("Re-attaching current = %p / %s\n", workspace, workspace->name); + con_attach(workspace, content, false); + DLOG("Done, next\n"); + } + + /* if a workspace exists, we are done now */ + if (!TAILQ_EMPTY(&(content->nodes_head))) { + /* ensure that one of the workspaces is actually visible (in fullscreen + * mode), if they were invisible before, this might not be the case. */ + Con *visible = NULL; + GREP_FIRST(visible, content, child->fullscreen_mode == CF_OUTPUT); + if (!visible) { + visible = TAILQ_FIRST(&(content->nodes_head)); + workspace_show(visible->name); + } + return; + } + + /* otherwise, we create the first assigned ws for this output */ + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + if (strcmp(assignment->output, output->name) != 0) + continue; + + LOG("Initializing first assigned workspace \"%s\" for output \"%s\"\n", + assignment->name, assignment->output); + workspace_show(assignment->name); + return; + } + + /* if there is still no workspace, we create the first free workspace */ DLOG("Now adding a workspace\n"); /* add a workspace to this output */ @@ -731,15 +802,16 @@ void randr_query_outputs() { ewmh_update_workarea(); -#if 0 - /* Just go through each active output and associate one workspace */ + /* Just go through each active output and assign one workspace */ TAILQ_FOREACH(output, &outputs, outputs) { - if (!output->active || output->current_workspace != NULL) - continue; - ws = get_first_workspace_for_output(output); - initialize_output(conn, output, ws); + if (!output->active) + continue; + Con *content = output_get_content(output->con); + if (!TAILQ_EMPTY(&(content->nodes_head))) + continue; + DLOG("Should add ws for output %s\n", output->name); + init_ws_for_output(output, content); } -#endif /* Focus the primary screen, if possible */ TAILQ_FOREACH(output, &outputs, outputs) { diff --git a/src/workspace.c b/src/workspace.c index 7ff31157..5127fb00 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -18,24 +18,25 @@ * */ Con *workspace_get(const char *num, bool *created) { - Con *output, *workspace = NULL, *child; + Con *output, *workspace = NULL; - /* TODO: could that look like this in the future? - GET_MATCHING_NODE(workspace, croot, strcasecmp(current->name, num) != 0); - */ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) - TAILQ_FOREACH(child, &(output_get_content(output)->nodes_head), nodes) { - if (strcasecmp(child->name, num) != 0) + GREP_FIRST(workspace, output_get_content(output), !strcasecmp(child->name, num)); + + if (workspace == NULL) { + LOG("Creating new workspace \"%s\"\n", num); + /* unless an assignment is found, we will create this workspace on the current output */ + output = con_get_output(focused); + /* look for assignments */ + struct Workspace_Assignment *assignment; + TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { + if (strcmp(assignment->name, num) != 0) continue; - workspace = child; + LOG("Found workspace assignment to output \"%s\"\n", assignment->output); + GREP_FIRST(output, croot, !strcmp(child->name, assignment->output)); break; } - - LOG("getting ws %s\n", num); - if (workspace == NULL) { - LOG("need to create this one\n"); - output = con_get_output(focused); Con *content = output_get_content(output); LOG("got output %p with content %p\n", output, content); /* We need to attach this container after setting its type. con_attach @@ -225,7 +226,6 @@ void workspace_show(const char *num) { old = current; current->fullscreen_mode = CF_NONE; } - assert(old != NULL); /* enable fullscreen for the target workspace. If it happens to be the * same one we are currently on anyways, we can stop here. */