X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=src%2Fworkspace.c;h=8c46a94917b9f2f3dbc18848e8154d157a99fa06;hb=4b4fba0e74a2a949efa27da80059ee072a078eb5;hp=70022151117c7392f1de4bff97ee8e776322e79e;hpb=fdfe4081593268257cd65932bd5353eade45e190;p=i3%2Fi3 diff --git a/src/workspace.c b/src/workspace.c index 70022151..8c46a949 100644 --- a/src/workspace.c +++ b/src/workspace.c @@ -1,5 +1,3 @@ -#undef I3__FILE__ -#define I3__FILE__ "workspace.c" /* * vim:ts=4:sw=4:expandtab * @@ -17,6 +15,10 @@ * back-and-forth switching. */ static char *previous_workspace_name = NULL; +/* NULL-terminated list of workspace names (in order) extracted from + * keybindings. */ +static char **binding_workspace_names = NULL; + /* * Sets ws->layout to splith/splitv if default_orientation was specified in the * configfile. Otherwise, it uses splith/splitv depending on whether the output @@ -97,6 +99,7 @@ Con *workspace_get(const char *num, bool *created) { ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); + ewmh_update_wm_desktop(); if (created != NULL) *created = true; } else if (created != NULL) { @@ -107,21 +110,21 @@ Con *workspace_get(const char *num, bool *created) { } /* - * Returns a pointer to a new workspace in the given output. The workspace - * is created attached to the tree hierarchy through the given content - * container. + * Extracts workspace names from keybindings (e.g. “web” from “bindsym $mod+1 + * workspace web”), so that when an output needs a workspace, i3 can start with + * the first configured one. Needs to be called before reorder_bindings() so + * that the config-file order is used, not the i3-internal order. * */ -Con *create_workspace_on_output(Output *output, Con *content) { - /* add a workspace to this output */ - Con *out, *current; - char *name; - bool exists = true; - Con *ws = con_new(NULL, NULL); - ws->type = CT_WORKSPACE; - - /* try the configured workspace bindings first to find a free name */ +void extract_workspace_names_from_bindings(void) { Binding *bind; + int n = 0; + if (binding_workspace_names != NULL) { + for (int i = 0; binding_workspace_names[i] != NULL; i++) { + free(binding_workspace_names[i]); + } + FREE(binding_workspace_names); + } TAILQ_FOREACH(bind, bindings, bindings) { DLOG("binding with command %s\n", bind->command); if (strlen(bind->command) < strlen("workspace ") || @@ -152,18 +155,40 @@ Con *create_workspace_on_output(Output *output, Con *content) { free(target_name); continue; } - FREE(ws->name); - ws->name = target_name; - DLOG("trying name *%s*\n", ws->name); + DLOG("Saving workspace name \"%s\"\n", target_name); + + binding_workspace_names = srealloc(binding_workspace_names, ++n * sizeof(char *)); + binding_workspace_names[n - 1] = target_name; + } + binding_workspace_names = srealloc(binding_workspace_names, ++n * sizeof(char *)); + binding_workspace_names[n - 1] = NULL; +} + +/* + * Returns a pointer to a new workspace in the given output. The workspace + * is created attached to the tree hierarchy through the given content + * container. + * + */ +Con *create_workspace_on_output(Output *output, Con *content) { + /* add a workspace to this output */ + Con *out, *current; + char *name; + bool exists = true; + Con *ws = con_new(NULL, NULL); + ws->type = CT_WORKSPACE; + /* try the configured workspace bindings first to find a free name */ + for (int n = 0; binding_workspace_names[n] != NULL; n++) { + char *target_name = binding_workspace_names[n]; /* 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; struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (strcmp(assignment->name, ws->name) != 0 || - strcmp(assignment->output, output->name) == 0) + if (strcmp(assignment->name, target_name) != 0 || + strcmp(assignment->output, output_primary_name(output)) == 0) continue; assigned = true; @@ -175,10 +200,10 @@ Con *create_workspace_on_output(Output *output, Con *content) { current = NULL; TAILQ_FOREACH(out, &(croot->nodes_head), nodes) - GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, ws->name)); - + GREP_FIRST(current, output_get_content(out), !strcasecmp(child->name, target_name)); exists = (current != NULL); if (!exists) { + ws->name = sstrdup(target_name); /* Set ->num to the number of the workspace, if the name actually * is a number or starts with a number */ ws->num = ws_name_to_number(ws->name); @@ -337,6 +362,7 @@ static void workspace_defer_update_urgent_hint_cb(EV_P_ ev_timer *w, int revents static void _workspace_show(Con *workspace) { Con *current, *old = NULL; + Con *old_focus = focused; /* safe-guard against showing i3-internal workspaces like __i3_scratch */ if (con_is_internal(workspace)) @@ -415,7 +441,7 @@ static void _workspace_show(Con *workspace) { DLOG("old = %p / %s\n", old, (old ? old->name : "(null)")); /* Close old workspace if necessary. This must be done *after* doing - * urgency handling, because tree_close() will do a con_focus() on the next + * urgency handling, because tree_close_internal() will do a con_focus() on the next * client, which will clear the urgency flag too early. Also, there is no * way for con_focus() to know about when to clear urgency immediately and * when to defer it. */ @@ -424,7 +450,7 @@ static void _workspace_show(Con *workspace) { if (!workspace_is_visible(old)) { LOG("Closing old workspace (%p / %s), it is empty\n", old, old->name); yajl_gen gen = ipc_marshal_workspace_event("empty", old, NULL); - tree_close(old, DONT_KILL_WINDOW, false, false); + tree_close_internal(old, DONT_KILL_WINDOW, false, false); const unsigned char *payload; ylength length; @@ -433,9 +459,15 @@ static void _workspace_show(Con *workspace) { y(free); + /* Avoid calling output_push_sticky_windows later with a freed container. */ + if (old == old_focus) { + old_focus = NULL; + } + ewmh_update_number_of_desktops(); ewmh_update_desktop_names(); ewmh_update_desktop_viewport(); + ewmh_update_wm_desktop(); } } @@ -450,6 +482,9 @@ static void _workspace_show(Con *workspace) { /* Update the EWMH hints */ ewmh_update_current_desktop(); + + /* Push any sticky windows to the now visible workspace. */ + output_push_sticky_windows(old_focus); } /* @@ -476,34 +511,13 @@ void workspace_show_by_name(const char *num) { */ Con *workspace_next(void) { Con *current = con_get_workspace(focused); - Con *next = NULL; + Con *next = NULL, *first = NULL, *first_opposite = NULL; Con *output; if (current->num == -1) { /* If currently a named workspace, find next named workspace. */ - next = TAILQ_NEXT(current, nodes); - } else { - /* If currently a numbered workspace, find next numbered workspace. */ - TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (child->num == -1) - break; - /* Need to check child against current and next because we are - * traversing multiple lists and thus are not guaranteed the - * relative order between the list of workspaces. */ - if (current->num < child->num && (!next || child->num < next->num)) - next = child; - } - } - } - - /* Find next named workspace. */ - if (!next) { + if ((next = TAILQ_NEXT(current, nodes)) != NULL) + return next; bool found_current = false; TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ @@ -512,18 +526,20 @@ Con *workspace_next(void) { NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; + if (!first) + first = child; + if (!first_opposite || (child->num != -1 && child->num < first_opposite->num)) + first_opposite = child; if (child == current) { - found_current = 1; - } else if (child->num == -1 && (current->num != -1 || found_current)) { + found_current = true; + } else if (child->num == -1 && found_current) { next = child; - goto workspace_next_end; + return next; } } } - } - - /* Find first workspace. */ - if (!next) { + } else { + /* If currently a numbered workspace, find next numbered workspace. */ TAILQ_FOREACH(output, &(croot->nodes_head), nodes) { /* Skip outputs starting with __, they are internal. */ if (con_is_internal(output)) @@ -531,12 +547,24 @@ Con *workspace_next(void) { NODES_FOREACH(output_get_content(output)) { if (child->type != CT_WORKSPACE) continue; - if (!next || (child->num != -1 && child->num < next->num)) + if (!first || (child->num != -1 && child->num < first->num)) + first = child; + if (!first_opposite && child->num == -1) + first_opposite = child; + if (child->num == -1) + break; + /* Need to check child against current and next because we are + * traversing multiple lists and thus are not guaranteed the + * relative order between the list of workspaces. */ + if (current->num < child->num && (!next || child->num < next->num)) next = child; } } } -workspace_next_end: + + if (!next) + next = first_opposite ? first_opposite : first; + return next; } @@ -546,7 +574,7 @@ workspace_next_end: */ Con *workspace_prev(void) { Con *current = con_get_workspace(focused); - Con *prev = NULL; + Con *prev = NULL, *first_opposite = NULL, *last = NULL; Con *output; if (current->num == -1) { @@ -554,6 +582,28 @@ Con *workspace_prev(void) { prev = TAILQ_PREV(current, nodes_head, nodes); if (prev && prev->num != -1) prev = NULL; + if (!prev) { + bool found_current = false; + TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { + /* Skip outputs starting with __, they are internal. */ + if (con_is_internal(output)) + continue; + NODES_FOREACH_REVERSE(output_get_content(output)) { + if (child->type != CT_WORKSPACE) + continue; + if (!last) + last = child; + if (!first_opposite || (child->num != -1 && child->num > first_opposite->num)) + first_opposite = child; + if (child == current) { + found_current = true; + } else if (child->num == -1 && found_current) { + prev = child; + return prev; + } + } + } + } } else { /* If numbered workspace, find previous numbered workspace. */ TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { @@ -561,7 +611,13 @@ Con *workspace_prev(void) { if (con_is_internal(output)) continue; NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE || child->num == -1) + if (child->type != CT_WORKSPACE) + continue; + if (!last || (child->num != -1 && last->num < child->num)) + last = child; + if (!first_opposite && child->num == -1) + first_opposite = child; + if (child->num == -1) continue; /* Need to check child against current and previous because we * are traversing multiple lists and thus are not guaranteed @@ -572,42 +628,9 @@ Con *workspace_prev(void) { } } - /* Find previous named workspace. */ - if (!prev) { - bool found_current = false; - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (child == current) { - found_current = true; - } else if (child->num == -1 && (current->num != -1 || found_current)) { - prev = child; - goto workspace_prev_end; - } - } - } - } + if (!prev) + prev = first_opposite ? first_opposite : last; - /* Find last workspace. */ - if (!prev) { - TAILQ_FOREACH_REVERSE(output, &(croot->nodes_head), nodes_head, nodes) { - /* Skip outputs starting with __, they are internal. */ - if (con_is_internal(output)) - continue; - NODES_FOREACH_REVERSE(output_get_content(output)) { - if (child->type != CT_WORKSPACE) - continue; - if (!prev || child->num > prev->num) - prev = child; - } - } - } - -workspace_prev_end: return prev; } @@ -645,7 +668,7 @@ Con *workspace_next_on_output(void) { if (child->type != CT_WORKSPACE) continue; if (child == current) { - found_current = 1; + found_current = true; } else if (child->num == -1 && (current->num != -1 || found_current)) { next = child; goto workspace_next_on_output_end; @@ -792,9 +815,9 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { /* 2: copy layout from workspace */ split->layout = ws->layout; - Con *old_focused = TAILQ_FIRST(&(ws->focus_head)); - /* 3: move the existing cons of this workspace below the new con */ + Con **focus_order = get_focus_order(ws); + DLOG("Moving cons\n"); while (!TAILQ_EMPTY(&(ws->nodes_head))) { Con *child = TAILQ_FIRST(&(ws->nodes_head)); @@ -802,6 +825,9 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { con_attach(child, split, true); } + set_focus_order(split, focus_order); + free(focus_order); + /* 4: switch workspace layout */ ws->layout = (orientation == HORIZ) ? L_SPLITH : L_SPLITV; DLOG("split->layout = %d, ws->layout = %d\n", split->layout, ws->layout); @@ -812,9 +838,6 @@ void ws_force_orientation(Con *ws, orientation_t orientation) { /* 6: fix the percentages */ con_fix_percent(ws); - - if (old_focused) - con_focus(old_focused); } /* @@ -869,9 +892,10 @@ Con *workspace_encapsulate(Con *ws) { new->parent = ws; new->layout = ws->layout; + Con **focus_order = get_focus_order(ws); + DLOG("Moving children of workspace %p / %s into container %p\n", ws, ws->name, new); - Con *child; while (!TAILQ_EMPTY(&(ws->nodes_head))) { child = TAILQ_FIRST(&(ws->nodes_head)); @@ -879,6 +903,9 @@ Con *workspace_encapsulate(Con *ws) { con_attach(child, new, true); } + set_focus_order(new, focus_order); + free(focus_order); + con_attach(new, ws, true); return new; @@ -888,20 +915,15 @@ Con *workspace_encapsulate(Con *ws) { * Move the given workspace to the specified output. * This returns true if and only if moving the workspace was successful. */ -bool workspace_move_to_output(Con *ws, char *name) { +bool workspace_move_to_output(Con *ws, const char *name) { LOG("Trying to move workspace %p / %s to output \"%s\".\n", ws, ws->name, name); - Con *current_output_con = con_get_output(ws); - if (!current_output_con) { - ELOG("Could not get the output container for workspace %p / %s.\n", ws, ws->name); - return false; - } - - Output *current_output = get_output_by_name(current_output_con->name); - if (!current_output) { + Output *current_output = get_output_for_con(ws); + if (current_output == NULL) { ELOG("Cannot get current output. This is a bug in i3.\n"); return false; } + Output *output = get_output_from_string(current_output, name); if (!output) { ELOG("Could not get output from string \"%s\"\n", name); @@ -911,7 +933,7 @@ bool workspace_move_to_output(Con *ws, char *name) { Con *content = output_get_content(output->con); LOG("got output %p with content %p\n", output, content); - Con *previously_visible_ws = TAILQ_FIRST(&(content->nodes_head)); + Con *previously_visible_ws = TAILQ_FIRST(&(content->focus_head)); LOG("Previously visible workspace = %p / %s\n", previously_visible_ws, previously_visible_ws->name); bool workspace_was_visible = workspace_is_visible(ws); @@ -922,7 +944,7 @@ bool workspace_move_to_output(Con *ws, char *name) { bool used_assignment = false; struct Workspace_Assignment *assignment; TAILQ_FOREACH(assignment, &ws_assignments, ws_assignments) { - if (assignment->output == NULL || strcmp(assignment->output, current_output->name) != 0) + if (assignment->output == NULL || strcmp(assignment->output, output_primary_name(current_output)) != 0) continue; /* check if this workspace is already attached to the tree */