From 359285928b63605a130990699c33c8af42be9fd2 Mon Sep 17 00:00:00 2001 From: Nicholas Marriott Date: Mon, 13 Jul 2009 10:43:52 +0000 Subject: [PATCH] Support "alternate screen" mode (terminfo smcup/rmcup) typically used by full screen interactive programs to preserve the screen contents. When activated, it saves a copy of the visible grid and disables scrolling into and resizing out of the history; when deactivated the visible data is restored and the history reenabled. --- grid-view.c | 2 +- grid.c | 46 +++++++++++++++++++++++++++++++++ input.c | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++-- screen.c | 26 ++++++++++++++----- tmux.h | 10 ++++++++ window.c | 4 +++ 6 files changed, 153 insertions(+), 9 deletions(-) diff --git a/grid-view.c b/grid-view.c index 5ad8fc2f..bf58c61f 100644 --- a/grid-view.c +++ b/grid-view.c @@ -92,7 +92,7 @@ grid_view_scroll_region_up(struct grid *gd, u_int rupper, u_int rlower) { GRID_DEBUG(gd, "rupper=%u, rlower=%u", rupper, rlower); - if (rupper == 0 && rlower == gd->sy - 1) { + if (gd->flags & GRID_HISTORY && rupper == 0 && rlower == gd->sy - 1) { grid_scroll_line(gd); return; } diff --git a/grid.c b/grid.c index 92683716..38560f73 100644 --- a/grid.c +++ b/grid.c @@ -95,6 +95,8 @@ grid_create(u_int sx, u_int sy, u_int hlimit) gd->sx = sx; gd->sy = sy; + gd->flags = GRID_HISTORY; + gd->hsize = 0; gd->hlimit = hlimit; @@ -517,3 +519,47 @@ grid_string_cells(struct grid *gd, u_int px, u_int py, u_int nx) buf[off] = '\0'; return (buf); } + +/* + * Duplicate a set of lines between two grids. If there aren't enough lines in + * either source or destination, the number of lines is limited to the number + * available. + */ +void +grid_duplicate_lines( + struct grid *dst, u_int dy, struct grid *src, u_int sy, u_int ny) +{ + u_int yy; + + GRID_DEBUG(src, "dy=%u, sy=%u, ny=%u", dy, sy, ny); + + if (dy + ny > dst->hsize + dst->sy) + ny = dst->hsize + dst->sy - dy; + if (sy + ny > src->hsize + src->sy) + ny = src->hsize + src->sy - sy; + grid_clear_lines(dst, dy, ny); + + for (yy = 0; yy < ny; yy++) { + dst->size[dy] = src->size[sy]; + if (src->size[sy] == 0) + dst->data[dy] = NULL; + else { + dst->data[dy] = xcalloc( + src->size[sy], sizeof **dst->data); + memcpy(dst->data[dy], src->data[sy], + src->size[sy] * (sizeof **dst->data)); + } + + dst->usize[dy] = src->usize[sy]; + if (src->usize[sy] == 0) + dst->udata[dy] = NULL; + else { + dst->udata[sy] = xcalloc( + src->usize[sy], sizeof **dst->udata); + memcpy(dst->udata[dy], src->udata[sy], + src->usize[sy] * (sizeof **dst->udata)); + } + + sy++; dy++; + } +} diff --git a/input.c b/input.c index b1247f2b..b75494c8 100644 --- a/input.c +++ b/input.c @@ -1151,7 +1151,10 @@ input_handle_sequence_el(struct input_ctx *ictx) void input_handle_sequence_sm(struct input_ctx *ictx) { - uint16_t n; + struct window_pane *wp = ictx->wp; + struct screen *s = &wp->base; + u_int sx, sy; + uint16_t n; if (ARRAY_LENGTH(&ictx->args) > 1) return; @@ -1172,6 +1175,29 @@ input_handle_sequence_sm(struct input_ctx *ictx) screen_write_mousemode(&ictx->ctx, 1); log_debug("mouse on"); break; + case 1049: + if (wp->saved_grid != NULL) + break; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * Enter alternative screen mode. A copy of the visible + * screen is saved and the history is not updated + */ + + wp->saved_grid = grid_create(sx, sy, 0); + grid_duplicate_lines( + wp->saved_grid, 0, s->grid, screen_hsize(s), sy); + wp->saved_cx = s->cx; + wp->saved_cy = s->cy; + + grid_view_clear(s->grid, 0, 0, sx, sy); + + wp->base.grid->flags &= ~GRID_HISTORY; + + wp->flags |= PANE_REDRAW; + break; default: log_debug("unknown SM [%hhu]: %u", ictx->private, n); break; @@ -1195,7 +1221,10 @@ input_handle_sequence_sm(struct input_ctx *ictx) void input_handle_sequence_rm(struct input_ctx *ictx) { - uint16_t n; + struct window_pane *wp = ictx->wp; + struct screen *s = &wp->base; + u_int sx, sy; + uint16_t n; if (ARRAY_LENGTH(&ictx->args) > 1) return; @@ -1216,6 +1245,47 @@ input_handle_sequence_rm(struct input_ctx *ictx) screen_write_mousemode(&ictx->ctx, 0); log_debug("mouse off"); break; + case 1049: + if (wp->saved_grid == NULL) + break; + sx = screen_size_x(s); + sy = screen_size_y(s); + + /* + * Exit alternative screen mode and restore the copied + * grid. + */ + + /* + * If the current size is bigger, temporarily resize + * to the old size before copying back. + */ + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, wp->saved_grid->sy); + + /* Restore the grid and cursor position. */ + grid_duplicate_lines( + s->grid, screen_hsize(s), wp->saved_grid, 0, sy); + s->cx = wp->saved_cx; + if (s->cx > screen_size_x(s) - 1) + s->cx = screen_size_x(s) - 1; + s->cy = wp->saved_cy; + if (s->cy > screen_size_y(s) - 1) + s->cy = screen_size_y(s) - 1; + + /* + * Turn history back on (so resize can use it) and then + * resize back to the current size. + */ + wp->base.grid->flags |= GRID_HISTORY; + if (sy > wp->saved_grid->sy) + screen_resize(s, sx, sy); + + grid_destroy(wp->saved_grid); + wp->saved_grid = NULL; + + wp->flags |= PANE_REDRAW; + break; default: log_debug("unknown RM [%hhu]: %u", ictx->private, n); break; diff --git a/screen.c b/screen.c index 16c3b5ea..f1a723a3 100644 --- a/screen.c +++ b/screen.c @@ -174,10 +174,20 @@ screen_resize_y(struct screen *s, u_int sy) needed -= available; /* - * Now just increase the history size to take over the lines - * which are left. XXX Should apply history limit? + * Now just increase the history size, if possible, to take + * over the lines which are left. If history is off, delete + * lines from the top. + * + * XXX Should apply history limit? */ - gd->hsize += needed; + available = s->cy; + if (gd->flags & GRID_HISTORY) + gd->hsize += needed; + else if (available > 0) { + if (available > needed) + available = needed; + grid_view_delete_lines(gd, 0, available); + } s->cy -= needed; } @@ -191,14 +201,18 @@ screen_resize_y(struct screen *s, u_int sy) if (sy > oldy) { needed = sy - oldy; - /* Try to pull as much as possible out of the history. */ + /* + * Try to pull as much as possible out of the history, if is + * is enabled. + */ available = gd->hsize; - if (available > 0) { + if (gd->flags & GRID_HISTORY && available > 0) { if (available > needed) available = needed; gd->hsize -= available; s->cy += available; - } + } else + available = 0; needed -= available; /* Then fill the rest in with blanks. */ diff --git a/tmux.h b/tmux.h index 28778e9f..481b0ca7 100644 --- a/tmux.h +++ b/tmux.h @@ -443,6 +443,9 @@ struct grid_utf8 { /* Entire grid of cells. */ struct grid { + int flags; +#define GRID_HISTORY 0x1 /* scroll lines into history */ + u_int sx; u_int sy; @@ -614,6 +617,11 @@ struct window_pane { struct screen *screen; struct screen base; + /* Saved in alternative screen mode. */ + u_int saved_cx; + u_int saved_cy; + struct grid *saved_grid; + const struct window_mode *mode; void *modedata; @@ -1328,6 +1336,8 @@ void grid_clear_lines(struct grid *, u_int, u_int); void grid_move_lines(struct grid *, u_int, u_int, u_int); void grid_move_cells(struct grid *, u_int, u_int, u_int, u_int); char *grid_string_cells(struct grid *, u_int, u_int, u_int); +void grid_duplicate_lines( + struct grid *, u_int, struct grid *, u_int, u_int); /* grid-view.c */ const struct grid_cell *grid_view_peek_cell(struct grid *, u_int, u_int); diff --git a/window.c b/window.c index 95addc41..753f796a 100644 --- a/window.c +++ b/window.c @@ -407,6 +407,8 @@ window_pane_create(struct window *w, u_int sx, u_int sy, u_int hlimit) wp->sx = sx; wp->sy = sy; + wp->saved_grid = NULL; + screen_init(&wp->base, sx, sy, hlimit); wp->screen = &wp->base; @@ -425,6 +427,8 @@ window_pane_destroy(struct window_pane *wp) window_pane_reset_mode(wp); screen_free(&wp->base); + if (wp->saved_grid != NULL) + grid_destroy(wp->saved_grid); buffer_destroy(wp->in); buffer_destroy(wp->out);