Allow multiple modes to be open in a pane. A stack of open modes is kept

and the previous restored when the top is exited. If a mode that is
already on the stack is entered, the existing instance is moved to the
top as the active mode rather than being opened new.
pull/1628/head
nicm 2019-03-12 11:16:49 +00:00
parent ff4c80d53d
commit 3f6bfbaf2b
15 changed files with 182 additions and 116 deletions

10
cfg.c
View File

@ -341,17 +341,17 @@ cfg_print_causes(struct cmdq_item *item)
void
cfg_show_causes(struct session *s)
{
struct window_pane *wp;
u_int i;
struct window_pane *wp;
struct window_mode_entry *wme;
u_int i;
if (s == NULL || cfg_ncauses == 0)
return;
wp = s->curw->window->active;
if (wp->mode == NULL || wp->mode->mode != &window_view_mode) {
window_pane_reset_mode(wp);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
}
for (i = 0; i < cfg_ncauses; i++) {
window_copy_add(wp, "%s", cfg_causes[i]);
free(cfg_causes[i]);

View File

@ -199,8 +199,7 @@ cmd_capture_pane_exec(struct cmd *self, struct cmdq_item *item)
size_t len;
if (self->entry == &cmd_clear_history_entry) {
if (wp->mode != NULL && wp->mode->mode == &window_copy_mode)
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
grid_clear_history(wp->base.grid);
return (CMD_RETURN_NORMAL);
}

View File

@ -60,7 +60,6 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
struct client *c = item->client;
struct session *s;
struct window_pane *wp = item->target.wp;
int flag;
if (args_has(args, 'M')) {
if ((wp = cmd_mouse_pane(&shared->mouse, &s, NULL)) == NULL)
@ -74,16 +73,10 @@ cmd_copy_mode_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_NORMAL);
}
if (wp->mode == NULL || wp->mode->mode != &window_copy_mode) {
flag = window_pane_set_mode(wp, &window_copy_mode, NULL, args);
if (flag != 0)
return (CMD_RETURN_NORMAL);
}
if (args_has(args, 'M')) {
if (wp->mode != NULL && wp->mode->mode != &window_copy_mode)
return (CMD_RETURN_NORMAL);
if (window_pane_set_mode(wp, &window_copy_mode, NULL, args) != 0)
return (CMD_RETURN_NORMAL);
if (args_has(args, 'M'))
window_copy_start_drag(c, &shared->mouse);
}
if (args_has(self->args, 'u'))
window_copy_pageup(wp, 0);

View File

@ -404,10 +404,11 @@ cmdq_guard(struct cmdq_item *item, const char *guard, int flags)
void
cmdq_print(struct cmdq_item *item, const char *fmt, ...)
{
struct client *c = item->client;
struct window_pane *wp;
va_list ap;
char *tmp, *msg;
struct client *c = item->client;
struct window_pane *wp;
struct window_mode_entry *wme;
va_list ap;
char *tmp, *msg;
va_start(ap, fmt);
@ -426,10 +427,9 @@ cmdq_print(struct cmdq_item *item, const char *fmt, ...)
server_client_push_stdout(c);
} else {
wp = c->session->curw->window->active;
if (wp->mode == NULL || wp->mode->mode != &window_view_mode) {
window_pane_reset_mode(wp);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
}
window_copy_vadd(wp, fmt, ap);
}

View File

@ -67,7 +67,7 @@ cmd_respawn_pane_exec(struct cmd *self, struct cmdq_item *item)
return (CMD_RETURN_ERROR);
}
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);

View File

@ -99,7 +99,7 @@ cmd_respawn_window_exec(struct cmd *self, struct cmdq_item *item)
free(cwd);
layout_init(w, wp);
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
screen_reinit(&wp->base);
input_init(wp);
window_set_active_pane(w, wp);

View File

@ -60,6 +60,7 @@ cmd_run_shell_print(struct job *job, const char *msg)
struct cmd_run_shell_data *cdata = job_get_data(job);
struct window_pane *wp = NULL;
struct cmd_find_state fs;
struct window_mode_entry *wme;
if (cdata->wp_id != -1)
wp = window_pane_find_by_id(cdata->wp_id);
@ -75,10 +76,9 @@ cmd_run_shell_print(struct job *job, const char *msg)
return;
}
if (wp->mode == NULL || wp->mode->mode != &window_view_mode) {
window_pane_reset_mode(wp);
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_view_mode)
window_pane_set_mode(wp, &window_view_mode, NULL, NULL);
}
window_copy_add(wp, "%s", msg);
}

View File

@ -61,10 +61,11 @@ cmd_send_keys_inject(struct client *c, struct cmdq_item *item, key_code key)
struct window_pane *wp = item->target.wp;
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct window_mode_entry *wme = wp->mode;
struct window_mode_entry *wme;
struct key_table *table;
struct key_binding *bd;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode->key_table == NULL) {
if (options_get_number(wp->window->options, "xterm-keys"))
key |= KEYC_XTERM;
@ -90,7 +91,7 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
struct session *s = item->target.s;
struct winlink *wl = item->target.wl;
struct mouse_event *m = &item->shared->mouse;
struct window_mode_entry *wme = wp->mode;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct utf8_data *ud, *uc;
wchar_t wc;
int i, literal;
@ -105,8 +106,13 @@ cmd_send_keys_exec(struct cmd *self, struct cmdq_item *item)
free(cause);
return (CMD_RETURN_ERROR);
}
if (wme != NULL && (args_has(args, 'X') || args->argc == 0))
if (wme != NULL && (args_has(args, 'X') || args->argc == 0)) {
if (wme == NULL || wme->mode->command == NULL) {
cmdq_error(item, "not in a mode");
return (CMD_RETURN_ERROR);
}
wme->prefix = np;
}
}
if (args_has(args, 'X')) {

View File

@ -617,6 +617,22 @@ format_cb_session_group_list(struct format_tree *ft, struct format_entry *fe)
evbuffer_free(buffer);
}
/* Callback for pane_in_mode. */
static void
format_cb_pane_in_mode(struct format_tree *ft, struct format_entry *fe)
{
struct window_pane *wp = ft->wp;
u_int n = 0;
struct window_mode_entry *wme;
if (wp == NULL)
return;
TAILQ_FOREACH(wme, &wp->modes, entry)
n++;
xasprintf(&fe->value, "%u", n);
}
/* Merge a format tree. */
static void
format_merge(struct format_tree *ft, struct format_tree *from)
@ -1495,10 +1511,11 @@ format_defaults_winlink(struct format_tree *ft, struct winlink *wl)
void
format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
{
struct window *w = wp->window;
struct grid *gd = wp->base.grid;
int status = wp->status;
u_int idx;
struct window *w = wp->window;
struct grid *gd = wp->base.grid;
int status = wp->status;
u_int idx;
struct window_mode_entry *wme;
if (ft->w == NULL)
ft->w = w;
@ -1533,9 +1550,13 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "pane_at_right", "%d", wp->xoff + wp->sx == w->sx);
format_add(ft, "pane_at_bottom", "%d", wp->yoff + wp->sy == w->sy);
format_add(ft, "pane_in_mode", "%d", wp->screen != &wp->base);
if (wp->mode != NULL)
format_add(ft, "pane_mode", "%s", wp->mode->mode->name);
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) {
format_add(ft, "pane_mode", "%s", wme->mode->name);
if (wme->mode->formats != NULL)
wme->mode->formats(wme, ft);
}
format_add_cb(ft, "pane_in_mode", format_cb_pane_in_mode);
format_add(ft, "pane_synchronized", "%d",
!!options_get_number(w->options, "synchronize-panes"));
@ -1552,9 +1573,6 @@ format_defaults_pane(struct format_tree *ft, struct window_pane *wp)
format_add(ft, "scroll_region_upper", "%u", wp->base.rupper);
format_add(ft, "scroll_region_lower", "%u", wp->base.rlower);
if (wp->mode != NULL && wp->mode->mode->formats != NULL)
wp->mode->mode->formats(wp->mode, ft);
format_add(ft, "alternate_on", "%d", wp->saved_grid ? 1 : 0);
format_add(ft, "alternate_saved_x", "%u", wp->saved_cx);
format_add(ft, "alternate_saved_y", "%u", wp->saved_cy);

View File

@ -805,7 +805,7 @@ input_reset(struct window_pane *wp, int clear)
input_reset_cell(ictx);
if (clear) {
if (wp->mode == NULL)
if (TAILQ_EMPTY(&wp->modes))
screen_write_start(&ictx->ctx, wp, &wp->base);
else
screen_write_start(&ictx->ctx, NULL, &wp->base);
@ -861,7 +861,7 @@ input_parse(struct window_pane *wp)
* Open the screen. Use NULL wp if there is a mode set as don't want to
* update the tty.
*/
if (wp->mode == NULL)
if (TAILQ_EMPTY(&wp->modes))
screen_write_start(&ictx->ctx, wp, &wp->base);
else
screen_write_start(&ictx->ctx, NULL, &wp->base);

View File

@ -921,18 +921,18 @@ server_client_assume_paste(struct session *s)
void
server_client_handle_key(struct client *c, key_code key)
{
struct mouse_event *m = &c->tty.mouse;
struct session *s = c->session;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct timeval tv;
struct key_table *table, *first;
const char *tablename;
struct key_binding *bd;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
struct mouse_event *m = &c->tty.mouse;
struct session *s = c->session;
struct winlink *wl;
struct window *w;
struct window_pane *wp;
struct window_mode_entry *wme;
struct timeval tv;
struct key_table *table, *first;
struct key_binding *bd;
int xtimeout, flags;
struct cmd_find_state fs;
key_code key0;
/* Check the client is good to accept input. */
if (s == NULL || (c->flags & (CLIENT_DEAD|CLIENT_SUSPENDED)) != 0)
@ -1009,11 +1009,9 @@ server_client_handle_key(struct client *c, key_code key)
*/
if (server_client_is_default_key_table(c, c->keytable) &&
wp != NULL &&
wp->mode != NULL &&
wp->mode->mode->key_table != NULL) {
tablename = wp->mode->mode->key_table(wp->mode);
table = key_bindings_get_table(tablename, 1);
}
(wme = TAILQ_FIRST(&wp->modes)) != NULL &&
wme->mode->key_table != NULL)
table = key_bindings_get_table(wme->mode->key_table(wme), 1);
else
table = c->keytable;
first = table;

7
tmux.h
View File

@ -722,7 +722,10 @@ struct window_mode_entry {
const struct window_mode *mode;
void *data;
struct screen *screen;
u_int prefix;
TAILQ_ENTRY (window_mode_entry) entry;
};
/* Child window structure. */
@ -768,6 +771,7 @@ struct window_pane {
int fd;
struct bufferevent *event;
u_int disabled;
struct event resize_timer;
@ -793,7 +797,7 @@ struct window_pane {
struct grid *saved_grid;
struct grid_cell saved_cell;
struct window_mode_entry *mode;
TAILQ_HEAD (, window_mode_entry) modes;
struct event modetimer;
time_t modelast;
char *searchstr;
@ -2208,6 +2212,7 @@ int window_pane_set_mode(struct window_pane *,
const struct window_mode *, struct cmd_find_state *,
struct args *);
void window_pane_reset_mode(struct window_pane *);
void window_pane_reset_mode_all(struct window_pane *);
void window_pane_key(struct window_pane *, struct client *,
struct session *, struct winlink *, key_code,
struct mouse_event *);

View File

@ -136,6 +136,9 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
evtimer_del(&data->timer);
evtimer_add(&data->timer, &tv);
if (TAILQ_FIRST(&wp->modes) != wme)
return;
t = time(NULL);
gmtime_r(&t, &now);
gmtime_r(&data->tim, &then);
@ -144,7 +147,7 @@ window_clock_timer_callback(__unused int fd, __unused short events, void *arg)
data->tim = t;
window_clock_draw_screen(wme);
server_redraw_window(wp->window);
wp->flags |= PANE_REDRAW;
}
static struct screen *

View File

@ -258,7 +258,7 @@ window_copy_init(struct window_mode_entry *wme,
data = window_copy_common_init(wme);
if (wp->fd != -1)
if (wp->fd != -1 && wp->disabled++ == 0)
bufferevent_disable(wp->event, EV_READ|EV_WRITE);
data->backing = &wp->base;
@ -302,7 +302,7 @@ window_copy_free(struct window_mode_entry *wme)
struct window_pane *wp = wme->wp;
struct window_copy_mode_data *data = wme->data;
if (wp->fd != -1)
if (wp->fd != -1 && --wp->disabled == 0)
bufferevent_enable(wp->event, EV_READ|EV_WRITE);
free(data->searchmark);
@ -330,7 +330,7 @@ window_copy_add(struct window_pane *wp, const char *fmt, ...)
void
window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
{
struct window_mode_entry *wme = wp->mode;
struct window_mode_entry *wme = TAILQ_FIRST(&wp->modes);
struct window_copy_mode_data *data = wme->data;
struct screen *backing = data->backing;
struct screen_write_ctx back_ctx, ctx;
@ -377,7 +377,7 @@ window_copy_vadd(struct window_pane *wp, const char *fmt, va_list ap)
void
window_copy_pageup(struct window_pane *wp, int half_page)
{
window_copy_pageup1(wp->mode, half_page);
window_copy_pageup1(TAILQ_FIRST(&wp->modes), half_page);
}
static void
@ -2540,34 +2540,38 @@ window_copy_rectangle_toggle(struct window_mode_entry *wme)
static void
window_copy_move_mouse(struct mouse_event *m)
{
struct window_pane *wp;
u_int x, y;
struct window_pane *wp;
struct window_mode_entry *wme;
u_int x, y;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL ||
wp->mode == NULL ||
wp->mode->mode != &window_copy_mode)
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_copy_mode)
return;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
window_copy_update_cursor(wp->mode, x, y);
window_copy_update_cursor(wme, x, y);
}
void
window_copy_start_drag(struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
u_int x, y;
struct window_pane *wp;
struct window_mode_entry *wme;
u_int x, y;
if (c == NULL)
return;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL ||
wp->mode == NULL ||
wp->mode->mode != &window_copy_mode)
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_copy_mode)
return;
if (cmd_mouse_at(wp, m, &x, &y, 1) != 0)
@ -2576,30 +2580,35 @@ window_copy_start_drag(struct client *c, struct mouse_event *m)
c->tty.mouse_drag_update = window_copy_drag_update;
c->tty.mouse_drag_release = NULL; /* will fire MouseDragEnd key */
window_copy_update_cursor(wp->mode, x, y);
window_copy_start_selection(wp->mode);
window_copy_redraw_screen(wp->mode);
window_copy_update_cursor(wme, x, y);
window_copy_start_selection(wme);
window_copy_redraw_screen(wme);
}
static void
window_copy_drag_update(__unused struct client *c, struct mouse_event *m)
window_copy_drag_update(struct client *c, struct mouse_event *m)
{
struct window_pane *wp;
struct window_mode_entry *wme;
struct window_copy_mode_data *data;
u_int x, y, old_cy;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL ||
wp->mode == NULL ||
wp->mode->mode != &window_copy_mode)
if (c == NULL)
return;
data = wp->mode->data;
wp = cmd_mouse_pane(m, NULL, NULL);
if (wp == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);
if (wme == NULL || wme->mode != &window_copy_mode)
return;
data = wme->data;
if (cmd_mouse_at(wp, m, &x, &y, 0) != 0)
return;
old_cy = data->cy;
window_copy_update_cursor(wp->mode, x, y);
if (window_copy_update_selection(wp->mode, 1))
window_copy_redraw_selection(wp->mode, old_cy);
window_copy_update_cursor(wme, x, y);
if (window_copy_update_selection(wme, 1))
window_copy_redraw_selection(wme, old_cy);
}

View File

@ -810,7 +810,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
wp->fd = -1;
wp->event = NULL;
wp->mode = NULL;
TAILQ_INIT(&wp->modes);
wp->layout_cell = NULL;
@ -844,7 +844,7 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit)
static void
window_pane_destroy(struct window_pane *wp)
{
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
free(wp->searchstr);
if (wp->fd != -1) {
@ -1047,14 +1047,18 @@ window_pane_error_callback(__unused struct bufferevent *bufev,
void
window_pane_resize(struct window_pane *wp, u_int sx, u_int sy)
{
struct window_mode_entry *wme;
if (sx == wp->sx && sy == wp->sy)
return;
wp->sx = sx;
wp->sy = sy;
screen_resize(&wp->base, sx, sy, wp->saved_grid == NULL);
if (wp->mode != NULL && wp->mode->mode->resize != NULL)
wp->mode->mode->resize(wp->mode, sx, sy);
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL && wme->mode->resize != NULL)
wme->mode->resize(wme, sx, sy);
wp->flags |= PANE_RESIZE;
}
@ -1208,7 +1212,7 @@ window_pane_mode_timer(__unused int fd, __unused short events, void *arg)
if (wp->modelast < time(NULL) - WINDOW_MODE_TIMEOUT) {
if (ioctl(wp->fd, FIONREAD, &n) == -1 || n > 0)
window_pane_reset_mode(wp);
window_pane_reset_mode_all(wp);
}
}
@ -1216,59 +1220,90 @@ int
window_pane_set_mode(struct window_pane *wp, const struct window_mode *mode,
struct cmd_find_state *fs, struct args *args)
{
struct screen *s;
struct timeval tv = { .tv_sec = 10 };
struct timeval tv = { .tv_sec = 10 };
struct window_mode_entry *wme;
if (wp->mode != NULL)
if (!TAILQ_EMPTY(&wp->modes) && TAILQ_FIRST(&wp->modes)->mode == mode)
return (1);
wp->mode = xcalloc(1, sizeof *wp->mode);
wp->mode->wp = wp;
wp->mode->mode = mode;
wp->mode->prefix = 1;
wp->modelast = time(NULL);
evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
evtimer_add(&wp->modetimer, &tv);
if (TAILQ_EMPTY(&wp->modes)) {
evtimer_set(&wp->modetimer, window_pane_mode_timer, wp);
evtimer_add(&wp->modetimer, &tv);
}
if ((s = wp->mode->mode->init(wp->mode, fs, args)) != NULL)
wp->screen = s;
TAILQ_FOREACH(wme, &wp->modes, entry) {
if (wme->mode == mode)
break;
}
if (wme != NULL)
TAILQ_REMOVE(&wp->modes, wme, entry);
else {
wme = xcalloc(1, sizeof *wme);
wme->wp = wp;
wme->mode = mode;
wme->prefix = 1;
wme->screen = wme->mode->init(wme, fs, args);
}
TAILQ_INSERT_HEAD(&wp->modes, wme, entry);
wp->screen = wme->screen;
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
return (0);
}
void
window_pane_reset_mode(struct window_pane *wp)
{
if (wp->mode == NULL)
struct window_mode_entry *wme, *next;
if (TAILQ_EMPTY(&wp->modes))
return;
evtimer_del(&wp->modetimer);
wme = TAILQ_FIRST(&wp->modes);
TAILQ_REMOVE(&wp->modes, wme, entry);
wme->mode->free(wme);
free(wme);
wp->mode->mode->free(wp->mode);
free(wp->mode);
wp->mode = NULL;
wp->screen = &wp->base;
next = TAILQ_FIRST(&wp->modes);
if (next == NULL) {
log_debug("%s: no next mode", __func__);
evtimer_del(&wp->modetimer);
wp->screen = &wp->base;
} else {
log_debug("%s: next mode is %s", __func__, next->mode->name);
wp->screen = next->screen;
if (next != NULL && next->mode->resize != NULL)
next->mode->resize(next, wp->sx, wp->sy);
}
wp->flags |= (PANE_REDRAW|PANE_CHANGED);
server_status_window(wp->window);
notify_pane("pane-mode-changed", wp);
}
void
window_pane_reset_mode_all(struct window_pane *wp)
{
while (!TAILQ_EMPTY(&wp->modes))
window_pane_reset_mode(wp);
}
void
window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
struct winlink *wl, key_code key, struct mouse_event *m)
{
struct window_mode_entry *wme = wp->mode;
struct window_mode_entry *wme;
struct window_pane *wp2;
if (KEYC_IS_MOUSE(key) && m == NULL)
return;
wme = TAILQ_FIRST(&wp->modes);
if (wme != NULL) {
wp->modelast = time(NULL);
if (wme->mode->key != NULL)
@ -1286,7 +1321,7 @@ window_pane_key(struct window_pane *wp, struct client *c, struct session *s,
if (options_get_number(wp->window->options, "synchronize-panes")) {
TAILQ_FOREACH(wp2, &wp->window->panes, entry) {
if (wp2 != wp &&
wp2->mode == NULL &&
TAILQ_EMPTY(&wp2->modes) &&
wp2->fd != -1 &&
(~wp2->flags & PANE_INPUTOFF) &&
window_pane_visible(wp2))