void tty_cmd_cell(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; u_int cx; u_int width; const struct grid_cell *gc = ctx->cell; const struct grid_utf8 *gu = ctx->utf8; if (gc->flags & GRID_FLAG_UTF8) width = gu->width; else width = 1; tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); /* Is the cursor in the very last position? */ if (ctx->ocx > wp->sx - width) { if (wp->xoff != 0 || wp->sx != tty->sx) { /* * The pane doesn't fill the entire line, the linefeed * will already have happened, so just move the cursor. */ tty_cursor_pane(tty, ctx, 0, ctx->ocy + 1); } else if (tty->cx < tty->sx) { /* * The cursor isn't in the last position already, so * move as far left as possible and redraw the last * cell to move into the last position. */ cx = screen_size_x(s) - width; tty_cursor_pane(tty, ctx, cx, ctx->ocy); tty_cell(tty, &ctx->last_cell, &ctx->last_utf8); } } else tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_cell(tty, ctx->cell, ctx->utf8); }
void window_copy_cursor_right(struct window_pane *wp) { struct window_copy_mode_data *data = wp->modedata; u_int px, py; if (data->screen.sel.flag && data->rectflag) px = screen_size_x(&data->screen); else { py = screen_hsize(&wp->base) + data->cy - data->oy; px = window_copy_find_length(wp, py); } if (data->cx >= px) { window_copy_cursor_start_of_line(wp); window_copy_cursor_down(wp, 0); } else { window_copy_update_cursor(wp, data->cx + 1, data->cy); if (window_copy_update_selection(wp)) window_copy_redraw_lines(wp, data->cy, 1); } }
/* * UTF-8 wide characters are a bit of an annoyance. They take up more than one * cell on the screen, so following cells must not be drawn by marking them as * padding. * * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 * character, it is necessary to also overwrite any other cells which covered * by the same character. */ void screen_write_overwrite(struct screen_write_ctx *ctx, u_int width) { struct screen *s = ctx->s; struct grid *gd = s->grid; const struct grid_cell *gc; u_int xx; gc = grid_view_peek_cell(gd, s->cx, s->cy); if (gc->flags & GRID_FLAG_PADDING) { /* * A padding cell, so clear any following and leading padding * cells back to the character. Don't overwrite the current * cell as that happens later anyway. */ xx = s->cx + 1; while (--xx > 0) { gc = grid_view_peek_cell(gd, xx, s->cy); if (!(gc->flags & GRID_FLAG_PADDING)) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } /* Overwrite the character at the start of this padding. */ grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } /* * Overwrite any padding cells that belong to a UTF-8 character * we'll be overwriting with the current character. */ xx = s->cx + width - 1; while (++xx < screen_size_x(s)) { gc = grid_view_peek_cell(gd, xx, s->cy); if (!(gc->flags & GRID_FLAG_PADDING)) break; grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); } }
/* Resize screen. */ void screen_resize(struct screen *s, u_int sx, u_int sy) { if (sx < 1) sx = 1; if (sy < 1) sy = 1; if (sx != screen_size_x(s)) { screen_resize_x(s, sx); /* * It is unclear what should happen to tabs on resize. xterm * seems to try and maintain them, rxvt resets them. Resetting * is simpler and more reliable so let's do that. */ screen_reset_tabs(s); } if (sy != screen_size_y(s)) screen_resize_y(s, sy); }
void tty_cmd_linefeed(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; if (ctx->ocy != ctx->orlower) return; if (!tty_pane_full_width(tty, ctx) || !tty_term_has(tty->term, TTYC_CSR)) { if (tty_large_region(tty, ctx)) wp->flags |= PANE_REDRAW; else if (tty_use_rect(tty)) { tty_cra_pane (tty, ctx, ctx->orupper + 1, 0, ctx->orlower, screen_size_x(s) - 1, ctx->orupper, 0); tty_cmd_clearline(tty, ctx); } else tty_redraw_region(tty, ctx); return; } /* * If this line wrapped naturally (ctx->num is nonzero), don't do * anything - the cursor can just be moved to the last cell and wrap * naturally. */ if (ctx->num && !(tty->term->flags & TERM_EARLYWRAP)) return; tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->ocy); tty_putc(tty, '\n'); }
/* Clear to start of screen. */ void screen_write_clearstartofscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx = screen_size_x(s); screen_write_initctx(ctx, &ttyctx); if (s->cy > 0) { screen_dirty_clear(s, 0, 0, sx - 1, s->cy); grid_view_clear(s->grid, 0, 0, sx, s->cy, 8); } if (s->cx > sx - 1) { screen_dirty_clear(s, 0, s->cy, sx - 1, s->cy); grid_view_clear(s->grid, 0, s->cy, sx, 1, 8); } else { screen_dirty_clear(s, 0, s->cy, s->cx, s->cy); grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, 8); } tty_write(tty_cmd_clearstartofscreen, &ttyctx); }
/* Clear to end of screen from cursor. */ void screen_write_clearendofscreen(struct screen_write_ctx *ctx) { struct screen *s = ctx->s; struct tty_ctx ttyctx; u_int sx, sy; screen_write_initctx(ctx, &ttyctx, 0); sx = screen_size_x(s); sy = screen_size_y(s); /* Scroll into history if it is enabled and clearing entire screen. */ if (s->cy == 0 && s->grid->flags & GRID_HISTORY) grid_view_clear_history(s->grid); else { if (s->cx <= sx - 1) grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1); grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1)); } tty_write(tty_cmd_clearendofscreen, &ttyctx); }
struct screen * window_more_init(struct window_pane *wp) { struct window_more_mode_data *data; struct screen *s; int keys; wp->modedata = data = xmalloc(sizeof *data); ARRAY_INIT(&data->list); data->top = 0; s = &data->screen; screen_init(s, screen_size_x(&wp->base), screen_size_y(&wp->base), 0); s->mode &= ~MODE_CURSOR; keys = options_get_number(&wp->window->options, "mode-keys"); if (keys == MODEKEY_EMACS) mode_key_init(&data->mdata, &mode_key_tree_emacs_choice); else mode_key_init(&data->mdata, &mode_key_tree_vi_choice); return (s); }
void tty_cmd_reverseindex(struct tty *tty, const struct tty_ctx *ctx) { struct window_pane *wp = ctx->wp; struct screen *s = wp->screen; if (ctx->ocy != ctx->orupper) return; if (wp->xoff != 0 || screen_size_x(s) < tty->sx || !tty_term_has(tty->term, TTYC_CSR) || !tty_term_has(tty->term, TTYC_RI)) { tty_redraw_region(tty, ctx); return; } tty_reset(tty); tty_region_pane(tty, ctx, ctx->orupper, ctx->orlower); tty_cursor_pane(tty, ctx, ctx->ocx, ctx->orupper); tty_putcode(tty, TTYC_RI); }
/* Cursor up by ny. */ void screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; if (ny == 0) ny = 1; if (s->cy < s->rupper) { /* Above region. */ if (ny > s->cy) ny = s->cy; } else { /* Below region. */ if (ny > s->cy - s->rupper) ny = s->cy - s->rupper; } if (s->cx == screen_size_x(s)) s->cx--; if (ny == 0) return; s->cy -= ny; }
/* Cursor down by ny. */ void screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) { struct screen *s = ctx->s; if (ny == 0) ny = 1; if (s->cy > s->rlower) { /* Below region. */ if (ny > screen_size_y(s) - 1 - s->cy) ny = screen_size_y(s) - 1 - s->cy; } else { /* Above region. */ if (ny > s->rlower - s->cy) ny = s->rlower - s->cy; } if (s->cx == screen_size_x(s)) s->cx--; if (ny == 0) return; s->cy += ny; }
/* Execute control sequence. */ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; if (ictx->flags & INPUT_DISCARD) return (0); if (input_split(ictx) != 0) return (0); log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), sizeof input_csi_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ n = input_get(ictx, 0, 1, 1); while (s->cx > 0 && n-- > 0) { do s->cx--; while (s->cx > 0 && !bit_test(s->tabs, s->cx)); } break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUD: screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUF: screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CNL: screen_write_carriagereturn(sctx); screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CPL: screen_write_carriagereturn(sctx); screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[?1;2c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[>0;95;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ECH: screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DCH: screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case 5: input_reply(ictx, "\033[0n"); break; case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ED: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofscreen(sctx); break; case 1: screen_write_clearstartofscreen(sctx); break; case 2: screen_write_clearscreen(sctx); break; case 3: switch (input_get(ictx, 1, 0, 0)) { case 0: /* * Linux console extension to clear history * (for example before locking the screen). */ screen_write_clearhistory(sctx); break; } break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_EL: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofline(sctx); break; case 1: screen_write_clearstartofline(sctx); break; case 2: screen_write_clearline(sctx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, n - 1, s->cy); break; case INPUT_CSI_ICH: screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_IL: screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_RCP: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_mode_clear(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_RM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_mode_clear(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 7: /* DECAWM */ screen_write_mode_clear(&ictx->ctx, MODE_WRAP); break; case 25: /* TCEM */ screen_write_mode_clear(&ictx->ctx, MODE_CURSOR); break; case 1000: case 1001: case 1002: case 1003: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); break; case 1004: wp->focus_notify &= ~PANE_FOCUS_NOTIFY; break; case 1005: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_clear(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_off(wp, &ictx->cell, 0); break; case 1049: window_pane_alternate_off(wp, &ictx->cell, 1); break; case 2004: screen_write_mode_clear(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SCP: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_CSI_SGR: input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_mode_set(&ictx->ctx, MODE_INSERT); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_mode_set(&ictx->ctx, MODE_KCURSOR); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 7: /* DECAWM */ screen_write_mode_set(&ictx->ctx, MODE_WRAP); break; case 25: /* TCEM */ screen_write_mode_set(&ictx->ctx, MODE_CURSOR); break; case 1000: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_STANDARD); break; case 1002: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_BUTTON); break; case 1003: screen_write_mode_clear(&ictx->ctx, ALL_MOUSE_MODES); screen_write_mode_set(&ictx->ctx, MODE_MOUSE_ANY); break; case 1004: wp->focus_notify |= PANE_FOCUS_NOTIFY; break; case 1005: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_UTF8); break; case 1006: screen_write_mode_set(&ictx->ctx, MODE_MOUSE_SGR); break; case 47: case 1047: window_pane_alternate_on(wp, &ictx->cell, 0); break; case 1049: window_pane_alternate_on(wp, &ictx->cell, 1); break; case 2004: screen_write_mode_set(&ictx->ctx, MODE_BRACKETPASTE); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case 0: if (s->cx < screen_size_x(s)) bit_clear(s->tabs, s->cx); break; case 3: bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_VPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, s->cx, n - 1); break; case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); screen_set_cursor_style(s, n); break; } return (0); }
/* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; u_int sx = screen_size_x(s), sy = screen_size_y(s); struct grid_line *gl; struct grid_cell tmp_gc, now_gc; struct grid_cell_entry *gce; int insert, skip, selected, wrapped = 0; ctx->cells++; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > sx || (s->cx != sx && s->cx > sx - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx); tty_write(tty_cmd_utf8character, &ttyctx); } return; } /* Initialise the redraw context. */ screen_write_initctx(ctx, &ttyctx); /* If in insert mode, make space for the cells. */ if (s->mode & MODE_INSERT) { if (s->cx <= sx - width) { screen_write_flush(ctx); xx = sx - s->cx - width; grid_view_insert_cells(s->grid, s->cx, s->cy, xx, 8); } insert = 1; } else insert = 0; skip = !insert; /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > sx - width) { screen_write_flush(ctx); screen_write_save_last(ctx, &ttyctx); screen_write_linefeed(ctx, 1); s->cx = 0; /* carriage return */ skip = 0; wrapped = 1; } /* Sanity check cursor position. */ if (s->cx > sx - width || s->cy > sy - 1) return; /* Handle overwriting of UTF-8 characters. */ gl = &s->grid->linedata[s->grid->hsize + s->cy]; if (gl->flags & GRID_LINE_EXTENDED) { grid_view_get_cell(gd, s->cx, s->cy, &now_gc); if (screen_write_overwrite(ctx, &now_gc, width)) skip = 0; } /* * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ for (xx = s->cx + 1; xx < s->cx + width; xx++) { grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); skip = 0; } /* If no change, do not draw. */ if (skip) { if (s->cx >= gl->cellsize) skip = grid_cells_equal(gc, &grid_default_cell); else { gce = &gl->celldata[s->cx]; if (gce->flags & GRID_FLAG_EXTENDED) skip = 0; else if (gc->flags != gce->flags) skip = 0; else if (gc->attr != gce->data.attr) skip = 0; else if (gc->fg != gce->data.fg) skip = 0; else if (gc->bg != gce->data.bg) skip = 0; else if (gc->data.width != 1) skip = 0; else if (gce->data.data != gc->data.data[0]) skip = 0; } } /* Update the selection the flag and set the cell. */ selected = screen_check_selection(s, s->cx, s->cy); if (selected && ~gc->flags & GRID_FLAG_SELECTED) { skip = 0; memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!selected && gc->flags & GRID_FLAG_SELECTED) { skip = 0; memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags &= ~GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!skip) grid_view_set_cell(gd, s->cx, s->cy, gc); /* * Move the cursor. If not wrapping, stick at the last character and * replace it. */ last = !(s->mode & MODE_WRAP); if (s->cx <= sx - last - width) s->cx += width; else s->cx = sx - last; /* Create space for character in insert mode. */ if (insert) { ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Write to the screen. */ if (selected) { screen_write_flush(ctx); screen_select_cell(s, &tmp_gc, gc); ttyctx.cell = &tmp_gc; tty_write(tty_cmd_cell, &ttyctx); ctx->written++; } else if (!skip) { if (wrapped) { ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); ctx->written++; } else { /* * If wp is NULL, we are not updating the terminal and * don't care about actually writing the cells * (tty_write will just return). So don't even bother * allocating the dirty array. */ if (ctx->wp != NULL && s->dirty == NULL) { log_debug("%s: allocating %u bits", __func__, s->dirtysize); s->dirty = bit_alloc(s->dirtysize); } if (s->dirty != NULL) { bit_set(s->dirty, screen_dirty_bit(s, ttyctx.ocx, ttyctx.ocy)); ctx->dirty++; } } } else ctx->skipped++; }
static char * cmd_capture_pane_history(struct args *args, struct cmd_q *cmdq, struct window_pane *wp, size_t *len) { struct grid *gd; const struct grid_line *gl; struct grid_cell *gc = NULL; int n, with_codes, escape_c0, join_lines; u_int i, sx, top, bottom, tmp; char *cause, *buf, *line; const char *Sflag, *Eflag; size_t linelen; sx = screen_size_x(&wp->base); if (args_has(args, 'a')) { gd = wp->saved_grid; if (gd == NULL) { if (!args_has(args, 'q')) { cmdq_error(cmdq, "no alternate screen"); return (NULL); } return (xstrdup("")); } } else gd = wp->base.grid; Sflag = args_get(args, 'S'); if (Sflag != NULL && strcmp(Sflag, "-") == 0) top = 0; else { n = args_strtonum(args, 'S', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { top = gd->hsize; free(cause); } else if (n < 0 && (u_int) -n > gd->hsize) top = 0; else top = gd->hsize + n; if (top > gd->hsize + gd->sy - 1) top = gd->hsize + gd->sy - 1; } Eflag = args_get(args, 'E'); if (Eflag != NULL && strcmp(Eflag, "-") == 0) bottom = gd->hsize + gd->sy - 1; else { n = args_strtonum(args, 'E', INT_MIN, SHRT_MAX, &cause); if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; free(cause); } else if (n < 0 && (u_int) -n > gd->hsize) bottom = 0; else bottom = gd->hsize + n; if (bottom > gd->hsize + gd->sy - 1) bottom = gd->hsize + gd->sy - 1; } if (bottom < top) { tmp = bottom; bottom = top; top = tmp; } with_codes = args_has(args, 'e'); escape_c0 = args_has(args, 'C'); join_lines = args_has(args, 'J'); buf = NULL; for (i = top; i <= bottom; i++) { line = grid_string_cells(gd, 0, i, sx, &gc, with_codes, escape_c0, !join_lines); linelen = strlen(line); buf = cmd_capture_pane_append(buf, len, line, linelen); gl = grid_peek_line(gd, i); if (!join_lines || !(gl->flags & GRID_LINE_WRAPPED)) buf[(*len)++] = '\n'; free(line); } return (buf); }
void window_copy_copy_selection(struct window_pane *wp, struct client *c) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; char *buf; size_t off; u_int i, xx, yy, sx, sy, ex, ey, limit; u_int firstsx, lastex, restex, restsx; if (!s->sel.flag) return; buf = xmalloc(1); off = 0; *buf = '\0'; /* * The selection extends from selx,sely to (adjusted) cx,cy on * the base screen. */ /* Find start and end. */ xx = data->cx; yy = screen_hsize(&wp->base) + data->cy - data->oy; if (yy < data->sely || (yy == data->sely && xx < data->selx)) { sx = xx; sy = yy; ex = data->selx; ey = data->sely; } else { sx = data->selx; sy = data->sely; ex = xx; ey = yy; } /* Trim ex to end of line. */ xx = window_copy_find_length(wp, ey); if (ex > xx) ex = xx; /* * Deal with rectangle-copy if necessary; four situations: start of * first line (firstsx), end of last line (lastex), start (restsx) and * end (restex) of all other lines. */ xx = screen_size_x(s); if (data->rectflag) { /* * Need to ignore the column with the cursor in it, which for * rectangular copy means knowing which side the cursor is on. */ if (data->selx < data->cx) { /* Selection start is on the left. */ lastex = data->cx; restex = data->cx; firstsx = data->selx; restsx = data->selx; } else { /* Cursor is on the left. */ lastex = data->selx + 1; restex = data->selx + 1; firstsx = data->cx + 1; restsx = data->cx + 1; } } else { /* * Like emacs, keep the top-left-most character, and drop the * bottom-right-most, regardless of copy direction. */ lastex = ex; restex = xx; firstsx = sx; restsx = 0; } /* Copy the lines. */ if (sy == ey) window_copy_copy_line(wp, &buf, &off, sy, firstsx, lastex); else { window_copy_copy_line(wp, &buf, &off, sy, firstsx, restex); if (ey - sy > 1) { for (i = sy + 1; i < ey; i++) { window_copy_copy_line( wp, &buf, &off, i, restsx, restex); } } window_copy_copy_line(wp, &buf, &off, ey, restsx, lastex); } /* Don't bother if no data. */ if (off == 0) { xfree(buf); return; } off--; /* remove final \n */ /* Add the buffer to the stack. */ limit = options_get_number(&c->session->options, "buffer-limit"); paste_add(&c->session->buffers, buf, off, limit); }
void window_clock_draw_screen(struct window_pane *wp) { struct window_clock_mode_data *data = wp->modedata; struct screen_write_ctx ctx; int colour, style; struct screen *s = &data->screen; struct grid_cell gc; char tim[64], *ptr; time_t t; struct tm *tm; u_int i, j, x, y, idx; colour = options_get_number(&wp->window->options, "clock-mode-colour"); style = options_get_number(&wp->window->options, "clock-mode-style"); screen_write_start(&ctx, NULL, s); t = time(NULL); tm = localtime(&t); if (style == 0) { strftime(tim, sizeof tim, "%l:%M ", localtime(&t)); if (tm->tm_hour >= 12) strlcat(tim, "PM", sizeof tim); else strlcat(tim, "AM", sizeof tim); } else strftime(tim, sizeof tim, "%H:%M", tm); screen_write_clearscreen(&ctx); if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { x = (screen_size_x(s) / 2) - (strlen(tim) / 2); y = screen_size_y(s) / 2; screen_write_cursormove(&ctx, x, y); memcpy(&gc, &grid_default_cell, sizeof gc); colour_set_fg(&gc, colour); screen_write_puts(&ctx, &gc, "%s", tim); } screen_write_stop(&ctx); return; } x = (screen_size_x(s) / 2) - 3 * strlen(tim); y = (screen_size_y(s) / 2) - 3; memcpy(&gc, &grid_default_cell, sizeof gc); colour_set_bg(&gc, colour); for (ptr = tim; *ptr != '\0'; ptr++) { if (*ptr >= '0' && *ptr <= '9') idx = *ptr - '0'; else if (*ptr == ':') idx = 10; else if (*ptr == 'A') idx = 11; else if (*ptr == 'P') idx = 12; else if (*ptr == 'M') idx = 13; else { x += 6; continue; } for (j = 0; j < 5; j++) { for (i = 0; i < 5; i++) { screen_write_cursormove(&ctx, x + i, y + j); if (window_clock_table[idx][j][i]) screen_write_putc(&ctx, &gc, ' '); } } x += 6; } screen_write_stop(&ctx); }
void clock_draw(struct screen_write_ctx *ctx, u_int colour, int style) { struct screen *s = ctx->s; struct grid_cell gc; char tim[64], *ptr; time_t t; u_int i, j, x, y, idx; t = time(NULL); if (style == 0) strftime(tim, sizeof tim, "%l:%M %p", localtime(&t)); else strftime(tim, sizeof tim, "%H:%M", localtime(&t)); screen_write_clearscreen(ctx); memcpy(&gc, &grid_default_cell, sizeof gc); gc.fg = colour; if (screen_size_x(s) < 6 * strlen(tim) || screen_size_y(s) < 6) { if (screen_size_x(s) >= strlen(tim) && screen_size_y(s) != 0) { x = (screen_size_x(s) / 2) - (strlen(tim) / 2); y = screen_size_y(s) / 2; screen_write_cursormove(ctx, x, y); gc.fg = colour; screen_write_puts(ctx, &gc, "%s", tim); } return; } x = (screen_size_x(s) / 2) - 3 * strlen(tim); y = (screen_size_y(s) / 2) - 3; for (ptr = tim; *ptr != '\0'; ptr++) { if (*ptr >= '0' && *ptr <= '9') idx = *ptr - '0'; else if (*ptr == ':') idx = 10; else if (*ptr == 'A') idx = 11; else if (*ptr == 'P') idx = 12; else if (*ptr == 'M') idx = 13; else { x += 6; continue; } for (j = 0; j < 5; j++) { screen_write_cursormove(ctx, x, y + j); for (i = 0; i < 5; i++) { if (clock_table[idx][j][i]) gc.attr |= GRID_ATTR_REVERSE; else gc.attr &= ~GRID_ATTR_REVERSE; screen_write_putc(ctx, &gc, ' '); } } x += 6; } }
/* Execute escape sequence. */ int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), sizeof input_esc_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_ESC_RIS: memcpy(&ictx->cell, &grid_default_cell, sizeof ictx->cell); memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = 0; ictx->old_cy = 0; screen_write_reset(sctx); break; case INPUT_ESC_IND: screen_write_linefeed(sctx, 0); break; case INPUT_ESC_NEL: screen_write_carriagereturn(sctx); screen_write_linefeed(sctx, 0); break; case INPUT_ESC_HTS: if (s->cx < screen_size_x(s)) bit_set(s->tabs, s->cx); break; case INPUT_ESC_RI: screen_write_reverseindex(sctx); break; case INPUT_ESC_DECKPAM: screen_write_mode_set(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECKPNM: screen_write_mode_clear(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECSC: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_ESC_DECRC: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; case INPUT_ESC_SCSON_G0: /* * Not really supported, but fake it up enough for those that * use it to switch character sets (by redefining G0 to * graphics set, rather than switching to G1). */ ictx->cell.attr &= ~GRID_ATTR_CHARSET; break; case INPUT_ESC_SCSOFF_G0: ictx->cell.attr |= GRID_ATTR_CHARSET; break; } return (0); }
/* Execute C0 control sequence. */ int input_c0_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; u_int trigger; log_debug("%s: '%c", __func__, ictx->ch); switch (ictx->ch) { case '\000': /* NUL */ break; case '\007': /* BEL */ wp->window->flags |= WINDOW_BELL; break; case '\010': /* BS */ screen_write_backspace(sctx); goto count_c0; case '\011': /* HT */ /* Don't tab beyond the end of the line. */ if (s->cx >= screen_size_x(s) - 1) break; /* Find the next tab point, or use the last column if none. */ do { s->cx++; if (bit_test(s->tabs, s->cx)) break; } while (s->cx < screen_size_x(s) - 1); break; case '\012': /* LF */ case '\013': /* VT */ case '\014': /* FF */ screen_write_linefeed(sctx, 0); goto count_c0; case '\015': /* CR */ screen_write_carriagereturn(sctx); goto count_c0; case '\016': /* SO */ ictx->cell.attr |= GRID_ATTR_CHARSET; break; case '\017': /* SI */ ictx->cell.attr &= ~GRID_ATTR_CHARSET; break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } return (0); count_c0: trigger = options_get_number(&wp->window->options, "c0-change-trigger"); if (++wp->changes == trigger) { wp->flags |= PANE_DROP; window_pane_timer_start(wp); } return (0); }
/* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; struct grid_cell tmp_gc, now_gc; int insert, skip, selected; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > screen_size_x(s) || (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx); tty_write(tty_cmd_utf8character, &ttyctx); } return; } /* Initialise the redraw context. */ screen_write_initctx(ctx, &ttyctx); /* If in insert mode, make space for the cells. */ if ((s->mode & MODE_INSERT) && s->cx <= screen_size_x(s) - width) { xx = screen_size_x(s) - s->cx - width; grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); insert = 1; } else insert = 0; skip = !insert; /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { screen_write_linefeed(ctx, 1); s->cx = 0; /* carriage return */ skip = 0; } /* Sanity check cursor position. */ if (s->cx > screen_size_x(s) - width || s->cy > screen_size_y(s) - 1) return; /* Handle overwriting of UTF-8 characters. */ grid_view_get_cell(gd, s->cx, s->cy, &now_gc); if (screen_write_overwrite(ctx, &now_gc, width)) skip = 0; /* * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ for (xx = s->cx + 1; xx < s->cx + width; xx++) { grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); skip = 0; } /* If no change, do not draw. */ if (skip) skip = (memcmp(&now_gc, gc, sizeof now_gc) == 0); /* Update the selection the flag and set the cell. */ selected = screen_check_selection(s, s->cx, s->cy); if (selected && ~gc->flags & GRID_FLAG_SELECTED) { skip = 0; memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!selected && gc->flags & GRID_FLAG_SELECTED) { skip = 0; memcpy(&tmp_gc, gc, sizeof tmp_gc); tmp_gc.flags &= ~GRID_FLAG_SELECTED; grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); } else if (!skip) grid_view_set_cell(gd, s->cx, s->cy, gc); /* * Move the cursor. If not wrapping, stick at the last character and * replace it. */ last = !(s->mode & MODE_WRAP); if (s->cx <= screen_size_x(s) - last - width) s->cx += width; else s->cx = screen_size_x(s) - last; /* Create space for character in insert mode. */ if (insert) { ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } /* Save last cell if it will be needed. */ if (!skip && ctx->wp != NULL && ttyctx.ocx > ctx->wp->sx - width) screen_write_save_last(ctx, &ttyctx); /* Write to the screen. */ if (selected) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags; tmp_gc.flags &= ~(GRID_FLAG_FGRGB|GRID_FLAG_BGRGB); tmp_gc.flags &= ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; tty_write(tty_cmd_cell, &ttyctx); } else if (!skip) { ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); } }
void tty_draw_line(struct tty *tty, struct screen *s, u_int py, u_int ox, u_int oy) { const struct grid_cell *gc; struct grid_line *gl; struct grid_cell tmpgc; const struct grid_utf8 *gu; u_int i, sx; tty_update_mode(tty, tty->mode & ~MODE_CURSOR, s); sx = screen_size_x(s); if (sx > s->grid->linedata[s->grid->hsize + py].cellsize) sx = s->grid->linedata[s->grid->hsize + py].cellsize; if (sx > tty->sx) sx = tty->sx; /* * Don't move the cursor to the start permission if it will wrap there * itself. */ gl = NULL; if (py != 0) gl = &s->grid->linedata[s->grid->hsize + py - 1]; if (oy + py == 0 || gl == NULL || !(gl->flags & GRID_LINE_WRAPPED) || tty->cx < tty->sx || ox != 0 || (oy + py != tty->cy + 1 && tty->cy != s->rlower + oy)) tty_cursor(tty, ox, oy + py); for (i = 0; i < sx; i++) { gc = grid_view_peek_cell(s->grid, i, py); gu = NULL; if (gc->flags & GRID_FLAG_UTF8) gu = grid_view_peek_utf8(s->grid, i, py); if (screen_check_selection(s, i, py)) { memcpy(&tmpgc, &s->sel.cell, sizeof tmpgc); tmpgc.data = gc->data; tmpgc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmpgc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); tty_cell(tty, &tmpgc, gu); } else tty_cell(tty, gc, gu); } if (sx >= tty->sx) { tty_update_mode(tty, tty->mode, s); return; } tty_reset(tty); tty_cursor(tty, ox + sx, oy + py); if (screen_size_x(s) >= tty->sx && tty_term_has(tty->term, TTYC_EL)) tty_putcode(tty, TTYC_EL); else { for (i = sx; i < screen_size_x(s); i++) tty_putc(tty, ' '); } tty_update_mode(tty, tty->mode, s); }
/* Execute escape sequence. */ int input_esc_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; if (ictx->flags & INPUT_DISCARD) return (0); log_debug("%s: '%c', %s", __func__, ictx->ch, ictx->interm_buf); entry = bsearch(ictx, input_esc_table, nitems(input_esc_table), sizeof input_esc_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_ESC_RIS: input_reset_cell(ictx); screen_write_reset(sctx); break; case INPUT_ESC_IND: screen_write_linefeed(sctx, 0); break; case INPUT_ESC_NEL: screen_write_carriagereturn(sctx); screen_write_linefeed(sctx, 0); break; case INPUT_ESC_HTS: if (s->cx < screen_size_x(s)) bit_set(s->tabs, s->cx); break; case INPUT_ESC_RI: screen_write_reverseindex(sctx); break; case INPUT_ESC_DECKPAM: screen_write_mode_set(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECKPNM: screen_write_mode_clear(sctx, MODE_KKEYPAD); break; case INPUT_ESC_DECSC: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_ESC_DECRC: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_ESC_DECALN: screen_write_alignmenttest(sctx); break; case INPUT_ESC_SCSG0_ON: ictx->cell.g0set = 1; break; case INPUT_ESC_SCSG0_OFF: ictx->cell.g0set = 0; break; case INPUT_ESC_SCSG1_ON: ictx->cell.g1set = 1; break; case INPUT_ESC_SCSG1_OFF: ictx->cell.g1set = 0; break; } return (0); }
int cmd_capture_pane_exec(struct cmd *self, struct cmd_ctx *ctx) { struct args *args = self->args; struct window_pane *wp; char *buf, *line, *cause; struct screen *s; struct grid *gd; int buffer, n; u_int i, limit, top, bottom, tmp; size_t len, linelen; if (cmd_find_pane(ctx, args_get(args, 't'), NULL, &wp) == NULL) return (-1); s = &wp->base; gd = s->grid; buf = NULL; len = 0; n = args_strtonum(args, 'S', SHRT_MIN, SHRT_MAX, &cause); if (cause != NULL) { top = gd->hsize; xfree(cause); } else if (n < 0 && (u_int) -n > gd->hsize) top = 0; else top = gd->hsize + n; if (top > gd->hsize + gd->sy - 1) top = gd->hsize + gd->sy - 1; n = args_strtonum(args, 'E', SHRT_MIN, SHRT_MAX, &cause); if (cause != NULL) { bottom = gd->hsize + gd->sy - 1; xfree(cause); } else if (n < 0 && (u_int) -n > gd->hsize) bottom = 0; else bottom = gd->hsize + n; if (bottom > gd->hsize + gd->sy - 1) bottom = gd->hsize + gd->sy - 1; if (bottom < top) { tmp = bottom; bottom = top; top = tmp; } for (i = top; i <= bottom; i++) { line = grid_string_cells(s->grid, 0, i, screen_size_x(s)); linelen = strlen(line); buf = xrealloc(buf, 1, len + linelen + 1); memcpy(buf + len, line, linelen); len += linelen; buf[len++] = '\n'; xfree(line); } limit = options_get_number(&global_options, "buffer-limit"); if (!args_has(args, 'b')) { paste_add(&global_buffers, buf, len, limit); return (0); } buffer = args_strtonum(args, 'b', 0, INT_MAX, &cause); if (cause != NULL) { ctx->error(ctx, "buffer %s", cause); xfree(cause); return (-1); } if (paste_replace(&global_buffers, buffer, buf, len) != 0) { ctx->error(ctx, "no buffer %d", buffer); xfree(buf); return (-1); } return (0); }
/* Execute control sequence. */ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct window_pane *wp = ictx->wp; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; if (ictx->flags & INPUT_DISCARD) return (0); if (input_split(ictx) != 0) return (0); log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), sizeof input_csi_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ n = input_get(ictx, 0, 1, 1); while (s->cx > 0 && n-- > 0) { do s->cx--; while (s->cx > 0 && !bit_test(s->tabs, s->cx)); } break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUD: screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUF: screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[?1;2c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_DCH: screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case 5: input_reply(ictx, "\033[0n"); break; case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ED: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofscreen(sctx); break; case 1: screen_write_clearstartofscreen(sctx); break; case 2: screen_write_clearscreen(sctx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_EL: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofline(sctx); break; case 1: screen_write_clearstartofline(sctx); break; case 2: screen_write_clearline(sctx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, n - 1, s->cy); break; case INPUT_CSI_ICH: screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_IL: screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_RM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_insertmode(&ictx->ctx, 0); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_RM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_kcursormode(&ictx->ctx, 0); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 25: /* TCEM */ screen_write_cursormode(&ictx->ctx, 0); break; case 1000: screen_write_mousemode(&ictx->ctx, 0); break; case 1049: window_pane_alternate_off(wp, &ictx->cell); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SGR: input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: switch (input_get(ictx, 0, 0, -1)) { case 4: /* IRM */ screen_write_insertmode(&ictx->ctx, 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_SM_PRIVATE: switch (input_get(ictx, 0, 0, -1)) { case 1: /* GATM */ screen_write_kcursormode(&ictx->ctx, 1); break; case 3: /* DECCOLM */ screen_write_cursormove(&ictx->ctx, 0, 0); screen_write_clearscreen(&ictx->ctx); break; case 25: /* TCEM */ screen_write_cursormode(&ictx->ctx, 1); break; case 1000: screen_write_mousemode(&ictx->ctx, 1); break; case 1049: window_pane_alternate_on(wp, &ictx->cell); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case 0: if (s->cx < screen_size_x(s)) bit_clear(s->tabs, s->cx); break; case 3: bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_VPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, s->cx, n - 1); break; } return (0); }
/* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc, const struct utf8_data *utf8data) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; struct grid_utf8 gu; u_int width, xx; struct grid_cell tmp_gc, *tmp_gcp; int insert = 0; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; /* Find character width. */ if (gc->flags & GRID_FLAG_UTF8) width = utf8data->width; else width = 1; /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > screen_size_x(s) || (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { if (screen_write_combine(ctx, utf8data) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } return; } /* Initialise the redraw context, saving the last cell. */ screen_write_initctx(ctx, &ttyctx, 1); /* If in insert mode, make space for the cells. */ if ((s->mode & MODE_INSERT) && s->cx <= screen_size_x(s) - width) { xx = screen_size_x(s) - s->cx - width; grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); insert = 1; } /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { screen_write_linefeed(ctx, 1); s->cx = 0; /* carriage return */ } /* Sanity checks. */ if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) || s->cy > screen_size_y(s) - 1) return; /* Handle overwriting of UTF-8 characters. */ screen_write_overwrite(ctx, width); /* * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ for (xx = s->cx + 1; xx < s->cx + width; xx++) { tmp_gcp = grid_view_get_cell(gd, xx, s->cy); if (tmp_gcp != NULL) tmp_gcp->flags |= GRID_FLAG_PADDING; } /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); if (gc->flags & GRID_FLAG_UTF8) { /* Construct UTF-8 and write it. */ grid_utf8_set(&gu, utf8data); grid_view_set_utf8(gd, s->cx, s->cy, &gu); } /* Move the cursor. */ s->cx += width; /* Draw to the screen if necessary. */ if (insert) { ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } ttyctx.utf8 = &gu; if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); tmp_gc.data = gc->data; tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; tty_write(tty_cmd_cell, &ttyctx); } else { ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); } }
/* Write cell data. */ void screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) { struct screen *s = ctx->s; struct grid *gd = s->grid; struct tty_ctx ttyctx; u_int width, xx, last; struct grid_cell tmp_gc; int insert; /* Ignore padding. */ if (gc->flags & GRID_FLAG_PADDING) return; width = gc->data.width; /* * If this is a wide character and there is no room on the screen, for * the entire character, don't print it. */ if (!(s->mode & MODE_WRAP) && (width > 1 && (width > screen_size_x(s) || (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))) return; /* * If the width is zero, combine onto the previous character, if * there is space. */ if (width == 0) { if (screen_write_combine(ctx, &gc->data) == 0) { screen_write_initctx(ctx, &ttyctx, 0); tty_write(tty_cmd_utf8character, &ttyctx); } return; } /* Initialise the redraw context, saving the last cell. */ screen_write_initctx(ctx, &ttyctx, 1); /* If in insert mode, make space for the cells. */ if ((s->mode & MODE_INSERT) && s->cx <= screen_size_x(s) - width) { xx = screen_size_x(s) - s->cx - width; grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx); insert = 1; } else insert = 0; /* Check this will fit on the current line and wrap if not. */ if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) { screen_write_linefeed(ctx, 1); s->cx = 0; /* carriage return */ } /* Sanity check cursor position. */ if (s->cx > screen_size_x(s) - width || s->cy > screen_size_y(s) - 1) return; /* Handle overwriting of UTF-8 characters. */ screen_write_overwrite(ctx, width); /* * If the new character is UTF-8 wide, fill in padding cells. Have * already ensured there is enough room. */ memcpy(&tmp_gc, &grid_default_cell, sizeof tmp_gc); tmp_gc.flags |= GRID_FLAG_PADDING; tmp_gc.data.width = 0; for (xx = s->cx + 1; xx < s->cx + width; xx++) grid_view_set_cell(gd, xx, s->cy, &tmp_gc); /* Set the cell. */ grid_view_set_cell(gd, s->cx, s->cy, gc); /* * Move the cursor. If not wrapping, stick at the last character and * replace it. */ last = !(s->mode & MODE_WRAP); if (s->cx <= screen_size_x(s) - last - width) s->cx += width; else s->cx = screen_size_x(s) - last; /* Draw to the screen if necessary. */ if (insert) { ttyctx.num = width; tty_write(tty_cmd_insertcharacter, &ttyctx); } if (screen_check_selection(s, s->cx - width, s->cy)) { memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc); utf8_copy(&tmp_gc.data, &gc->data); tmp_gc.attr = tmp_gc.attr & ~GRID_ATTR_CHARSET; tmp_gc.attr |= gc->attr & GRID_ATTR_CHARSET; tmp_gc.flags = gc->flags & ~(GRID_FLAG_FG256|GRID_FLAG_BG256); tmp_gc.flags |= s->sel.cell.flags & (GRID_FLAG_FG256|GRID_FLAG_BG256); ttyctx.cell = &tmp_gc; tty_write(tty_cmd_cell, &ttyctx); } else { ttyctx.cell = gc; tty_write(tty_cmd_cell, &ttyctx); } }
/* ARGSUSED */ void window_copy_mouse( struct window_pane *wp, struct session *sess, struct mouse_event *m) { struct window_copy_mode_data *data = wp->modedata; struct screen *s = &data->screen; u_int i, old_cy; if (m->x >= screen_size_x(s)) return; if (m->y >= screen_size_y(s)) return; /* If mouse wheel (buttons 4 and 5), scroll. */ if ((m->b & MOUSE_45)) { if ((m->b & MOUSE_BUTTON) == MOUSE_1) { for (i = 0; i < 5; i++) window_copy_cursor_up(wp, 0); } else if ((m->b & MOUSE_BUTTON) == MOUSE_2) { old_cy = data->cy; for (i = 0; i < 5; i++) window_copy_cursor_down(wp, 0); if (old_cy == data->cy) goto reset_mode; } return; } /* * If already reading motion, move the cursor while buttons are still * pressed, or stop the selection on their release. */ if (s->mode & MODE_MOUSE_BUTTON) { if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { window_copy_update_cursor(wp, m->x, m->y); if (window_copy_update_selection(wp)) window_copy_redraw_screen(wp); return; } goto reset_mode; } /* Otherwise if other buttons pressed, start selection and motion. */ if ((m->b & MOUSE_BUTTON) != MOUSE_UP) { s->mode &= ~MODE_MOUSE_STANDARD; s->mode |= MODE_MOUSE_BUTTON; window_copy_update_cursor(wp, m->x, m->y); window_copy_start_selection(wp); window_copy_redraw_screen(wp); } return; reset_mode: s->mode &= ~MODE_MOUSE_BUTTON; s->mode |= MODE_MOUSE_STANDARD; if (sess != NULL) { window_copy_copy_selection(wp); window_pane_reset_mode(wp); } }
/* Execute control sequence. */ int input_csi_dispatch(struct input_ctx *ictx) { struct screen_write_ctx *sctx = &ictx->ctx; struct screen *s = sctx->s; struct input_table_entry *entry; int n, m; if (ictx->flags & INPUT_DISCARD) return (0); if (input_split(ictx) != 0) return (0); log_debug("%s: '%c' \"%s\" \"%s\"", __func__, ictx->ch, ictx->interm_buf, ictx->param_buf); entry = bsearch(ictx, input_csi_table, nitems(input_csi_table), sizeof input_csi_table[0], input_table_compare); if (entry == NULL) { log_debug("%s: unknown '%c'", __func__, ictx->ch); return (0); } switch (entry->type) { case INPUT_CSI_CBT: /* Find the previous tab point, n times. */ n = input_get(ictx, 0, 1, 1); while (s->cx > 0 && n-- > 0) { do s->cx--; while (s->cx > 0 && !bit_test(s->tabs, s->cx)); } break; case INPUT_CSI_CUB: screen_write_cursorleft(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUD: screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUF: screen_write_cursorright(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CUP: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, 1); screen_write_cursormove(sctx, m - 1, n - 1); break; case INPUT_CSI_CUU: screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CNL: screen_write_carriagereturn(sctx); screen_write_cursordown(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_CPL: screen_write_carriagereturn(sctx); screen_write_cursorup(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DA: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[?1;2c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_DA_TWO: switch (input_get(ictx, 0, 0, 0)) { case 0: input_reply(ictx, "\033[>0;95;0c"); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ECH: screen_write_clearcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DCH: screen_write_deletecharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DECSTBM: n = input_get(ictx, 0, 1, 1); m = input_get(ictx, 1, 1, screen_size_y(s)); screen_write_scrollregion(sctx, n - 1, m - 1); break; case INPUT_CSI_DL: screen_write_deleteline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_DSR: switch (input_get(ictx, 0, 0, 0)) { case 5: input_reply(ictx, "\033[0n"); break; case 6: input_reply(ictx, "\033[%u;%uR", s->cy + 1, s->cx + 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_ED: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofscreen(sctx); break; case 1: screen_write_clearstartofscreen(sctx); break; case 2: screen_write_clearscreen(sctx); break; case 3: switch (input_get(ictx, 1, 0, 0)) { case 0: /* * Linux console extension to clear history * (for example before locking the screen). */ screen_write_clearhistory(sctx); break; } break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_EL: switch (input_get(ictx, 0, 0, 0)) { case 0: screen_write_clearendofline(sctx); break; case 1: screen_write_clearstartofline(sctx); break; case 2: screen_write_clearline(sctx); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_HPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, n - 1, s->cy); break; case INPUT_CSI_ICH: screen_write_insertcharacter(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_IL: screen_write_insertline(sctx, input_get(ictx, 0, 1, 1)); break; case INPUT_CSI_RCP: memcpy(&ictx->cell, &ictx->old_cell, sizeof ictx->cell); screen_write_cursormove(sctx, ictx->old_cx, ictx->old_cy); break; case INPUT_CSI_RM: input_csi_dispatch_rm(ictx); break; case INPUT_CSI_RM_PRIVATE: input_csi_dispatch_rm_private(ictx); break; case INPUT_CSI_SCP: memcpy(&ictx->old_cell, &ictx->cell, sizeof ictx->old_cell); ictx->old_cx = s->cx; ictx->old_cy = s->cy; break; case INPUT_CSI_SGR: input_csi_dispatch_sgr(ictx); break; case INPUT_CSI_SM: input_csi_dispatch_sm(ictx); break; case INPUT_CSI_SM_PRIVATE: input_csi_dispatch_sm_private(ictx); break; case INPUT_CSI_TBC: switch (input_get(ictx, 0, 0, 0)) { case 0: if (s->cx < screen_size_x(s)) bit_clear(s->tabs, s->cx); break; case 3: bit_nclear(s->tabs, 0, screen_size_x(s) - 1); break; default: log_debug("%s: unknown '%c'", __func__, ictx->ch); break; } break; case INPUT_CSI_VPA: n = input_get(ictx, 0, 1, 1); screen_write_cursormove(sctx, s->cx, n - 1); break; case INPUT_CSI_DECSCUSR: n = input_get(ictx, 0, 0, 0); screen_set_cursor_style(s, n); break; } return (0); }