diff --git a/cmd-break-pane.c b/cmd-break-pane.c index 87892d73..9483aa7e 100644 --- a/cmd-break-pane.c +++ b/cmd-break-pane.c @@ -89,6 +89,7 @@ cmd_break_pane_exec(struct cmd *self, struct cmdq_item *item) } TAILQ_REMOVE(&w->panes, wp, entry); + server_client_remove_pane(wp); window_lost_pane(w, wp); layout_close_pane(wp); diff --git a/cmd-find.c b/cmd-find.c index 9c8bcbc1..9f04c4a8 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -588,22 +588,22 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) return (-1); return (0); } else if (strcmp(pane, "{up-of}") == 0) { - fs->wp = window_pane_find_up(fs->w->active); + fs->wp = window_pane_find_up(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{down-of}") == 0) { - fs->wp = window_pane_find_down(fs->w->active); + fs->wp = window_pane_find_down(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{left-of}") == 0) { - fs->wp = window_pane_find_left(fs->w->active); + fs->wp = window_pane_find_left(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); } else if (strcmp(pane, "{right-of}") == 0) { - fs->wp = window_pane_find_right(fs->w->active); + fs->wp = window_pane_find_right(fs->current->wp); if (fs->wp == NULL) return (-1); return (0); @@ -615,7 +615,7 @@ cmd_find_get_pane_with_window(struct cmd_find_state *fs, const char *pane) n = strtonum(pane + 1, 1, INT_MAX, NULL); else n = 1; - wp = fs->w->active; + wp = fs->current->wp; if (pane[0] == '+') fs->wp = window_pane_next_by_number(fs->w, wp, n); else @@ -867,7 +867,18 @@ cmd_find_from_client(struct cmd_find_state *fs, struct client *c, int flags) /* If this is an attached client, all done. */ if (c->session != NULL) { - cmd_find_from_session(fs, c->session, flags); + cmd_find_clear_state(fs, flags); + + fs->wp = server_client_get_pane(c); + if (fs->wp == NULL) { + cmd_find_from_session(fs, c->session, flags); + return (0); + } + fs->s = c->session; + fs->wl = fs->s->curw; + fs->w = fs->wl->window; + + cmd_find_log_state(__func__, fs); return (0); } cmd_find_clear_state(fs, flags); diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 3efe769b..9802083d 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -136,6 +136,7 @@ cmd_join_pane_exec(struct cmd *self, struct cmdq_item *item) layout_close_pane(src_wp); + server_client_remove_pane(src_wp); window_lost_pane(src_w, src_wp); TAILQ_REMOVE(&src_w->panes, src_wp, entry); diff --git a/cmd-kill-pane.c b/cmd-kill-pane.c index 2302d7bb..3bf6e26e 100644 --- a/cmd-kill-pane.c +++ b/cmd-kill-pane.c @@ -54,6 +54,7 @@ cmd_kill_pane_exec(struct cmd *self, struct cmdq_item *item) TAILQ_FOREACH_SAFE(loopwp, &wl->window->panes, entry, tmpwp) { if (loopwp == wp) continue; + server_client_remove_pane(loopwp); layout_close_pane(loopwp); window_remove_pane(wl->window, loopwp); } diff --git a/cmd-queue.c b/cmd-queue.c index 6bc6d0d2..5620fdad 100644 --- a/cmd-queue.c +++ b/cmd-queue.c @@ -809,7 +809,7 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...) } file_print(c, "%s\n", msg); } else { - wp = c->session->curw->window->active; + wp = server_client_get_pane(c); wme = TAILQ_FIRST(&wp->modes); if (wme == NULL || wme->mode != &window_view_mode) { window_pane_set_mode(wp, NULL, &window_view_mode, NULL, diff --git a/cmd-select-pane.c b/cmd-select-pane.c index 224370ab..3b639e06 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -87,10 +87,11 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) const struct cmd_entry *entry = cmd_get_entry(self); struct cmd_find_state *current = cmdq_get_current(item); struct cmd_find_state *target = cmdq_get_target(item); + 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, *markedwp; + struct window_pane *wp = target->wp, *activewp, *lastwp, *markedwp; struct options *oo = wp->options; char *title; const char *style; @@ -201,16 +202,21 @@ cmd_select_pane_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_NORMAL); } - if (wp == w->active) + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + activewp = server_client_get_pane(c); + else + activewp = w->active; + if (wp == activewp) return (CMD_RETURN_NORMAL); if (window_push_zoom(w, args_has(args, 'Z'))) server_redraw_window(w); window_redraw_active_switch(w, wp); - if (window_set_active_pane(w, wp, 1)) { + if (c->session != NULL && (c->flags & CLIENT_ACTIVEPANE)) + server_client_set_pane(c, wp); + else if (window_set_active_pane(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); - } + cmdq_insert_hook(s, item, current, "after-select-pane"); + cmd_select_pane_redraw(w); if (window_pop_zoom(w)) server_redraw_window(w); diff --git a/cmd-split-window.c b/cmd-split-window.c index c0e0e22a..c9d92fae 100644 --- a/cmd-split-window.c +++ b/cmd-split-window.c @@ -160,6 +160,7 @@ cmd_split_window_exec(struct cmd *self, struct cmdq_item *item) return (CMD_RETURN_ERROR); } if (input && window_pane_start_input(new_wp, item, &cause) != 0) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(wp->window, new_wp); cmdq_error(item, "%s", cause); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 021ac224..dd981b9a 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -79,6 +79,9 @@ cmd_swap_pane_exec(struct cmd *self, struct cmdq_item *item) if (src_wp == dst_wp) goto out; + server_client_remove_pane(src_wp); + server_client_remove_pane(dst_wp); + tmp_wp = TAILQ_PREV(dst_wp, window_panes, entry); TAILQ_REMOVE(&dst_w->panes, dst_wp, entry); TAILQ_REPLACE(&src_w->panes, src_wp, dst_wp, entry); diff --git a/screen-redraw.c b/screen-redraw.c index 0f83479c..ffa7aecf 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -242,7 +242,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, struct window_pane **wpp) { struct window *w = c->session->curw->window; - struct window_pane *wp; + struct window_pane *wp, *active; int border; u_int right, line; @@ -254,7 +254,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, return (screen_redraw_type_of_cell(c, px, py, pane_status)); if (pane_status != PANE_STATUS_OFF) { - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next1; @@ -272,10 +272,10 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); } - wp = w->active; + active = wp = server_client_get_pane(c); do { if (!window_pane_visible(wp)) goto next2; @@ -296,7 +296,7 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, int pane_status, wp = TAILQ_NEXT(wp, entry); if (wp == NULL) wp = TAILQ_FIRST(&w->panes); - } while (wp != w->active); + } while (wp != active); return (CELL_OUTSIDE); } @@ -330,7 +330,7 @@ screen_redraw_make_pane_status(struct client *c, struct window *w, ft = format_create(c, NULL, FORMAT_PANE|wp->id, FORMAT_STATUS); format_defaults(ft, c, c->session, c->session->curw, wp); - if (wp == w->active) + if (wp == server_client_get_pane(c)) style_apply(&gc, w->options, "pane-active-border-style", ft); else style_apply(&gc, w->options, "pane-border-style", ft); @@ -558,6 +558,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, struct client *c = ctx->c; struct session *s = c->session; struct window *w = s->curw->window; + struct window_pane *active = server_client_get_pane(c); struct options *oo = w->options; struct grid_cell *gc; struct format_tree *ft; @@ -569,7 +570,7 @@ screen_redraw_draw_borders_style(struct screen_redraw_ctx *ctx, u_int x, ft = format_create_defaults(NULL, c, s, s->curw, wp); gc = &wp->border_gc; - if (screen_redraw_check_is(x, y, ctx->pane_status, w->active)) { + if (screen_redraw_check_is(x, y, ctx->pane_status, active)) { style_apply(gc, oo, "pane-active-border-style", ft); gc->attr |= GRID_ATTR_CHARSET; } else { diff --git a/server-client.c b/server-client.c index 1e0d992d..3ce393cd 100644 --- a/server-client.c +++ b/server-client.c @@ -56,6 +56,19 @@ static void server_client_dispatch_read_data(struct client *, static void server_client_dispatch_read_done(struct client *, struct imsg *); +/* Compare client windows. */ +static int +server_client_window_cmp(struct client_window *cw1, + struct client_window *cw2) +{ + if (cw1->window < cw2->window) + return (-1); + if (cw1->window > cw2->window) + return (1); + return (0); +} +RB_GENERATE(client_windows, client_window, entry, server_client_window_cmp); + /* Number of attached clients. */ u_int server_client_how_many(void) @@ -211,6 +224,7 @@ server_client_create(int fd) c->cwd = NULL; c->queue = cmdq_new(); + RB_INIT(&c->windows); c->tty.fd = -1; c->tty.sx = 80; @@ -272,6 +286,7 @@ void server_client_lost(struct client *c) { struct client_file *cf, *cf1; + struct client_window *cw, *cw1; c->flags |= CLIENT_DEAD; @@ -283,6 +298,10 @@ server_client_lost(struct client *c) cf->error = EINTR; file_fire_done(cf); } + RB_FOREACH_SAFE(cw, client_windows, &c->windows, cw1) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } TAILQ_REMOVE(&clients, c, entry); log_debug("lost client %p", c); @@ -1126,7 +1145,7 @@ server_client_key_callback(struct cmdq_item *item, void *data) /* Find affected pane. */ if (!KEYC_IS_MOUSE(key) || cmd_find_from_mouse(&fs, m, 0) != 0) - cmd_find_from_session(&fs, s, 0); + cmd_find_from_client(&fs, c, 0); wp = fs.wp; /* Forward mouse keys if disabled. */ @@ -1535,7 +1554,7 @@ server_client_reset_state(struct client *c) { struct tty *tty = &c->tty; struct window *w = c->session->curw->window; - struct window_pane *wp = w->active, *loop; + struct window_pane *wp = server_client_get_pane(c), *loop; struct screen *s = NULL; struct options *oo = c->session->options; int mode = 0, cursor, flags; @@ -2236,6 +2255,8 @@ server_client_set_flags(struct client *c, const char *flags) flag = CLIENT_READONLY; else if (strcmp(next, "ignore-size") == 0) flag = CLIENT_IGNORESIZE; + else if (strcmp(next, "active-pane") == 0) + flag = CLIENT_ACTIVEPANE; else continue; @@ -2266,6 +2287,8 @@ server_client_get_flags(struct client *c) strlcat(s, "no-output,", sizeof s); if (c->flags & CLIENT_READONLY) strlcat(s, "read-only,", sizeof s); + if (c->flags & CLIENT_ACTIVEPANE) + strlcat(s, "active-pane,", sizeof s); if (c->flags & CLIENT_SUSPENDED) strlcat(s, "suspended,", sizeof s); if (c->flags & CLIENT_UTF8) @@ -2274,3 +2297,67 @@ server_client_get_flags(struct client *c) s[strlen(s) - 1] = '\0'; return (s); } + +/* Get client window. */ +static struct client_window * +server_client_get_client_window(struct client *c, u_int id) +{ + struct client_window cw = { .window = id }; + + return (RB_FIND(client_windows, &c->windows, &cw)); +} + +/* Get client active pane. */ +struct window_pane * +server_client_get_pane(struct client *c) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return (NULL); + + if (~c->flags & CLIENT_ACTIVEPANE) + return (s->curw->window->active); + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) + return (s->curw->window->active); + return (cw->pane); +} + +/* Set client active pane. */ +void +server_client_set_pane(struct client *c, struct window_pane *wp) +{ + struct session *s = c->session; + struct client_window *cw; + + if (s == NULL) + return; + + cw = server_client_get_client_window(c, s->curw->window->id); + if (cw == NULL) { + cw = xcalloc(1, sizeof *cw); + cw->window = s->curw->window->id; + RB_INSERT(client_windows, &c->windows, cw); + } + cw->pane = wp; + log_debug("%s pane now %%%u", c->name, wp->id); +} + +/* Remove pane from client lists. */ +void +server_client_remove_pane(struct window_pane *wp) +{ + struct client *c; + struct window *w = wp->window; + struct client_window *cw; + + TAILQ_FOREACH(c, &clients, entry) { + cw = server_client_get_client_window(c, w->id); + if (cw != NULL && cw->pane == wp) { + RB_REMOVE(client_windows, &c->windows, cw); + free(cw); + } + } +} diff --git a/server-fn.c b/server-fn.c index fde1d8e8..d66aed0b 100644 --- a/server-fn.c +++ b/server-fn.c @@ -187,6 +187,7 @@ server_kill_pane(struct window_pane *wp) recalculate_sizes(); } else { server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); server_redraw_window(w); @@ -348,6 +349,7 @@ server_destroy_pane(struct window_pane *wp, int notify) notify_pane("pane-exited", wp); server_unzoom_window(w); + server_client_remove_pane(wp); layout_close_pane(wp); window_remove_pane(w, wp); diff --git a/spawn.c b/spawn.c index f05887b2..01f19097 100644 --- a/spawn.c +++ b/spawn.c @@ -362,6 +362,7 @@ spawn_pane(struct spawn_context *sc, char **cause) xasprintf(cause, "fork failed: %s", strerror(errno)); new_wp->fd = -1; if (~sc->flags & SPAWN_RESPAWN) { + server_client_remove_pane(new_wp); layout_close_pane(new_wp); window_remove_pane(w, new_wp); } diff --git a/tmux.1 b/tmux.1 index baa296fb..0b03becc 100644 --- a/tmux.1 +++ b/tmux.1 @@ -986,6 +986,8 @@ the client is read-only the client does not affect the size of other clients .It no-output the client does not receive pane output in control mode +.It active-pane +the client has an independent active pane .El .Pp A leading @@ -1000,6 +1002,13 @@ When a client is read-only, only keys bound to the or .Ic switch-client commands have any effect. +A client with the +.Ar active-pane +flag allows the active pane to be selected independently of the window's active +pane used by clients without the flag. +This only affects the cursor position and commands issued from the client; +other features such as hooks and styles continue to use the window's active +pane. .Pp If no server is started, .Ic attach-session diff --git a/tmux.h b/tmux.h index 3ccb005a..3bd37f05 100644 --- a/tmux.h +++ b/tmux.h @@ -1508,6 +1508,14 @@ struct client_file { }; RB_HEAD(client_files, client_file); +/* Client window. */ +struct client_window { + u_int window; + struct window_pane *pane; + RB_ENTRY(client_window) entry; +}; +RB_HEAD(client_windows, client_window); + /* Client connection. */ typedef int (*prompt_input_cb)(struct client *, void *, const char *, int); typedef void (*prompt_free_cb)(void *); @@ -1521,6 +1529,8 @@ struct client { struct tmuxpeer *peer; struct cmdq_list *queue; + struct client_windows windows; + pid_t pid; int fd; struct event event; @@ -1585,6 +1595,7 @@ struct client { #define CLIENT_STARTSERVER 0x10000000 #define CLIENT_REDRAWPANES 0x20000000 #define CLIENT_NOFORK 0x40000000 +#define CLIENT_ACTIVEPANE 0x80000000ULL #define CLIENT_ALLREDRAWFLAGS \ (CLIENT_REDRAWWINDOW| \ CLIENT_REDRAWSTATUS| \ @@ -1600,7 +1611,7 @@ struct client { (CLIENT_DEAD| \ CLIENT_SUSPENDED| \ CLIENT_DETACHING) - int flags; + uint64_t flags; struct key_table *keytable; uint64_t redraw_panes; @@ -2299,6 +2310,7 @@ void server_add_accept(int); void printflike(1, 2) server_add_message(const char *, ...); /* server-client.c */ +RB_PROTOTYPE(client_windows, client_window, entry, server_client_window_cmp); u_int server_client_how_many(void); void server_client_set_overlay(struct client *, u_int, overlay_check_cb, overlay_mode_cb, overlay_draw_cb, overlay_key_cb, @@ -2321,6 +2333,9 @@ void server_client_push_stderr(struct client *); const char *server_client_get_cwd(struct client *, struct session *); void server_client_set_flags(struct client *, const char *); const char *server_client_get_flags(struct client *); +struct window_pane *server_client_get_pane(struct client *); +void server_client_set_pane(struct client *, struct window_pane *); +void server_client_remove_pane(struct window_pane *); /* server-fn.c */ void server_redraw_client(struct client *); diff --git a/tty.c b/tty.c index a1e2f2c6..a4f2acee 100644 --- a/tty.c +++ b/tty.c @@ -693,8 +693,7 @@ tty_update_mode(struct tty *tty, int mode, struct screen *s) } if (s != NULL && tty->cstyle != s->cstyle) { if (tty_term_has(tty->term, TTYC_SS)) { - if (s->cstyle == 0 && - tty_term_has(tty->term, TTYC_SE)) + if (s->cstyle == 0 && tty_term_has(tty->term, TTYC_SE)) tty_putcode(tty, TTYC_SE); else tty_putcode1(tty, TTYC_SS, s->cstyle); @@ -792,7 +791,7 @@ 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 = c->session->curw->window; - struct window_pane *wp = w->active; + struct window_pane *wp = server_client_get_pane(c); u_int cx, cy, lines; lines = status_line_size(c);