diff --git a/active.c b/active.c index e1bea5d92..9773b808f 100644 --- a/active.c +++ b/active.c @@ -43,6 +43,26 @@ struct active_window { }; RB_HEAD(active_windows, active_window); +/* Pane entry in a client-local last-pane stack. */ +struct active_pane_entry { + struct window_pane *wp; + TAILQ_ENTRY(active_pane_entry) entry; +}; +TAILQ_HEAD(active_pane_stack, active_pane_entry); + +/* Per-client and per-window local active pane state. */ +struct active_pane { + struct client *client; + struct window *window; + enum active_mode mode; + struct window_pane *pane; + struct active_pane_stack last_panes; + + RB_ENTRY(active_pane) entry; +}; +RB_HEAD(active_panes, active_pane); + +/* Compare local active window states by client and session. */ static int active_window_cmp(struct active_window *aw1, struct active_window *aw2) { @@ -63,22 +83,36 @@ active_window_cmp(struct active_window *aw1, struct active_window *aw2) } RB_GENERATE_STATIC(active_windows, active_window, entry, active_window_cmp); -static struct active_window *active_get(struct client *, struct session *); -static struct active_window *active_add(struct client *, struct session *, - struct winlink *); -static void active_free(struct active_window *); -static struct winlink *active_resolve(struct active_window *); -static void active_stack_remove(struct active_window_stack *, u_int); -static void active_stack_push(struct active_window_stack *, u_int); -static void active_changed(struct client *, struct session *, - struct winlink *, struct winlink *); +/* Compare local active pane states by client and window. */ +static int +active_pane_cmp(struct active_pane *ap1, struct active_pane *ap2) +{ + uintptr_t c1 = (uintptr_t)ap1->client; + uintptr_t c2 = (uintptr_t)ap2->client; + uintptr_t w1 = (uintptr_t)ap1->window; + uintptr_t w2 = (uintptr_t)ap2->window; + + if (c1 < c2) + return (-1); + if (c1 > c2) + return (1); + if (w1 < w2) + return (-1); + if (w1 > w2) + return (1); + return (0); +} +RB_GENERATE_STATIC(active_panes, active_pane, entry, active_pane_cmp); /* All local active window states. */ static struct active_windows active_windows = RB_INITIALIZER(&active_windows); +/* All local active pane states. */ +static struct active_panes active_panes = RB_INITIALIZER(&active_panes); + /* Find local active window state for a client and session. */ static struct active_window * -active_get(struct client *c, struct session *s) +active_window_get(struct client *c, struct session *s) { struct active_window aw; @@ -91,11 +125,11 @@ active_get(struct client *c, struct session *s) /* Add local active window state for a client and session. */ static struct active_window * -active_add(struct client *c, struct session *s, struct winlink *wl) +active_window_add(struct client *c, struct session *s, struct winlink *wl) { struct active_window *aw; - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw == NULL) { aw = xcalloc(1, sizeof *aw); aw->client = c; @@ -108,9 +142,35 @@ active_add(struct client *c, struct session *s, struct winlink *wl) return (aw); } +/* Remove a window from a local last-window stack. */ +static void +active_window_stack_remove(struct active_window_stack *stack, u_int window) +{ + struct active_window_entry *awe, *awe1; + + TAILQ_FOREACH_SAFE(awe, stack, entry, awe1) { + if (awe->window == window) { + TAILQ_REMOVE(stack, awe, entry); + free(awe); + } + } +} + +/* Push a window onto a local last-window stack. */ +static void +active_window_stack_push(struct active_window_stack *stack, u_int window) +{ + struct active_window_entry *awe; + + active_window_stack_remove(stack, window); + awe = xcalloc(1, sizeof *awe); + awe->window = window; + TAILQ_INSERT_HEAD(stack, awe, entry); +} + /* Free local active window state. */ static void -active_free(struct active_window *aw) +active_window_free(struct active_window *aw) { struct active_window_entry *awe; @@ -125,7 +185,7 @@ active_free(struct active_window *aw) /* Resolve local active window state to a valid winlink. */ static struct winlink * -active_resolve(struct active_window *aw) +active_window_resolve(struct active_window *aw) { struct winlink *wl, *next; u_int window; @@ -141,7 +201,7 @@ active_resolve(struct active_window *aw) window = TAILQ_FIRST(&aw->lastw)->window; next = winlink_find_by_window_id(&aw->session->windows, window); - active_stack_remove(&aw->lastw, window); + active_window_stack_remove(&aw->lastw, window); if (next != NULL) { aw->window = next->window->id; return (next); @@ -152,35 +212,9 @@ active_resolve(struct active_window *aw) return (aw->session->curw); } -/* Remove a window from a local last-window stack. */ -static void -active_stack_remove(struct active_window_stack *stack, u_int window) -{ - struct active_window_entry *awe, *awe1; - - TAILQ_FOREACH_SAFE(awe, stack, entry, awe1) { - if (awe->window == window) { - TAILQ_REMOVE(stack, awe, entry); - free(awe); - } - } -} - -/* Push a window onto a local last-window stack. */ -static void -active_stack_push(struct active_window_stack *stack, u_int window) -{ - struct active_window_entry *awe; - - active_stack_remove(stack, window); - awe = xcalloc(1, sizeof *awe); - awe->window = window; - TAILQ_INSERT_HEAD(stack, awe, entry); -} - /* Notify the server that a client's effective window changed. */ static void -active_changed(struct client *c, struct session *s, struct winlink *wl, +active_window_changed(struct client *c, struct session *s, struct winlink *wl, struct winlink *old) { struct event_payload *ep; @@ -225,17 +259,114 @@ active_changed(struct client *c, struct session *s, struct winlink *wl, recalculate_sizes(); } +/* Find local active pane state for a client and window. */ +static struct active_pane * +active_pane_get(struct client *c, struct window *w) +{ + struct active_pane ap; + + if (c == NULL || w == NULL) + return (NULL); + ap.client = c; + ap.window = w; + return (RB_FIND(active_panes, &active_panes, &ap)); +} + +/* Add local active pane state for a client and window. */ +static struct active_pane * +active_pane_add(struct client *c, struct window *w) +{ + struct active_pane *ap; + + ap = active_pane_get(c, w); + if (ap == NULL) { + ap = xcalloc(1, sizeof *ap); + ap->client = c; + ap->window = w; + TAILQ_INIT(&ap->last_panes); + RB_INSERT(active_panes, &active_panes, ap); + } + return (ap); +} + +/* Remove a pane from a client-local last-pane stack. */ +static void +active_pane_stack_remove(struct active_pane *ap, + struct window_pane *wp) +{ + struct active_pane_entry *ape, *ape1; + + TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) { + if (ape->wp == wp) { + TAILQ_REMOVE(&ap->last_panes, ape, entry); + free(ape); + } + } +} + +/* Push a pane onto a client-local last-pane stack. */ +static void +active_pane_stack_push(struct active_pane *ap, struct window_pane *wp) +{ + struct active_pane_entry *ape; + + if (wp == NULL) + return; + active_pane_stack_remove(ap, wp); + ape = xcalloc(1, sizeof *ape); + ape->wp = wp; + TAILQ_INSERT_HEAD(&ap->last_panes, ape, entry); +} + +/* Free local active pane state. */ +static void +active_pane_free(struct active_pane *ap) +{ + struct active_pane_entry *ape, *ape1; + + RB_REMOVE(active_panes, &active_panes, ap); + TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) { + TAILQ_REMOVE(&ap->last_panes, ape, entry); + free(ape); + } + free(ap); +} + +/* Check whether a pane can be the effective pane for a window. */ +static int +active_pane_valid(struct window *w, struct window_pane *wp) +{ + if (w == NULL || wp == NULL || wp->window != w) + return (0); + if (!window_has_pane(w, wp)) + return (0); + if (w->modal != NULL && wp != w->modal) + return (0); + return (1); +} + +/* Get the shared pane for a window, accounting for modal panes. */ +static struct window_pane * +active_pane_default(struct window *w) +{ + if (w == NULL) + return (NULL); + if (w->modal != NULL) + return (w->modal); + return (w->active); +} + /* Return if a client has local window selection for a session. */ int active_is_local_window(struct client *c, struct session *s) { - return (active_get(c, s) != NULL); + return (active_window_get(c, s) != NULL); } /* Make a client use local or shared window selection for a session. */ void active_set_local_window(struct client *c, struct session *s, - enum active_window_mode mode) + enum active_mode mode) { struct active_window *aw; struct winlink *wl, *old; @@ -245,17 +376,17 @@ active_set_local_window(struct client *c, struct session *s, old = active_get_effective_winlink(c, s); if (mode == ACTIVE_LOCAL) { - active_add(c, s, old); + active_window_add(c, s, old); if (c != NULL && c->session == s) c->flags |= CLIENT_REDRAWSTATUS; return; } - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw != NULL) { - active_free(aw); + active_window_free(aw); wl = active_get_effective_winlink(c, s); - active_changed(c, s, wl, old); + active_window_changed(c, s, wl, old); if (c != NULL && c->session == s) c->flags |= CLIENT_REDRAWSTATUS; } @@ -269,9 +400,9 @@ active_get_effective_winlink(struct client *c, struct session *s) if (s == NULL) return (NULL); - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw != NULL) - return (active_resolve(aw)); + return (active_window_resolve(aw)); return (s->curw); } @@ -297,19 +428,19 @@ active_select_window(struct client *c, struct session *s, struct winlink *wl) if (wl == NULL) return (-1); - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw == NULL) return (session_set_current(s, wl)); - old = active_resolve(aw); + old = active_window_resolve(aw); if (old == wl) return (1); if (old != NULL) - active_stack_push(&aw->lastw, old->window->id); - active_stack_remove(&aw->lastw, wl->window->id); + active_window_stack_push(&aw->lastw, old->window->id); + active_window_stack_remove(&aw->lastw, wl->window->id); aw->window = wl->window->id; changed = (old != wl); - active_changed(c, s, wl, old); + active_window_changed(c, s, wl, old); return (changed ? 0 : 1); } @@ -369,7 +500,7 @@ active_last_window(struct client *c, struct session *s) struct winlink *wl; u_int window; - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw == NULL) return (session_last(s)); while (!TAILQ_EMPTY(&aw->lastw)) { @@ -378,48 +509,11 @@ active_last_window(struct client *c, struct session *s) wl = winlink_find_by_window_id(&s->windows, window); if (wl != NULL) return (active_select_window(c, s, wl)); - active_stack_remove(&aw->lastw, window); + active_window_stack_remove(&aw->lastw, window); } return (-1); } -/* Remove all state for a client. */ -void -active_remove_client(struct client *c) -{ - struct active_window *aw, *aw1; - - RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) { - if (aw->client == c) - active_free(aw); - } -} - -/* Remove all state for a session. */ -void -active_remove_session(struct session *s) -{ - struct active_window *aw, *aw1; - - RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) { - if (aw->session == s) - active_free(aw); - } -} - -/* Remove a window from all local state. */ -void -active_remove_window(struct window *w) -{ - struct active_window *aw; - - RB_FOREACH(aw, active_windows, &active_windows) { - active_stack_remove(&aw->lastw, w->id); - if (aw->window == w->id && aw->session->curw != NULL) - aw->window = aw->session->curw->window->id; - } -} - /* Return if this winlink is the effective winlink. */ int active_is_effective_window(struct client *c, struct session *s, @@ -437,7 +531,7 @@ active_is_last_window(struct client *c, struct session *s, struct winlink *wl) if (wl == NULL) return (0); - aw = active_get(c, s); + aw = active_window_get(c, s); if (aw == NULL) return (wl == TAILQ_FIRST(&s->lastw)); TAILQ_FOREACH(awe, &aw->lastw, entry) { @@ -447,3 +541,197 @@ active_is_last_window(struct client *c, struct session *s, struct winlink *wl) } return (0); } + +/* Return if a client has local pane selection for a window. */ +int +active_has_local_pane(struct client *c, struct window *w) +{ + struct active_pane *ap; + + ap = active_pane_get(c, w); + return (ap != NULL && ap->mode == ACTIVE_LOCAL); +} + +/* Make a client use local or shared pane selection for a window. */ +void +active_set_pane_mode(struct client *c, struct window *w, enum active_mode mode) +{ + struct active_pane *ap; + struct window_pane *wp; + + if (c == NULL || w == NULL) + return; + if (mode == ACTIVE_SHARED) { + ap = active_pane_get(c, w); + if (ap != NULL) + ap->mode = ACTIVE_SHARED; + server_redraw_client(c); + server_status_client(c); + return; + } + + if (server_client_how_many() < 2) { + ap = active_pane_get(c, w); + if (ap != NULL) + ap->mode = ACTIVE_SHARED; + server_redraw_client(c); + server_status_client(c); + return; + } + + wp = active_get_effective_pane(c, w); + if (wp == NULL) + wp = active_pane_default(w); + ap = active_pane_add(c, w); + ap->mode = ACTIVE_LOCAL; + ap->pane = wp; + server_redraw_client(c); + server_status_client(c); +} + +/* Return the effective active pane for a client and window. */ +struct window_pane * +active_get_effective_pane(struct client *c, struct window *w) +{ + struct active_pane *ap; + struct window_pane *wp; + + wp = active_pane_default(w); + ap = active_pane_get(c, w); + if (ap == NULL || ap->mode != ACTIVE_LOCAL) + return (wp); + if (!active_pane_valid(w, ap->pane)) { + ap->pane = wp; + if (wp != NULL) + active_pane_stack_remove(ap, wp); + return (wp); + } + return (ap->pane); +} + +/* Return the effective last pane for a client and window. */ +struct window_pane * +active_get_last_pane(struct client *c, struct window *w) +{ + struct active_pane *ap; + struct active_pane_entry *ape, *ape1; + + if (!active_has_local_pane(c, w)) + return (w == NULL ? NULL : TAILQ_FIRST(&w->last_panes)); + + ap = active_pane_get(c, w); + TAILQ_FOREACH_SAFE(ape, &ap->last_panes, entry, ape1) { + if (active_pane_valid(w, ape->wp)) + return (ape->wp); + TAILQ_REMOVE(&ap->last_panes, ape, entry); + free(ape); + } + return (NULL); +} + +/* Select a pane using either shared or local state. */ +int +active_set_pane(struct client *c, struct window *w, struct window_pane *wp, + int notify) +{ + struct active_pane *ap; + struct window_pane *lastwp; + + if (!active_pane_valid(w, wp)) + return (0); + if (!active_has_local_pane(c, w)) + return (window_set_active_pane(w, wp, notify)); + + lastwp = active_get_effective_pane(c, w); + if (wp == lastwp) + return (0); + + ap = active_pane_add(c, w); + active_pane_stack_remove(ap, wp); + active_pane_stack_push(ap, lastwp); + ap->pane = wp; + + if (c != NULL) { + server_redraw_client(c); + server_status_client(c); + } + return (1); +} + +/* Remove all local pane state if fewer than two clients remain. */ +void +active_check_clients(void) +{ + struct active_pane *ap, *ap1; + + if (server_client_how_many() >= 2) + return; + + RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) { + if (ap->mode == ACTIVE_LOCAL) { + server_redraw_client(ap->client); + server_status_client(ap->client); + } + active_pane_free(ap); + } +} + +/* Remove all state for a client. */ +void +active_remove_client(struct client *c) +{ + struct active_window *aw, *aw1; + struct active_pane *ap, *ap1; + + RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) { + if (aw->client == c) + active_window_free(aw); + } + RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) { + if (ap->client == c) + active_pane_free(ap); + } +} + +/* Remove all window state for a session. */ +void +active_remove_session(struct session *s) +{ + struct active_window *aw, *aw1; + + RB_FOREACH_SAFE(aw, active_windows, &active_windows, aw1) { + if (aw->session == s) + active_window_free(aw); + } +} + +/* Remove a window from all local state. */ +void +active_remove_window(struct window *w) +{ + struct active_window *aw; + struct active_pane *ap, *ap1; + + RB_FOREACH(aw, active_windows, &active_windows) { + active_window_stack_remove(&aw->lastw, w->id); + if (aw->window == w->id && aw->session->curw != NULL) + aw->window = aw->session->curw->window->id; + } + RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) { + if (ap->window == w) + active_pane_free(ap); + } +} + +/* Remove a pane from all local pane state. */ +void +active_remove_pane(struct window_pane *wp) +{ + struct active_pane *ap, *ap1; + + RB_FOREACH_SAFE(ap, active_panes, &active_panes, ap1) { + active_pane_stack_remove(ap, wp); + if (ap->pane == wp) + ap->pane = NULL; + } +} diff --git a/cmd-find.c b/cmd-find.c index 090e8b4cb..13301e19f 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -885,7 +885,7 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) cmd_find_clear_state(fs, flags); fs->wl = active_get_effective_winlink(c, c->session); - fs->wp = fs->wl->window->active; + fs->wp = active_get_effective_pane(c, fs->wl->window); if (fs->wp == NULL) { cmd_find_from_session(fs, c->session, flags); return (0); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 283bdda72..c184fa977 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -186,6 +186,7 @@ cmd_join_pane_place(struct cmdq_item *item, struct winlink *wl, lc->g.yoff = yoff; layout_fix_panes(w, NULL); } + redraw_invalidate_scene(w); events_fire_window("window-layout-changed", w); server_redraw_window(w); @@ -358,6 +359,7 @@ cmd_join_pane_zindex(struct cmdq_item *item, struct winlink *wl, else TAILQ_INSERT_TAIL(&w->z_index, wp, zentry); + redraw_invalidate_scene(w); events_fire_window("window-layout-changed", w); server_redraw_window(w); @@ -397,6 +399,7 @@ cmd_join_pane_tile(struct cmdq_item *item, struct args *args, struct window *w, window_set_active_pane(w, wp, 1); layout_fix_offsets(w); layout_fix_panes(w, NULL); + redraw_invalidate_scene(w); events_fire_window("window-layout-changed", w); server_redraw_window(w); diff --git a/cmd-run-shell.c b/cmd-run-shell.c index 72a4cbad6..dd2bf0345 100644 --- a/cmd-run-shell.c +++ b/cmd-run-shell.c @@ -79,7 +79,7 @@ static void cmd_run_shell_print(struct job *job, const char *msg) { struct cmd_run_shell_data *cdata = job_get_data(job); - struct client *c; + struct client *c = cdata->client; struct session *s; struct window_pane *wp = NULL; struct window *w; @@ -93,11 +93,10 @@ cmd_run_shell_print(struct job *job, const char *msg) cmdq_print(cdata->item, "%s", msg); return; } - c = cdata->client; if (c != NULL && c->session != NULL) { s = c->session; w = active_get_effective_window(c, s); - wp = w->active; + wp = active_get_effective_pane(c, w); } if (wp == NULL && cmd_find_from_nothing(&fs, 0) == 0) wp = fs.wp; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 393a5c62c..75ebb1e02 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -33,8 +33,8 @@ const struct cmd_entry cmd_select_pane_entry = { .name = "select-pane", .alias = "selectp", - .args = { "DdegLlMmP:RT:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */ - .usage = "[-DdeLlMmRUZ] [-T title] " CMD_TARGET_PANE_USAGE, + .args = { "DdegLlMmP:RsST:t:UZ", 0, 0, NULL }, /* -P and -g deprecated */ + .usage = "[-DdeLlMmRsSUZ] [-T title] " CMD_TARGET_PANE_USAGE, .target = { 't', CMD_FIND_PANE, 0 }, @@ -85,6 +85,7 @@ static enum cmd_retval cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item) { struct args *args = cmd_get_args(self); + struct client *c = cmdq_get_client(item); struct cmd_find_state *target = cmdq_get_target(item); struct event_payload *ep; struct cmd_find_state fs; @@ -138,8 +139,9 @@ cmd_select_pane_marked_pane(struct cmd *self, struct cmdq_item *item) server_status_window(mwp->window); } if (window_pane_is_floating(wp)) { - window_redraw_active_switch(wp->window, wp); - window_set_active_pane(wp->window, wp, 1); + if (!active_has_local_pane(c, wp->window)) + window_redraw_active_switch(wp->window, wp); + active_set_pane(c, wp->window, wp, 1); } return (CMD_RETURN_NORMAL); } @@ -153,25 +155,39 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct event_payload *ep; struct cmd_find_state fs; + struct client *c = cmdq_get_client(item); struct winlink *wl = target->wl; struct window *w = wl->window; struct session *s = target->s; - struct window_pane *wp = target->wp, *lastwp; + struct window_pane *wp = target->wp, *lastwp, *activewp; struct options *oo = wp->options; char *title; const char *style; struct options_entry *o; + if (args_has(args, 's') || args_has(args, 'S')) { + if (c == NULL || c->session == NULL) { + cmdq_error(item, "no current client"); + return (CMD_RETURN_ERROR); + } + if (args_has(args, 's')) + active_set_pane_mode(c, w, ACTIVE_LOCAL); + else + active_set_pane_mode(c, w, ACTIVE_SHARED); + return (CMD_RETURN_NORMAL); + } + if (entry == &cmd_last_pane_entry || args_has(args, 'l')) { /* * Check for no last pane found in case the other pane was * spawned without being visited (for example split-window -d). */ - lastwp = TAILQ_FIRST(&w->last_panes); + lastwp = active_get_last_pane(c, w); if (lastwp == NULL && window_count_panes(w, 1) == 2) { - lastwp = TAILQ_PREV(w->active, window_panes, entry); + activewp = active_get_effective_pane(c, w); + lastwp = TAILQ_PREV(activewp, window_panes, entry); if (lastwp == NULL) - lastwp = TAILQ_NEXT(w->active, entry); + lastwp = TAILQ_NEXT(activewp, entry); } if (lastwp == NULL) { cmdq_error(item, "no last pane"); @@ -188,8 +204,9 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) } else { if (window_push_zoom(w, 0, args_has(args, 'Z'))) server_redraw_window(w); - window_redraw_active_switch(w, lastwp); - if (window_set_active_pane(w, lastwp, 1)) { + if (!active_has_local_pane(c, w)) + window_redraw_active_switch(w, lastwp); + if (active_set_pane(c, w, lastwp, 1)) { cmd_find_from_winlink(current, wl, 0); cmd_select_pane_redraw(w); } @@ -267,12 +284,14 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (wp == w->active) + activewp = active_get_effective_pane(c, w); + if (wp == activewp) return (CMD_RETURN_NORMAL); if (window_push_zoom(w, 0, args_has(args, 'Z'))) server_redraw_window(w); - window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp, 1)) + if (!active_has_local_pane(c, w)) + window_redraw_active_switch(w, wp); + if (active_set_pane(c, w, wp, 1)) cmd_find_from_winlink_pane(current, wl, wp, 0); cmdq_insert_hook(s, item, current, "after-select-pane"); cmd_select_pane_redraw(w); diff --git a/cmd-select-window.c b/cmd-select-window.c index ed7b86556..79d17bf8d 100644 --- a/cmd-select-window.c +++ b/cmd-select-window.c @@ -90,7 +90,7 @@ cmd_select_window_exec(struct cmd *self, struct cmdq_item *item) struct cmd_find_state *target = cmdq_get_target(item); struct winlink *wl = target->wl, *current_wl; struct session *s = target->s; - enum active_window_mode mode; + enum active_mode mode; int next, previous, last, activity; next = (cmd_get_entry(self) == &cmd_next_window_entry); diff --git a/format.c b/format.c index c3099fc51..b8e6b96c6 100644 --- a/format.c +++ b/format.c @@ -1061,7 +1061,8 @@ format_cb_pane_fg(struct format_tree *ft) if (wp == NULL) return (NULL); - tty_default_colours(&gc, wp, NULL); + tty_default_colours(&gc, wp, + active_get_effective_pane(ft->c, wp->window), NULL); return (xstrdup(colour_tostring(gc.fg))); } @@ -1112,7 +1113,8 @@ format_cb_pane_bg(struct format_tree *ft) if (wp == NULL) return (NULL); - tty_default_colours(&gc, wp, NULL); + tty_default_colours(&gc, wp, + active_get_effective_pane(ft->c, wp->window), NULL); return (xstrdup(colour_tostring(gc.bg))); } @@ -2099,14 +2101,45 @@ format_cb_synchronized_output_flag(struct format_tree *ft) static void * format_cb_pane_active(struct format_tree *ft) { + struct window_pane *active; + if (ft->wp != NULL) { - if (ft->wp == ft->wp->window->active) + active = active_get_effective_pane(ft->c, ft->wp->window); + if (ft->wp == active) return (xstrdup("1")); return (xstrdup("0")); } return (NULL); } +/* Callback for pane_local_active. */ +static void * +format_cb_pane_local_active(struct format_tree *ft) +{ + struct window_pane *active; + + if (ft->wp != NULL && active_has_local_pane(ft->c, ft->wp->window)) { + active = active_get_effective_pane(ft->c, ft->wp->window); + if (ft->wp == active) + return (xstrdup("1")); + return (xstrdup("0")); + } + return (NULL); +} + +/* Callback for pane_local_mode. */ +static void * +format_cb_pane_local_mode(struct format_tree *ft) +{ + struct window *w = ft->w; + + if (ft->wp != NULL) + w = ft->wp->window; + if (active_has_local_pane(ft->c, w)) + return (xstrdup("1")); + return (xstrdup("0")); +} + /* Callback for pane_at_left. */ static void * format_cb_pane_at_left(struct format_tree *ft) @@ -2385,7 +2418,7 @@ static void * format_cb_pane_last(struct format_tree *ft) { if (ft->wp != NULL) { - if (ft->wp == TAILQ_FIRST(&ft->wp->window->last_panes)) + if (ft->wp == active_get_last_pane(ft->c, ft->wp->window)) return (xstrdup("1")); return (xstrdup("0")); } @@ -3750,6 +3783,12 @@ static const struct format_table_entry format_table[] = { { "pane_left", FORMAT_TABLE_STRING, format_cb_pane_left }, + { "pane_local_active", FORMAT_TABLE_STRING, + format_cb_pane_local_active + }, + { "pane_local_mode", FORMAT_TABLE_STRING, + format_cb_pane_local_mode + }, { "pane_marked", FORMAT_TABLE_STRING, format_cb_pane_marked }, @@ -6746,7 +6785,7 @@ format_defaults(struct format_tree *ft, struct client *c, struct session *s, if (wl == NULL && s != NULL) wl = active_get_effective_winlink(c, s); if (wp == NULL && wl != NULL) - wp = wl->window->active; + wp = active_get_effective_pane(c, wl->window); if (c != NULL) format_defaults_client(ft, c); diff --git a/input.c b/input.c index 0aac9bfbb..bb542bec5 100644 --- a/input.c +++ b/input.c @@ -3078,7 +3078,8 @@ input_osc_10(struct input_ctx *ictx, const char *p) return; c = window_pane_get_fg_control_client(wp); if (c == -1) { - tty_default_colours(&defaults, wp, NULL); + tty_default_colours(&defaults, wp, wp->window->active, + NULL); if (COLOUR_DEFAULT(defaults.fg)) c = window_pane_get_fg(wp); else diff --git a/key-bindings.c b/key-bindings.c index b81cc5cab..ae62d56c3 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -71,6 +71,8 @@ " '#{?#{!:#{pane_floating_flag}},Horizontal Split,}' 'h' {split-window -h}" \ " '#{?#{!:#{pane_floating_flag}},Vertical Split,}' 'v' {split-window -v}" \ " ''" \ + " '#{?#{>=:#{window_active_clients},2},#{?pane_local_mode,Shared Active Pane,Local Active Pane},}' 'a' {if -F '#{pane_local_mode}' {select-pane -S} {select-pane -s}}" \ + " ''" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Up,}' 'u' {swap-pane -U}" \ " '#{?#{&&:#{!:#{pane_floating_flag}},#{>:#{window_panes},1}},Swap Down,}' 'd' {swap-pane -D}" \ " '#{?pane_marked_set,,-}Swap Marked' 's' {swap-pane}" \ @@ -501,6 +503,7 @@ key_bindings_init(void) /* Mouse button 1 down on pane. */ "bind -n MouseDown1Pane { select-pane -t=; send -M }", + "bind -n M-MouseDown1Pane { move-pane -P front -t= }", /* Mouse button 1 drag on pane. */ "bind -n MouseDrag1Pane { if -F '#{||:#{pane_in_mode},#{mouse_any_flag}}' { send -M } { copy-mode -M } }", @@ -522,6 +525,7 @@ key_bindings_init(void) /* Mouse button 1 on border. */ "bind -n MouseDown1Border { select-pane -M }", + "bind -n M-MouseDown1Border { move-pane -P front -t= }", /* Mouse button 1 drag on border. */ "bind -n MouseDrag1Border { resize-pane -M }", diff --git a/options-table.c b/options-table.c index 70efc9bbc..3fd714f52 100644 --- a/options-table.c +++ b/options-table.c @@ -1533,8 +1533,9 @@ const struct options_table_entry options_table[] = { .scope = OPTIONS_TABLE_WINDOW|OPTIONS_TABLE_PANE, .default_str = "fg=#{?pane_modal_flag,themeblue," "#{?pane_marked,thememagenta," + "#{?pane_local_active,themecyan," "#{?synchronize-panes,themered," - "#{?pane_in_mode,themeyellow,themegreen}}}}", + "#{?pane_in_mode,themeyellow,themegreen}}}}}", .flags = OPTIONS_TABLE_IS_STYLE, .separator = ",", .text = "Style of the active pane border." diff --git a/regress/local-pane-selection.sh b/regress/local-pane-selection.sh new file mode 100644 index 000000000..db0e4355c --- /dev/null +++ b/regress/local-pane-selection.sh @@ -0,0 +1,201 @@ +#!/bin/sh + +PATH=/bin:/usr/bin +TERM=screen +LANG=C.UTF-8 +LC_ALL=C.UTF-8 +export TERM LANG LC_ALL + +[ -z "$TEST_TMUX" ] && TEST_TMUX=$(readlink -f ../tmux) +TMUX="$TEST_TMUX -LtestA$$ -f/dev/null" + +TMPDIR=${TMPDIR:-/tmp}/tmux-local-pane.$$ +mkdir -p "$TMPDIR" || exit 1 +IN1="$TMPDIR/in1" +IN2="$TMPDIR/in2" +OUT1="$TMPDIR/out1" +OUT2="$TMPDIR/out2" + +cleanup() +{ + $TMUX kill-server 2>/dev/null + rm -rf "$TMPDIR" +} +trap cleanup 0 1 15 + +fail() +{ + echo "$1" + exit 1 +} + +wait_for() +{ + file=$1 + pattern=$2 + i=0 + while [ "$i" -lt 20 ]; do + if grep -q "$pattern" "$file"; then + return 0 + fi + sleep 1 + i=$((i + 1)) + done + echo "missing '$pattern' in $file" + cat "$file" + return 1 +} + +send1() +{ + printf '%s\n' "$*" >&3 +} + +send2() +{ + printf '%s\n' "$*" >&4 +} + +check_fmt() +{ + out=$($TMUX display-message -p -t "$1" "$2" 2>&1) + if [ "$out" != "$3" ]; then + echo "Format '$2' for '$1' wrong." + echo "Expected: '$3'" + echo "But got: '$out'" + exit 1 + fi +} + +$TMUX kill-server 2>/dev/null +$TMUX new-session -d -s active -x 80 -y 24 || exit 1 +$TMUX split-window -d -h -t active:0 || exit 1 +$TMUX split-window -d -v -t active:0.0 || exit 1 +style=$($TMUX show-options -wgv pane-active-border-style) || exit 1 +case "$style" in +*pane_local_active*themecyan*) ;; +*) fail "pane-active-border-style does not include local active colour" ;; +esac + +p0=$($TMUX display-message -p -t active:0.0 '#{pane_id}') || exit 1 +p1=$($TMUX display-message -p -t active:0.1 '#{pane_id}') || exit 1 +p2=$($TMUX display-message -p -t active:0.2 '#{pane_id}') || exit 1 +$TMUX select-pane -t "$p0" || exit 1 +check_fmt active:0 '#{pane_id}' "$p0" + +mkfifo "$IN1" "$IN2" || exit 1 +: >"$OUT1" +: >"$OUT2" +$TMUX -C attach-session -t active <"$IN1" >"$OUT1" 2>&1 & +PID1=$! +exec 3>"$IN1" + +send1 'display-message -p C1_READY' +wait_for "$OUT1" 'C1_READY' || exit 1 + +# One attached client cannot enter local mode. +send1 'select-pane -s' +send1 "select-pane -t $p1" +send1 'display-message -p "C1_SINGLE:#{pane_id}:#{pane_active}:#{pane_local_active}"' +wait_for "$OUT1" "C1_SINGLE:$p1:1:" || exit 1 +check_fmt active:0 '#{pane_id}' "$p1" +send1 "select-pane -t $p0" +check_fmt active:0 '#{pane_id}' "$p0" + +$TMUX -C attach-session -t active <"$IN2" >"$OUT2" 2>&1 & +PID2=$! +exec 4>"$IN2" + +send2 'display-message -p C2_READY' +wait_for "$OUT2" 'C2_READY' || exit 1 + +# Both clients start in shared mode and see the window active pane. +send1 'display-message -p "C1_START:#{pane_id}:#{pane_active}:#{pane_local_active}"' +send2 'display-message -p "C2_START:#{pane_id}:#{pane_active}:#{pane_local_active}"' +wait_for "$OUT1" "C1_START:$p0:1:" || exit 1 +wait_for "$OUT2" "C2_START:$p0:1:" || exit 1 + +# Client 1 enters local mode and selects a pane without changing shared state. +send1 'select-pane -s' +send1 "select-pane -t $p1" +send1 'display-message -p "C1_LOCAL:#{pane_id}:#{pane_active}:#{pane_local_active}:#{pane_local_mode}"' +send1 "display-message -p -t $p0 \"C1_LOCAL_INACTIVE:#{pane_id}:#{pane_active}:#{pane_local_active}\"" +wait_for "$OUT1" "C1_LOCAL:$p1:1:1:1" || exit 1 +wait_for "$OUT1" "C1_LOCAL_INACTIVE:$p0:0:0" || exit 1 +check_fmt active:0 '#{pane_id}' "$p0" +send2 'display-message -p "C2_SHARED:#{pane_id}:#{pane_active}:#{pane_local_active}"' +wait_for "$OUT2" "C2_SHARED:$p0:1:" || exit 1 + +# Client 2 can keep an independent local pane in the same window. +send2 'select-pane -s' +send2 "select-pane -t $p2" +send2 'display-message -p "C2_LOCAL:#{pane_id}:#{pane_active}:#{pane_local_active}"' +wait_for "$OUT2" "C2_LOCAL:$p2:1:1" || exit 1 +send1 'display-message -p "C1_STILL:#{pane_id}:#{pane_active}:#{pane_local_active}"' +wait_for "$OUT1" "C1_STILL:$p1:1:1" || exit 1 + +# Local last-pane history is independent of the shared window history. +send1 "select-pane -t $p2" +send1 'last-pane' +send1 'display-message -p "C1_LAST:#{pane_id}:#{pane_last}"' +wait_for "$OUT1" "C1_LAST:$p1:0" || exit 1 +check_fmt active:0 '#{pane_id}' "$p0" + +# Switching back to shared mode immediately follows window->active. +send1 'select-pane -S' +send1 'display-message -p "C1_SHARED_AGAIN:#{pane_id}:#{pane_active}"' +wait_for "$OUT1" "C1_SHARED_AGAIN:$p0:1" || exit 1 +send1 "select-pane -t $p1" +check_fmt active:0 '#{pane_id}' "$p1" +send2 'display-message -p "C2_STILL_LOCAL:#{pane_id}:#{pane_active}"' +wait_for "$OUT2" "C2_STILL_LOCAL:$p2:1" || exit 1 + +# Local selection survives switching away and back to the window. +send2 'new-window -n other' +send2 'previous-window' +send2 'display-message -p "C2_AFTER_WINDOW_SWITCH:#{pane_id}:#{pane_active}"' +wait_for "$OUT2" "C2_AFTER_WINDOW_SWITCH:$p2:1" || exit 1 + +# Removing a locally active pane recovers to the shared active pane. +$TMUX kill-pane -t "$p2" || exit 1 +send2 'display-message -p "C2_AFTER_KILL:#{pane_id}:#{pane_active}"' +wait_for "$OUT2" "C2_AFTER_KILL:$p1:1" || exit 1 + +# Local selection of a floating pane does not raise it unless requested. +f1=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 8 -Y 3 \ + -t active:0 'sleep 100') || exit 1 +f2=$($TMUX new-pane -dPF '#{pane_id}' -x 20 -y 6 -X 12 -Y 5 \ + -t active:0 'sleep 100') || exit 1 +check_fmt "$f1" '#{pane_z}' 1 +check_fmt "$f2" '#{pane_z}' 0 +send1 'select-pane -s' +send1 "select-pane -t $f1" +send1 "display-message -p -t $f1 \"C1_FLOAT_LOCAL:#{pane_id}:#{pane_active}:#{pane_z}\"" +wait_for "$OUT1" "C1_FLOAT_LOCAL:$f1:1:1" || exit 1 +check_fmt "$f1" '#{pane_z}' 1 +send1 "select-pane -M -t $f1" +send1 "display-message -p -t $f1 \"C1_FLOAT_BORDER:#{pane_id}:#{pane_active}:#{pane_z}\"" +wait_for "$OUT1" "C1_FLOAT_BORDER:$f1:1:1" || exit 1 +check_fmt "$f1" '#{pane_z}' 1 +send1 "move-pane -P front -t $f1" +send1 "display-message -p -t $f1 \"C1_FLOAT_RAISE:#{pane_z}\"" +wait_for "$OUT1" "C1_FLOAT_RAISE:0" || exit 1 +check_fmt "$f1" '#{pane_z}' 0 +$TMUX kill-pane -t "$f1" || exit 1 +$TMUX kill-pane -t "$f2" || exit 1 + +# When only one client remains, local mode is cleared. +send1 'select-pane -s' +send1 "select-pane -t $p0" +send1 'display-message -p "C1_LOCAL_BEFORE_DETACH:#{pane_id}:#{pane_local_active}"' +wait_for "$OUT1" "C1_LOCAL_BEFORE_DETACH:$p0:1" || exit 1 +kill "$PID2" 2>/dev/null +wait "$PID2" 2>/dev/null +send1 'display-message -p "C1_AFTER_DETACH:#{pane_id}:#{pane_local_active}"' +wait_for "$OUT1" "C1_AFTER_DETACH:$p1:" || exit 1 + +kill "$PID1" 2>/dev/null +wait "$PID1" 2>/dev/null +$TMUX display-message -p alive >/dev/null 2>&1 || fail "server died" + +exit 0 diff --git a/screen-redraw.c b/screen-redraw.c index cdf3990b1..e1189051f 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -1114,7 +1114,7 @@ redraw_draw_pane_span(struct redraw_draw_ctx *dctx, struct tty_style_ctx style_ctx; u_int px, py; - tty_default_colours(&defaults, wp, &style_ctx.dim); + tty_default_colours(&defaults, wp, dctx->active, &style_ctx.dim); style_ctx.defaults = &defaults; style_ctx.palette = &wp->palette; style_ctx.hyperlinks = s->hyperlinks; @@ -1323,7 +1323,7 @@ redraw_draw_scrollbar_span(struct redraw_draw_ctx *dctx, memcpy(&slgc, &gc, sizeof slgc); slgc.fg = gc.bg; slgc.bg = gc.fg; - tty_default_colours(&pad_gc, wp, NULL); + tty_default_colours(&pad_gc, wp, dctx->active, NULL); sb_w = sb_style->width; sb_pad = sb_style->pad; @@ -1601,7 +1601,7 @@ redraw_set_draw_context(struct redraw_draw_ctx *dctx, wl = active_get_effective_winlink(c, s); if (server_is_marked(s, wl, marked_pane.wp)) dctx->marked = marked_pane.wp; - dctx->active = active_get_effective_window(c, s)->active; + dctx->active = active_get_effective_pane(c, wl->window); lines = status_line_size(c); if (options_get_number(oo, "status-position") == 0) diff --git a/screen-write.c b/screen-write.c index 84ca6fe6f..6a74031a0 100644 --- a/screen-write.c +++ b/screen-write.c @@ -134,7 +134,7 @@ screen_write_redraw_cb(const struct tty_ctx *ttyctx) static int screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) { - struct window_pane *wp = ttyctx->arg; + struct window_pane *wp = ttyctx->arg, *active; if (ttyctx->flags & TTY_CTX_INVISIBLE_PANES) { if (session_has(c->session, wp->window)) @@ -160,11 +160,14 @@ screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) return (-1); } + active = active_get_effective_pane(c, wp->window); if (tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, &ttyctx->wsx, &ttyctx->wsy)) ttyctx->flags |= TTY_CTX_WINDOW_BIGGER; else ttyctx->flags &= ~TTY_CTX_WINDOW_BIGGER; + tty_default_colours(&ttyctx->defaults, wp, active, + &ttyctx->style_ctx.dim); ttyctx->xoff = ttyctx->rxoff = wp->xoff; ttyctx->yoff = ttyctx->ryoff = wp->yoff; @@ -296,7 +299,7 @@ screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, ttyctx->redraw_cb = screen_write_redraw_cb; if (ctx->wp != NULL) { tty_default_colours(&ttyctx->defaults, ctx->wp, - &ttyctx->style_ctx.dim); + ctx->wp->window->active, &ttyctx->style_ctx.dim); ttyctx->style_ctx.palette = &ctx->wp->palette; ttyctx->set_client_cb = screen_write_set_client_cb; ttyctx->arg = ctx->wp; diff --git a/server-client.c b/server-client.c index 116162a7d..76daa356f 100644 --- a/server-client.c +++ b/server-client.c @@ -505,6 +505,7 @@ server_client_lost(struct client *c) server_client_attached_lost(c); events_fire_client("client-detached", c); } + active_check_clients(); if (c->name != NULL && (c->flags & (CLIENT_CONTROL|CLIENT_TERMINAL))) events_fire_client("client-closed", c); @@ -2105,7 +2106,7 @@ server_client_reset_state(struct client *c) { struct tty *tty = &c->tty; struct window *w = active_get_effective_window(c, c->session); - struct window_pane *wp = w->active, *loop; + struct window_pane *wp, *loop; struct screen *s = NULL; struct options *oo = c->session->options; int mode = 0, cursor, flags, pane_mode = 0; @@ -2113,6 +2114,7 @@ server_client_reset_state(struct client *c) u_int sb_w; struct visible_ranges *r; + wp = active_get_effective_pane(c, w); if (c->flags & (CLIENT_CONTROL|CLIENT_SUSPENDED)) return; @@ -3074,6 +3076,7 @@ server_client_remove_pane(struct window_pane *wp) { struct client *c; + active_remove_pane(wp); TAILQ_FOREACH(c, &clients, entry) { if (c->tty.mouse_last_pane == (int)wp->id) { c->tty.mouse_last_pane = -1; @@ -3089,6 +3092,7 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb) { void *data = EVBUFFER_DATA(evb); size_t size = EVBUFFER_LENGTH(evb); + struct window *w; struct window_pane *wp; struct window_mode_entry *wme; char *sanitized, *msg, *line, empty = '\0'; @@ -3127,7 +3131,8 @@ server_client_print(struct client *c, int parse, struct evbuffer *evb) goto out; } - wp = active_get_effective_window(c, c->session)->active; + w = active_get_effective_window(c, c->session); + wp = active_get_effective_pane(c, w); wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) window_pane_set_mode(wp, NULL, &window_view_mode, NULL, NULL, diff --git a/tmux.1 b/tmux.1 index e2eb856a4..c43d4f801 100644 --- a/tmux.1 +++ b/tmux.1 @@ -3981,7 +3981,7 @@ applies the last set layout if possible (undoes the most recent layout change). spreads the current pane and any panes next to it out evenly. .Tg selectp .It Xo Ic select\-pane -.Op Fl DdeLlMmRUZ +.Op Fl ADdeLlMmRsSUZ .Op Fl T Ar title .Op Fl t Ar target\-pane .Xc @@ -3989,6 +3989,8 @@ spreads the current pane and any panes next to it out evenly. Make pane .Ar target\-pane the active pane in its window. +.Fl A +raises the pane if it is floating. If one of .Fl D , .Fl L , @@ -4003,6 +4005,15 @@ keeps the window zoomed if it was zoomed. is the same as using the .Ic last\-pane command. +.Fl s +switches the client to local pane selection for the target window when more +than one client is attached; the pane currently active for the client is +retained. +Local pane selection is disabled automatically when fewer than two clients +remain attached. +.Fl S +switches the client back to shared pane selection for the target window, so the +window's active pane is used. .Fl e enables or .Fl d @@ -7326,6 +7337,8 @@ The following variables are available, where appropriate: .It Li "pane_last" Ta "" Ta "1 if last pane" .It Li "pane_last_output_time" Ta "" Ta "Time pane last produced output" .It Li "pane_last_prompt_time" Ta "" Ta "Time most recent OSC 133 prompt began" +.It Li "pane_local_active" Ta "" Ta "1 if active pane is client-local" +.It Li "pane_local_mode" Ta "" Ta "1 if client has local pane selection" .It Li "pane_left" Ta "" Ta "Left of pane" .It Li "pane_marked" Ta "" Ta "1 if this is the marked pane" .It Li "pane_marked_set" Ta "" Ta "1 if a marked pane is set" diff --git a/tmux.h b/tmux.h index 132699dd6..1fb66a3d7 100644 --- a/tmux.h +++ b/tmux.h @@ -2742,13 +2742,13 @@ void events_fire_pane(const char *, struct window_pane *); void events_fire_winlink(const char *, struct winlink *); /* active.c */ -enum active_window_mode { +enum active_mode { ACTIVE_SHARED, ACTIVE_LOCAL }; int active_is_local_window(struct client *, struct session *); void active_set_local_window(struct client *, struct session *, - enum active_window_mode); + enum active_mode); struct winlink *active_get_effective_winlink(struct client *, struct session *); struct window *active_get_effective_window(struct client *, @@ -2766,6 +2766,16 @@ int active_is_effective_window(struct client *, struct session *, struct winlink *); int active_is_last_window(struct client *, struct session *, struct winlink *); +int active_has_local_pane(struct client *, struct window *); +void active_set_pane_mode(struct client *, struct window *, + enum active_mode); +struct window_pane *active_get_effective_pane(struct client *, + struct window *); +struct window_pane *active_get_last_pane(struct client *, struct window *); +int active_set_pane(struct client *, struct window *, + struct window_pane *, int); +void active_check_clients(void); +void active_remove_pane(struct window_pane *); /* format-draw.c */ void format_draw(struct screen_write_ctx *, const struct grid_cell *, @@ -2982,7 +2992,8 @@ void tty_cmd_sixelimage(struct tty *, const struct tty_ctx *); void tty_draw_images(struct client *, struct window_pane *); #endif void tty_cmd_syncstart(struct tty *, const struct tty_ctx *); -void tty_default_colours(struct grid_cell *, struct window_pane *, u_int *); +void tty_default_colours(struct grid_cell *, struct window_pane *, + struct window_pane *, u_int *); /* tty-term.c */ extern struct tty_terms tty_terms; diff --git a/tty.c b/tty.c index dc45e825c..8b51f6b24 100644 --- a/tty.c +++ b/tty.c @@ -960,9 +960,10 @@ tty_window_offset1(struct tty *tty, u_int *ox, u_int *oy, u_int *sx, u_int *sy) { struct client *c = tty->client; struct window *w = active_get_effective_window(c, c->session); - struct window_pane *wp = w->active; + struct window_pane *wp; u_int cx, cy, lines; + wp = active_get_effective_pane(c, w); lines = status_line_size(c); if (tty->sx >= w->sx && tty->sy - lines >= w->sy) { @@ -3140,23 +3141,24 @@ tty_style_changed(struct window_pane *wp) } void -tty_default_colours(struct grid_cell *gc, struct window_pane *wp, u_int *dim) +tty_default_colours(struct grid_cell *gc, struct window_pane *wp, + struct window_pane *active, u_int *dim) { if (wp->flags & PANE_STYLECHANGED) tty_style_changed (wp); memcpy(gc, &grid_default_cell, sizeof *gc); - if (wp == wp->window->active && wp->cached_active_gc.fg != 8) + if (wp == active && wp->cached_active_gc.fg != 8) gc->fg = wp->cached_active_gc.fg; else gc->fg = wp->cached_gc.fg; - if (wp == wp->window->active && wp->cached_active_gc.bg != 8) + if (wp == active && wp->cached_active_gc.bg != 8) gc->bg = wp->cached_active_gc.bg; else gc->bg = wp->cached_gc.bg; if (dim != NULL) { - if (wp == wp->window->active) + if (wp == active) *dim = wp->cached_active_dim; else *dim = wp->cached_dim; diff --git a/window-border.c b/window-border.c index 1be8c70a5..94a6973f8 100644 --- a/window-border.c +++ b/window-border.c @@ -91,9 +91,14 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c, struct format_tree *ft; const char *option; struct grid_cell *saved; - int *flag; + struct window *w; + struct window_pane *active; + int local, *flag; - if (wp == active_get_effective_window(c, c->session)->active) { + w = active_get_effective_window(c, c->session); + active = active_get_effective_pane(c, w); + local = (wp == active && active_has_local_pane(c, wp->window)); + if (wp == active) { flag = &wp->active_border_gc_set; saved = &wp->active_border_gc; option = "pane-active-border-style"; @@ -103,6 +108,13 @@ window_pane_get_border_style(struct window_pane *wp, struct client *c, option = "pane-border-style"; } + if (local) { + ft = format_create_defaults(NULL, c, s, s->curw, wp); + style_apply(gc, wp->options, option, ft); + format_free(ft); + return; + } + if (!*flag) { ft = format_create_defaults(NULL, c, s, NULL, wp); style_apply(saved, wp->options, option, ft); diff --git a/window.c b/window.c index 97cfd48d8..a68ec0e3f 100644 --- a/window.c +++ b/window.c @@ -795,7 +795,8 @@ window_redraw_active_switch(struct window *w, struct window_pane *wp) break; /* If the pane is floating, move to the front. */ - if (window_pane_is_floating(wp)) { + if (window_pane_is_floating(wp) && + TAILQ_FIRST(&w->z_index) != wp) { TAILQ_REMOVE(&w->z_index, wp, zentry); TAILQ_INSERT_HEAD(&w->z_index, wp, zentry); wp->flags |= PANE_REDRAW; @@ -2578,7 +2579,7 @@ window_pane_get_bg(struct window_pane *wp) c = window_pane_get_bg_control_client(wp); if (c == -1) { - tty_default_colours(&defaults, wp, NULL); + tty_default_colours(&defaults, wp, wp->window->active, NULL); if (COLOUR_DEFAULT(defaults.bg)) c = window_get_bg_client(wp); else