diff --git a/cmd-find.c b/cmd-find.c index 98f1e187..2b862f95 100644 --- a/cmd-find.c +++ b/cmd-find.c @@ -29,6 +29,7 @@ #define CMD_FIND_PREFER_UNATTACHED 0x1 #define CMD_FIND_QUIET 0x2 #define CMD_FIND_WINDOW_INDEX 0x4 +#define CMD_FIND_DEFAULT_MARKED 0x8 enum cmd_find_type { CMD_FIND_PANE, @@ -759,7 +760,14 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, /* Find current state. */ cmd_find_clear_state(¤t, cmdq, flags); - if (cmd_find_current_session(¤t) != 0) { + if (server_check_marked() && (flags & CMD_FIND_DEFAULT_MARKED)) { + current.s = marked_session; + current.wl = marked_winlink; + current.idx = current.wl->idx; + current.w = current.wl->window; + current.wp = marked_window_pane; + } + if (current.s == NULL && cmd_find_current_session(¤t) != 0) { if (~flags & CMD_FIND_QUIET) cmdq_error(cmdq, "no current session"); goto error; @@ -798,9 +806,24 @@ cmd_find_target(struct cmd_q *cmdq, const char *target, enum cmd_find_type type, } return (&fs); } - copy = xstrdup(target); + + /* Marked target is a plain ~ or {marked}. */ + if (strcmp(target, "~") == 0 || strcmp(target, "{marked}") == 0) { + if (!server_check_marked()) { + if (~flags & CMD_FIND_QUIET) + cmdq_error(cmdq, "no marked target"); + goto error; + } + fs.s = marked_session; + fs.wl = marked_winlink; + fs.idx = fs.wl->idx; + fs.w = fs.wl->window; + fs.wp = marked_window_pane; + return (&fs); + } /* Find separators if they exist. */ + copy = xstrdup(target); colon = strchr(copy, ':'); if (colon != NULL) *colon++ = '\0'; @@ -1053,6 +1076,24 @@ cmd_find_window(struct cmd_q *cmdq, const char *target, struct session **sp) return (fs->wl); } +/* Find the target window, defaulting to marked rather than current. */ +struct winlink * +cmd_find_window_marked(struct cmd_q *cmdq, const char *target, + struct session **sp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_WINDOW, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + return (fs->wl); +} + /* Find the target pane and report an error and return NULL. */ struct winlink * cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, @@ -1072,6 +1113,26 @@ cmd_find_pane(struct cmd_q *cmdq, const char *target, struct session **sp, return (fs->wl); } +/* Find the target pane, defaulting to marked rather than current. */ +struct winlink * +cmd_find_pane_marked(struct cmd_q *cmdq, const char *target, + struct session **sp, struct window_pane **wpp) +{ + struct cmd_find_state *fs; + int flags = CMD_FIND_DEFAULT_MARKED; + + fs = cmd_find_target(cmdq, target, CMD_FIND_PANE, flags); + cmd_find_log_state(__func__, target, fs); + if (fs == NULL) + return (NULL); + + if (sp != NULL) + *sp = fs->s; + if (wpp != NULL) + *wpp = fs->wp; + return (fs->wl); +} + /* Find the target client or report an error and return NULL. */ struct client * cmd_find_client(struct cmd_q *cmdq, const char *target, int quiet) diff --git a/cmd-join-pane.c b/cmd-join-pane.c index 943cdce4..b995b674 100644 --- a/cmd-join-pane.c +++ b/cmd-join-pane.c @@ -75,7 +75,7 @@ join_pane(struct cmd *self, struct cmd_q *cmdq, int not_same_window) dst_idx = dst_wl->idx; server_unzoom_window(dst_w); - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-select-pane.c b/cmd-select-pane.c index f237e8fd..5ea4bdc3 100644 --- a/cmd-select-pane.c +++ b/cmd-select-pane.c @@ -28,8 +28,8 @@ enum cmd_retval cmd_select_pane_exec(struct cmd *, struct cmd_q *); const struct cmd_entry cmd_select_pane_entry = { "select-pane", "selectp", - "DdegLlP:Rt:U", 0, 0, - "[-DdegLlRU] [-P style] " CMD_TARGET_PANE_USAGE, + "DdegLlMmP:Rt:U", 0, 0, + "[-DdegLlMmRU] [-P style] " CMD_TARGET_PANE_USAGE, 0, cmd_select_pane_exec }; @@ -47,7 +47,8 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) { struct args *args = self->args; struct winlink *wl; - struct window_pane *wp; + struct session *s; + struct window_pane *wp, *lastwp, *markedwp; const char *style; if (self->entry == &cmd_last_pane_entry || args_has(args, 'l')) { @@ -74,9 +75,31 @@ cmd_select_pane_exec(struct cmd *self, struct cmd_q *cmdq) return (CMD_RETURN_NORMAL); } - if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), NULL, &wp)) == NULL) + if ((wl = cmd_find_pane(cmdq, args_get(args, 't'), &s, &wp)) == NULL) return (CMD_RETURN_ERROR); + if (args_has(args, 'm') || args_has(args, 'M')) { + if (args_has(args, 'm') && !window_pane_visible(wp)) + return (CMD_RETURN_NORMAL); + lastwp = marked_window_pane; + + if (args_has(args, 'M') || server_is_marked(s, wl, wp)) + server_clear_marked(); + else + server_set_marked(s, wl, wp); + markedwp = marked_window_pane; + + if (lastwp != NULL) { + server_redraw_window_borders(lastwp->window); + server_status_window(lastwp->window); + } + if (markedwp != NULL) { + server_redraw_window_borders(markedwp->window); + server_status_window(markedwp->window); + } + return (CMD_RETURN_NORMAL); + } + if (args_has(self->args, 'P') || args_has(self->args, 'g')) { if (args_has(args, 'P')) { style = args_get(args, 'P'); diff --git a/cmd-swap-pane.c b/cmd-swap-pane.c index 918a2e4f..c4751c36 100644 --- a/cmd-swap-pane.c +++ b/cmd-swap-pane.c @@ -58,18 +58,22 @@ cmd_swap_pane_exec(struct cmd *self, struct cmd_q *cmdq) src_wp = TAILQ_NEXT(dst_wp, entry); if (src_wp == NULL) src_wp = TAILQ_FIRST(&dst_w->panes); + src_wl = dst_wl; } else if (args_has(self->args, 'U')) { src_wp = TAILQ_PREV(dst_wp, window_panes, entry); if (src_wp == NULL) src_wp = TAILQ_LAST(&dst_w->panes, window_panes); + src_wl = dst_wl; } else { - src_wl = cmd_find_pane(cmdq, NULL, NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, NULL, NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; } } else { - src_wl = cmd_find_pane(cmdq, args_get(args, 's'), NULL, &src_wp); + src_wl = cmd_find_pane_marked(cmdq, args_get(args, 's'), NULL, + &src_wp); if (src_wl == NULL) return (CMD_RETURN_ERROR); src_w = src_wl->window; diff --git a/cmd-swap-window.c b/cmd-swap-window.c index 655b910c..51682e37 100644 --- a/cmd-swap-window.c +++ b/cmd-swap-window.c @@ -47,7 +47,7 @@ cmd_swap_window_exec(struct cmd *self, struct cmd_q *cmdq) struct window *w; target_src = args_get(args, 's'); - if ((wl_src = cmd_find_window(cmdq, target_src, &src)) == NULL) + if ((wl_src = cmd_find_window_marked(cmdq, target_src, &src)) == NULL) return (CMD_RETURN_ERROR); target_dst = args_get(args, 't'); if ((wl_dst = cmd_find_window(cmdq, target_dst, &dst)) == NULL) diff --git a/key-bindings.c b/key-bindings.c index a3cb0bea..ab4751f9 100644 --- a/key-bindings.c +++ b/key-bindings.c @@ -177,6 +177,7 @@ key_bindings_init(void) "bind ? list-keys", "bind D choose-client", "bind L switch-client -l", + "bind M select-pane -M", "bind [ copy-mode", "bind ] paste-buffer", "bind c new-window", @@ -184,6 +185,7 @@ key_bindings_init(void) "bind f command-prompt \"find-window '%%'\"", "bind i display-message", "bind l last-window", + "bind m select-pane -m", "bind n next-window", "bind o select-pane -t:.+", "bind p previous-window", @@ -222,6 +224,7 @@ key_bindings_init(void) "bind -n MouseDrag1Border resize-pane -M", "bind -n MouseDown1Status select-window -t=", "bind -n MouseDrag1Pane if -Ft= '#{mouse_any_flag}' 'if -Ft= \"#{pane_in_mode}\" \"copy-mode -M\" \"send-keys -M\"' 'copy-mode -M'", + "bind -n MouseDown3Pane select-pane -mt=", }; u_int i; struct cmd_list *cmdlist; diff --git a/screen-redraw.c b/screen-redraw.c index babc74a9..799f5c55 100644 --- a/screen-redraw.c +++ b/screen-redraw.c @@ -26,8 +26,8 @@ int screen_redraw_cell_border1(struct window_pane *, u_int, u_int); int screen_redraw_cell_border(struct client *, u_int, u_int); int screen_redraw_check_cell(struct client *, u_int, u_int, struct window_pane **); -int screen_redraw_check_active(u_int, u_int, int, struct window *, - struct window_pane *); +int screen_redraw_check_is(u_int, u_int, int, struct window *, + struct window_pane *, struct window_pane *); void screen_redraw_draw_borders(struct client *, int, u_int); void screen_redraw_draw_panes(struct client *, u_int); @@ -175,13 +175,13 @@ screen_redraw_check_cell(struct client *c, u_int px, u_int py, return (CELL_OUTSIDE); } -/* Check active pane indicator. */ +/* Check if the border of a particular pane. */ int -screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, - struct window_pane *wp) +screen_redraw_check_is(u_int px, u_int py, int type, struct window *w, + struct window_pane *wantwp, struct window_pane *wp) { /* Is this off the active pane border? */ - if (screen_redraw_cell_border1(w->active, px, py) != 1) + if (screen_redraw_cell_border1(wantwp, px, py) != 1) return (0); /* If there are more than two panes, that's enough. */ @@ -196,7 +196,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->xoff == 0 && wp->sx == w->sx) { /* This can either be the top pane or the bottom pane. */ if (wp->yoff == 0) { /* top pane */ - if (wp == w->active) + if (wp == wantwp) return (px <= wp->sx / 2); return (px > wp->sx / 2); } @@ -207,7 +207,7 @@ screen_redraw_check_active(u_int px, u_int py, int type, struct window *w, if (wp->yoff == 0 && wp->sy == w->sy) { /* This can either be the left pane or the right pane. */ if (wp->xoff == 0) { /* left pane */ - if (wp == w->active) + if (wp == wantwp) return (py <= wp->sy / 2); return (py > wp->sy / 2); } @@ -274,13 +274,15 @@ screen_redraw_pane(struct client *c, struct window_pane *wp) void screen_redraw_draw_borders(struct client *c, int status, u_int top) { - struct window *w = c->session->curw->window; + struct session *s = c->session; + struct window *w = s->curw->window; struct options *oo = &w->options; struct tty *tty = &c->tty; struct window_pane *wp; - struct grid_cell active_gc, other_gc, msg_gc; + struct grid_cell m_active_gc, active_gc, m_other_gc, other_gc; + struct grid_cell msg_gc; u_int i, j, type, msgx = 0, msgy = 0; - int small, flags; + int active, small, flags; char msg[256]; const char *tmp; size_t msglen = 0; @@ -314,15 +316,29 @@ screen_redraw_draw_borders(struct client *c, int status, u_int top) style_apply(&active_gc, oo, "pane-active-border-style"); active_gc.attr = other_gc.attr = GRID_ATTR_CHARSET; + memcpy(&m_other_gc, &other_gc, sizeof m_other_gc); + m_other_gc.attr ^= GRID_ATTR_REVERSE; + memcpy(&m_active_gc, &active_gc, sizeof m_active_gc); + m_active_gc.attr ^= GRID_ATTR_REVERSE; + for (j = 0; j < tty->sy - status; j++) { for (i = 0; i < tty->sx; i++) { type = screen_redraw_check_cell(c, i, j, &wp); if (type == CELL_INSIDE) continue; - if (type == CELL_OUTSIDE && - small && i > msgx && j == msgy) + if (type == CELL_OUTSIDE && small && + i > msgx && j == msgy) continue; - if (screen_redraw_check_active(i, j, type, w, wp)) + active = screen_redraw_check_is(i, j, type, w, + w->active, wp); + if (server_is_marked(s, s->curw, marked_window_pane) && + screen_redraw_check_is(i, j, type, w, + marked_window_pane, wp)) { + if (active) + tty_attributes(tty, &m_active_gc, NULL); + else + tty_attributes(tty, &m_other_gc, NULL); + } else if (active) tty_attributes(tty, &active_gc, NULL); else tty_attributes(tty, &other_gc, NULL); diff --git a/server.c b/server.c index b53648f0..52fdb0c3 100644 --- a/server.c +++ b/server.c @@ -50,19 +50,85 @@ int server_shutdown; struct event server_ev_accept; struct event server_ev_second; -int server_create_socket(void); -void server_loop(void); -int server_should_shutdown(void); -void server_send_shutdown(void); -void server_clean_dead(void); -void server_accept_callback(int, short, void *); -void server_signal_callback(int, short, void *); -void server_child_signal(void); -void server_child_exited(pid_t, int); -void server_child_stopped(pid_t, int); -void server_second_callback(int, short, void *); -void server_lock_server(void); -void server_lock_sessions(void); +struct session *marked_session; +struct winlink *marked_winlink; +struct window *marked_window; +struct window_pane *marked_window_pane; +struct layout_cell *marked_layout_cell; + +int server_create_socket(void); +void server_loop(void); +int server_should_shutdown(void); +void server_send_shutdown(void); +void server_clean_dead(void); +void server_accept_callback(int, short, void *); +void server_signal_callback(int, short, void *); +void server_child_signal(void); +void server_child_exited(pid_t, int); +void server_child_stopped(pid_t, int); +void server_second_callback(int, short, void *); +void server_lock_server(void); +void server_lock_sessions(void); + +/* Set marked pane. */ +void +server_set_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + marked_session = s; + marked_winlink = wl; + marked_window = wl->window; + marked_window_pane = wp; + marked_layout_cell = wp->layout_cell; +} + +/* Clear marked pane. */ +void +server_clear_marked(void) +{ + marked_session = NULL; + marked_winlink = NULL; + marked_window = NULL; + marked_window_pane = NULL; + marked_layout_cell = NULL; +} + +/* Is this the marked pane? */ +int +server_is_marked(struct session *s, struct winlink *wl, struct window_pane *wp) +{ + if (s == NULL || wl == NULL || wp == NULL) + return (0); + if (marked_session != s || marked_winlink != wl) + return (0); + if (marked_window_pane != wp) + return (0); + return (server_check_marked()); +} + +/* Check if the marked pane is still valid. */ +int +server_check_marked(void) +{ + struct winlink *wl; + + if (marked_window_pane == NULL) + return (0); + if (marked_layout_cell != marked_window_pane->layout_cell) + return (0); + + if (!session_alive(marked_session)) + return (0); + RB_FOREACH(wl, winlinks, &marked_session->windows) { + if (wl->window == marked_window && wl == marked_winlink) + break; + } + if (wl == NULL) + return (0); + + if (!window_has_pane(marked_window, marked_window_pane)) + return (0); + return (window_pane_visible(marked_window_pane)); +} /* Create server socket. */ int diff --git a/tmux.1 b/tmux.1 index 1233d1be..015e5ee7 100644 --- a/tmux.1 +++ b/tmux.1 @@ -298,6 +298,12 @@ Change to the previous window. Briefly display pane indexes. .It r Force redraw of the attached client. +.It m +Mark the current pane (see +.Ic select-pane +.Fl m ) . +.It M +Clear the marked pane. .It s Select a new session for the attached client interactively. .It t @@ -351,6 +357,8 @@ This section contains a list of the commands supported by .Nm . Most commands accept the optional .Fl t +(and sometimes +.Fl s ) argument with one of .Ar target-client , .Ar target-session @@ -451,7 +459,6 @@ Each has a single-character alternative form. .It Li "{last}" Ta "!" Ta "The last (previously current) window" .It Li "{next}" Ta "+" Ta "The next window by number" .It Li "{previous}" Ta "-" Ta "The previous window by number" -.It Li "{mouse}" Ta "=" Ta "The window where the mouse event happened" .El .Pp .Ar target-pane @@ -481,7 +488,6 @@ The following special tokens are available for the pane index: .It Li "{down}" Ta "" Ta "The pane below the active pane" .It Li "{left}" Ta "" Ta "The pane to the left of the active pane" .It Li "{right}" Ta "" Ta "The pane to the right of the active pane" -.It Li "{mouse}" Ta "=" Ta "The pane where the mouse event happened" .El .Pp The tokens @@ -493,6 +499,27 @@ may be followed by an offset, for example: select-window -t:+2 .Ed .Pp +In addition, +.Em target-session , +.Em target-window +or +.Em target-pane +may consist entirely of the token +.Ql {mouse} +(alternative form +.Ql = ) +to specify the most recent mouse event +(see the +.Sx MOUSE SUPPORT +section) +or +.Ql {marked} +(alternative form +.Ql ~ ) +to specify the marked pane (see +.Ic select-pane +.Fl m ) . +.Pp Sessions, window and panes are each numbered with a unique ID; session IDs are prefixed with a .Ql $ , @@ -1440,6 +1467,13 @@ option causes .Ar src-pane to be joined to left of or above .Ar dst-pane . +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic kill-pane .Op Fl a .Op Fl t Ar target-pane @@ -1795,7 +1829,7 @@ commands. .Fl o applies the last set layout if possible (undoes the most recent layout change). .It Xo Ic select-pane -.Op Fl DdegLlRU +.Op Fl DdegLlMmRU .Op Fl P Ar style .Op Fl t Ar target-pane .Xc @@ -1823,6 +1857,20 @@ enables or .Fl d disables input to the pane. .Pp +.Fl m +and +.Fl M +are used to set and clear the +.Em marked pane . +There is one marked pane at a time, setting a new marked pane clears the last. +The marked pane is the default target for +.Fl s +to +.Ic join-pane , +.Ic swap-pane +and +.Ic swap-window . +.Pp Each pane has a style: by default the .Ic window-style and @@ -1911,6 +1959,13 @@ swaps with the next pane (after it numerically). instructs .Nm not to change the active pane. +.Pp +If +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the marked pane is used rather than the current pane. .It Xo Ic swap-window .Op Fl d .Op Fl s Ar src-window @@ -1922,6 +1977,15 @@ This is similar to except the source and destination windows are swapped. It is an error if no window exists at .Ar src-window . +.Pp +Like +.Ic swap-pane , +if +.Fl s +is omitted and a marked pane is present (see +.Ic select-pane +.Fl m ) , +the window containing the marked pane is used rather than the current window. .It Xo Ic unlink-window .Op Fl k .Op Fl t Ar target-window @@ -3488,6 +3552,7 @@ The flag is one of the following symbols appended to the window name: .It Li "#" Ta "Window is monitored and activity has been detected." .It Li "!" Ta "A bell has occurred in the window." .It Li "~" Ta "The window has been silent for the monitor-silence interval." +.It Li "M" Ta "The window contains the marked pane." .It Li "Z" Ta "The window's active pane is zoomed." .El .Pp diff --git a/tmux.h b/tmux.h index 78e64170..ae1682af 100644 --- a/tmux.h +++ b/tmux.h @@ -1688,8 +1688,12 @@ struct session *cmd_find_current(struct cmd_q *); struct session *cmd_find_session(struct cmd_q *, const char *, int); struct winlink *cmd_find_window(struct cmd_q *, const char *, struct session **); +struct winlink *cmd_find_window_marked(struct cmd_q *, const char *, + struct session **); struct winlink *cmd_find_pane(struct cmd_q *, const char *, struct session **, struct window_pane **); +struct winlink *cmd_find_pane_marked(struct cmd_q *, const char *, + struct session **, struct window_pane **); struct client *cmd_find_client(struct cmd_q *, const char *, int); int cmd_find_index(struct cmd_q *, const char *, struct session **); @@ -1850,6 +1854,15 @@ const char *key_string_lookup_key(int); /* server.c */ extern struct clients clients; extern struct clients dead_clients; +extern struct session *marked_session; +extern struct winlink *marked_winlink; +extern struct window_pane *marked_window_pane; +void server_set_marked(struct session *, struct winlink *, + struct window_pane *); +void server_clear_marked(void); +int server_is_marked(struct session *, struct winlink *, + struct window_pane *); +int server_check_marked(void); int server_start(int, char *); void server_update_socket(void); void server_add_accept(int); diff --git a/window.c b/window.c index bc17656d..a1d6792b 100644 --- a/window.c +++ b/window.c @@ -543,6 +543,9 @@ window_add_pane(struct window *w, u_int hlimit) void window_lost_pane(struct window *w, struct window_pane *wp) { + if (wp == marked_window_pane) + server_clear_marked(); + if (wp == w->active) { w->active = w->last; w->last = NULL; @@ -661,6 +664,8 @@ window_printable_flags(struct session *s, struct winlink *wl) flags[pos++] = '*'; if (wl == TAILQ_FIRST(&s->lastw)) flags[pos++] = '-'; + if (server_check_marked() && wl == marked_winlink) + flags[pos++] = 'M'; if (wl->window->flags & WINDOW_ZOOMED) flags[pos++] = 'Z'; flags[pos] = '\0';