199e242abSchristos /* $OpenBSD$ */ 2698d5317Sjmmv 3698d5317Sjmmv /* 4f26e8bc9Schristos * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com> 5698d5317Sjmmv * 6698d5317Sjmmv * Permission to use, copy, modify, and distribute this software for any 7698d5317Sjmmv * purpose with or without fee is hereby granted, provided that the above 8698d5317Sjmmv * copyright notice and this permission notice appear in all copies. 9698d5317Sjmmv * 10698d5317Sjmmv * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 11698d5317Sjmmv * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 12698d5317Sjmmv * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 13698d5317Sjmmv * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 14698d5317Sjmmv * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER 15698d5317Sjmmv * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING 16698d5317Sjmmv * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 17698d5317Sjmmv */ 18698d5317Sjmmv 19698d5317Sjmmv #include <sys/types.h> 20698d5317Sjmmv 2161fba46bSchristos #include <stdlib.h> 22698d5317Sjmmv #include <string.h> 23698d5317Sjmmv 24698d5317Sjmmv #include "tmux.h" 25698d5317Sjmmv 26e271dbb8Schristos static struct screen_write_citem *screen_write_collect_trim( 27e271dbb8Schristos struct screen_write_ctx *, u_int, u_int, u_int, int *); 28e9a2d6faSchristos static void screen_write_collect_clear(struct screen_write_ctx *, u_int, 29e9a2d6faSchristos u_int); 30e271dbb8Schristos static void screen_write_collect_scroll(struct screen_write_ctx *, u_int); 31e271dbb8Schristos static void screen_write_collect_flush(struct screen_write_ctx *, int, 32e271dbb8Schristos const char *); 33e9a2d6faSchristos static int screen_write_overwrite(struct screen_write_ctx *, 34e9a2d6faSchristos struct grid_cell *, u_int); 35f844e94eSwiz static int screen_write_combine(struct screen_write_ctx *, 36f844e94eSwiz const struct grid_cell *); 37e9a2d6faSchristos 38e271dbb8Schristos struct screen_write_citem { 39e9a2d6faSchristos u_int x; 40fe99a117Schristos int wrapped; 41e9a2d6faSchristos 42e271dbb8Schristos enum { TEXT, CLEAR } type; 43e9a2d6faSchristos u_int used; 44e271dbb8Schristos u_int bg; 45e9a2d6faSchristos 46e9a2d6faSchristos struct grid_cell gc; 47e9a2d6faSchristos 48e271dbb8Schristos TAILQ_ENTRY(screen_write_citem) entry; 49e9a2d6faSchristos }; 50e271dbb8Schristos struct screen_write_cline { 51e271dbb8Schristos char *data; 52e271dbb8Schristos TAILQ_HEAD(, screen_write_citem) items; 53e9a2d6faSchristos }; 54e271dbb8Schristos TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist = 55e271dbb8Schristos TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist); 56e271dbb8Schristos 57e271dbb8Schristos static struct screen_write_citem * 58e271dbb8Schristos screen_write_get_citem(void) 59e271dbb8Schristos { 60e271dbb8Schristos struct screen_write_citem *ci; 61e271dbb8Schristos 62e271dbb8Schristos ci = TAILQ_FIRST(&screen_write_citem_freelist); 63e271dbb8Schristos if (ci != NULL) { 64e271dbb8Schristos TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry); 65e271dbb8Schristos memset(ci, 0, sizeof *ci); 66e271dbb8Schristos return (ci); 67e271dbb8Schristos } 68e271dbb8Schristos return (xcalloc(1, sizeof *ci)); 69e271dbb8Schristos } 70e271dbb8Schristos 71e271dbb8Schristos static void 72e271dbb8Schristos screen_write_free_citem(struct screen_write_citem *ci) 73e271dbb8Schristos { 74e271dbb8Schristos TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry); 75e271dbb8Schristos } 76e9a2d6faSchristos 770a274e86Schristos static void 780a274e86Schristos screen_write_offset_timer(__unused int fd, __unused short events, void *data) 790a274e86Schristos { 800a274e86Schristos struct window *w = data; 810a274e86Schristos 820a274e86Schristos tty_update_window_offset(w); 830a274e86Schristos } 840a274e86Schristos 850a274e86Schristos /* Set cursor position. */ 860a274e86Schristos static void 870a274e86Schristos screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) 880a274e86Schristos { 890a274e86Schristos struct window_pane *wp = ctx->wp; 900a274e86Schristos struct window *w; 910a274e86Schristos struct screen *s = ctx->s; 920a274e86Schristos struct timeval tv = { .tv_usec = 10000 }; 930a274e86Schristos 940a274e86Schristos if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) 950a274e86Schristos return; 960a274e86Schristos 970a274e86Schristos if (cx != -1) { 980a274e86Schristos if ((u_int)cx > screen_size_x(s)) /* allow last column */ 990a274e86Schristos cx = screen_size_x(s) - 1; 1000a274e86Schristos s->cx = cx; 1010a274e86Schristos } 1020a274e86Schristos if (cy != -1) { 1030a274e86Schristos if ((u_int)cy > screen_size_y(s) - 1) 1040a274e86Schristos cy = screen_size_y(s) - 1; 1050a274e86Schristos s->cy = cy; 1060a274e86Schristos } 1070a274e86Schristos 1080a274e86Schristos if (wp == NULL) 1090a274e86Schristos return; 1100a274e86Schristos w = wp->window; 1110a274e86Schristos 1120a274e86Schristos if (!event_initialized(&w->offset_timer)) 1130a274e86Schristos evtimer_set(&w->offset_timer, screen_write_offset_timer, w); 1140a274e86Schristos if (!evtimer_pending(&w->offset_timer, NULL)) 1150a274e86Schristos evtimer_add(&w->offset_timer, &tv); 1160a274e86Schristos } 1170a274e86Schristos 118e271dbb8Schristos /* Do a full redraw. */ 119e271dbb8Schristos static void 120e271dbb8Schristos screen_write_redraw_cb(const struct tty_ctx *ttyctx) 121e271dbb8Schristos { 122e271dbb8Schristos struct window_pane *wp = ttyctx->arg; 123e271dbb8Schristos 124e271dbb8Schristos if (wp != NULL) 125e271dbb8Schristos wp->flags |= PANE_REDRAW; 126e271dbb8Schristos } 127e271dbb8Schristos 128e271dbb8Schristos /* Update context for client. */ 129e271dbb8Schristos static int 130e271dbb8Schristos screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c) 131e271dbb8Schristos { 132e271dbb8Schristos struct window_pane *wp = ttyctx->arg; 133e271dbb8Schristos 134f844e94eSwiz if (ttyctx->allow_invisible_panes) { 135f844e94eSwiz if (session_has(c->session, wp->window)) 136f844e94eSwiz return (1); 137f844e94eSwiz return (0); 138f844e94eSwiz } 139f844e94eSwiz 140e271dbb8Schristos if (c->session->curw->window != wp->window) 141e271dbb8Schristos return (0); 142e271dbb8Schristos if (wp->layout_cell == NULL) 143e271dbb8Schristos return (0); 144e271dbb8Schristos 145e271dbb8Schristos if (wp->flags & (PANE_REDRAW|PANE_DROP)) 146e271dbb8Schristos return (-1); 147e271dbb8Schristos if (c->flags & CLIENT_REDRAWPANES) { 148e271dbb8Schristos /* 149e271dbb8Schristos * Redraw is already deferred to redraw another pane - redraw 150e271dbb8Schristos * this one also when that happens. 151e271dbb8Schristos */ 152e271dbb8Schristos log_debug("%s: adding %%%u to deferred redraw", __func__, 153e271dbb8Schristos wp->id); 154e271dbb8Schristos wp->flags |= PANE_REDRAW; 155e271dbb8Schristos return (-1); 156e271dbb8Schristos } 157e271dbb8Schristos 158e271dbb8Schristos ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy, 159e271dbb8Schristos &ttyctx->wsx, &ttyctx->wsy); 160e271dbb8Schristos 161e271dbb8Schristos ttyctx->xoff = ttyctx->rxoff = wp->xoff; 162e271dbb8Schristos ttyctx->yoff = ttyctx->ryoff = wp->yoff; 163e271dbb8Schristos 164e271dbb8Schristos if (status_at_line(c) == 0) 165e271dbb8Schristos ttyctx->yoff += status_line_size(c); 166e271dbb8Schristos 167e271dbb8Schristos return (1); 168e271dbb8Schristos } 169e271dbb8Schristos 170e271dbb8Schristos /* Set up context for TTY command. */ 171e271dbb8Schristos static void 172e271dbb8Schristos screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, 173e271dbb8Schristos int sync) 174e271dbb8Schristos { 175e271dbb8Schristos struct screen *s = ctx->s; 176e271dbb8Schristos 177e271dbb8Schristos memset(ttyctx, 0, sizeof *ttyctx); 178e271dbb8Schristos 179e271dbb8Schristos ttyctx->s = s; 180e271dbb8Schristos ttyctx->sx = screen_size_x(s); 181e271dbb8Schristos ttyctx->sy = screen_size_y(s); 182e271dbb8Schristos 183e271dbb8Schristos ttyctx->ocx = s->cx; 184e271dbb8Schristos ttyctx->ocy = s->cy; 185e271dbb8Schristos ttyctx->orlower = s->rlower; 186e271dbb8Schristos ttyctx->orupper = s->rupper; 187e271dbb8Schristos 18846548964Swiz memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults); 18946548964Swiz if (ctx->init_ctx_cb != NULL) { 190e271dbb8Schristos ctx->init_ctx_cb(ctx, ttyctx); 19146548964Swiz if (ttyctx->palette != NULL) { 19246548964Swiz if (ttyctx->defaults.fg == 8) 19346548964Swiz ttyctx->defaults.fg = ttyctx->palette->fg; 19446548964Swiz if (ttyctx->defaults.bg == 8) 19546548964Swiz ttyctx->defaults.bg = ttyctx->palette->bg; 19646548964Swiz } 19746548964Swiz } else { 198e271dbb8Schristos ttyctx->redraw_cb = screen_write_redraw_cb; 19946548964Swiz if (ctx->wp != NULL) { 20046548964Swiz tty_default_colours(&ttyctx->defaults, ctx->wp); 20146548964Swiz ttyctx->palette = &ctx->wp->palette; 202e271dbb8Schristos ttyctx->set_client_cb = screen_write_set_client_cb; 203e271dbb8Schristos ttyctx->arg = ctx->wp; 204e271dbb8Schristos } 20546548964Swiz } 206e271dbb8Schristos 20746548964Swiz if (~ctx->flags & SCREEN_WRITE_SYNC) { 20846548964Swiz /* 20946548964Swiz * For the active pane or for an overlay (no pane), we want to 21046548964Swiz * only use synchronized updates if requested (commands that 21146548964Swiz * move the cursor); for other panes, always use it, since the 21246548964Swiz * cursor will have to move. 21346548964Swiz */ 21446548964Swiz if (ctx->wp != NULL) { 21546548964Swiz if (ctx->wp != ctx->wp->window->active) 21646548964Swiz ttyctx->num = 1; 21746548964Swiz else 21846548964Swiz ttyctx->num = sync; 21946548964Swiz } else 22046548964Swiz ttyctx->num = 0x10|sync; 221e271dbb8Schristos tty_write(tty_cmd_syncstart, ttyctx); 222e271dbb8Schristos ctx->flags |= SCREEN_WRITE_SYNC; 223e271dbb8Schristos } 224e271dbb8Schristos } 225e271dbb8Schristos 226e271dbb8Schristos /* Make write list. */ 227698d5317Sjmmv void 228e271dbb8Schristos screen_write_make_list(struct screen *s) 229698d5317Sjmmv { 230e9a2d6faSchristos u_int y; 231e9a2d6faSchristos 232e271dbb8Schristos s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list); 233e271dbb8Schristos for (y = 0; y < screen_size_y(s); y++) 234e271dbb8Schristos TAILQ_INIT(&s->write_list[y].items); 235e271dbb8Schristos } 236e271dbb8Schristos 237e271dbb8Schristos /* Free write list. */ 238e271dbb8Schristos void 239e271dbb8Schristos screen_write_free_list(struct screen *s) 240e271dbb8Schristos { 241e271dbb8Schristos u_int y; 242e271dbb8Schristos 243e271dbb8Schristos for (y = 0; y < screen_size_y(s); y++) 244e271dbb8Schristos free(s->write_list[y].data); 245e271dbb8Schristos free(s->write_list); 246e271dbb8Schristos } 247e271dbb8Schristos 248e271dbb8Schristos /* Set up for writing. */ 249e271dbb8Schristos static void 250e271dbb8Schristos screen_write_init(struct screen_write_ctx *ctx, struct screen *s) 251e271dbb8Schristos { 252e9a2d6faSchristos memset(ctx, 0, sizeof *ctx); 253e9a2d6faSchristos 254698d5317Sjmmv ctx->s = s; 255e9a2d6faSchristos 256e271dbb8Schristos if (ctx->s->write_list == NULL) 257e271dbb8Schristos screen_write_make_list(ctx->s); 258e271dbb8Schristos ctx->item = screen_write_get_citem(); 259e9a2d6faSchristos 260fe99a117Schristos ctx->scrolled = 0; 261fe99a117Schristos ctx->bg = 8; 262e271dbb8Schristos } 263e271dbb8Schristos 264e271dbb8Schristos /* Initialize writing with a pane. */ 265e271dbb8Schristos void 266e271dbb8Schristos screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp, 267e271dbb8Schristos struct screen *s) 268e271dbb8Schristos { 269e271dbb8Schristos if (s == NULL) 270e271dbb8Schristos s = wp->screen; 271e271dbb8Schristos screen_write_init(ctx, s); 272e271dbb8Schristos ctx->wp = wp; 273fe99a117Schristos 27468e6ba84Schristos if (log_get_level() != 0) { 27568e6ba84Schristos log_debug("%s: size %ux%u, pane %%%u (at %u,%u)", 276e271dbb8Schristos __func__, screen_size_x(ctx->s), screen_size_y(ctx->s), 277e271dbb8Schristos wp->id, wp->xoff, wp->yoff); 2780a274e86Schristos } 27968e6ba84Schristos } 280e271dbb8Schristos 281e271dbb8Schristos /* Initialize writing with a callback. */ 282e271dbb8Schristos void 283e271dbb8Schristos screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s, 284e271dbb8Schristos screen_write_init_ctx_cb cb, void *arg) 285e271dbb8Schristos { 286e271dbb8Schristos screen_write_init(ctx, s); 287e271dbb8Schristos 288e271dbb8Schristos ctx->init_ctx_cb = cb; 289e271dbb8Schristos ctx->arg = arg; 290e271dbb8Schristos 291e271dbb8Schristos if (log_get_level() != 0) { 292e271dbb8Schristos log_debug("%s: size %ux%u, with callback", __func__, 293e271dbb8Schristos screen_size_x(ctx->s), screen_size_y(ctx->s)); 294e271dbb8Schristos } 295e271dbb8Schristos } 296e271dbb8Schristos 297e271dbb8Schristos /* Initialize writing. */ 298e271dbb8Schristos void 299e271dbb8Schristos screen_write_start(struct screen_write_ctx *ctx, struct screen *s) 300e271dbb8Schristos { 301e271dbb8Schristos screen_write_init(ctx, s); 302e271dbb8Schristos 303e271dbb8Schristos if (log_get_level() != 0) { 304e271dbb8Schristos log_debug("%s: size %ux%u, no pane", __func__, 305e271dbb8Schristos screen_size_x(ctx->s), screen_size_y(ctx->s)); 306e271dbb8Schristos } 307698d5317Sjmmv } 308698d5317Sjmmv 309698d5317Sjmmv /* Finish writing. */ 310698d5317Sjmmv void 311e9a2d6faSchristos screen_write_stop(struct screen_write_ctx *ctx) 312698d5317Sjmmv { 313e9a2d6faSchristos screen_write_collect_end(ctx); 314e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 315e9a2d6faSchristos 316e271dbb8Schristos screen_write_free_citem(ctx->item); 317698d5317Sjmmv } 318698d5317Sjmmv 31961fba46bSchristos /* Reset screen state. */ 32061fba46bSchristos void 32161fba46bSchristos screen_write_reset(struct screen_write_ctx *ctx) 32261fba46bSchristos { 32361fba46bSchristos struct screen *s = ctx->s; 32461fba46bSchristos 32561fba46bSchristos screen_reset_tabs(s); 32661fba46bSchristos screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); 32761fba46bSchristos 3280a274e86Schristos s->mode = MODE_CURSOR|MODE_WRAP; 329*890b6d91Swiz 330f844e94eSwiz if (options_get_number(global_options, "extended-keys") == 2) 331*890b6d91Swiz s->mode = (s->mode & ~EXTENDED_KEY_MODES)|MODE_KEYS_EXTENDED; 33261fba46bSchristos 333e9a2d6faSchristos screen_write_clearscreen(ctx, 8); 3340a274e86Schristos screen_write_set_cursor(ctx, 0, 0); 33561fba46bSchristos } 33661fba46bSchristos 337698d5317Sjmmv /* Write character. */ 338698d5317Sjmmv void 339e9a2d6faSchristos screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 34099e242abSchristos u_char ch) 341698d5317Sjmmv { 342e9a2d6faSchristos struct grid_cell gc; 343e9a2d6faSchristos 344e9a2d6faSchristos memcpy(&gc, gcp, sizeof gc); 345e9a2d6faSchristos 346e9a2d6faSchristos utf8_set(&gc.data, ch); 347e9a2d6faSchristos screen_write_cell(ctx, &gc); 348698d5317Sjmmv } 349698d5317Sjmmv 350698d5317Sjmmv /* Calculate string length. */ 35199e242abSchristos size_t 352f26e8bc9Schristos screen_write_strlen(const char *fmt, ...) 353698d5317Sjmmv { 354698d5317Sjmmv va_list ap; 355698d5317Sjmmv char *msg; 356f26e8bc9Schristos struct utf8_data ud; 357f26e8bc9Schristos u_char *ptr; 358698d5317Sjmmv size_t left, size = 0; 359f26e8bc9Schristos enum utf8_state more; 360698d5317Sjmmv 361698d5317Sjmmv va_start(ap, fmt); 362698d5317Sjmmv xvasprintf(&msg, fmt, ap); 363698d5317Sjmmv va_end(ap); 364698d5317Sjmmv 365f26e8bc9Schristos ptr = (u_char *)msg; 366698d5317Sjmmv while (*ptr != '\0') { 367f26e8bc9Schristos if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { 368698d5317Sjmmv ptr++; 369698d5317Sjmmv 370f26e8bc9Schristos left = strlen((char *)ptr); 371f26e8bc9Schristos if (left < (size_t)ud.size - 1) 372698d5317Sjmmv break; 373f26e8bc9Schristos while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) 374698d5317Sjmmv ptr++; 375698d5317Sjmmv ptr++; 376698d5317Sjmmv 377f26e8bc9Schristos if (more == UTF8_DONE) 378f26e8bc9Schristos size += ud.width; 379698d5317Sjmmv } else { 380f26e8bc9Schristos if (*ptr > 0x1f && *ptr < 0x7f) 381698d5317Sjmmv size++; 382698d5317Sjmmv ptr++; 383698d5317Sjmmv } 384698d5317Sjmmv } 385698d5317Sjmmv 38661fba46bSchristos free(msg); 387698d5317Sjmmv return (size); 388698d5317Sjmmv } 389698d5317Sjmmv 390e271dbb8Schristos /* Write string wrapped over lines. */ 391e271dbb8Schristos int 392e271dbb8Schristos screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width, 393e271dbb8Schristos u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...) 394e271dbb8Schristos { 395e271dbb8Schristos struct screen *s = ctx->s; 396e271dbb8Schristos va_list ap; 397e271dbb8Schristos char *tmp; 398e271dbb8Schristos u_int cy = s->cy, i, end, next, idx = 0, at, left; 399e271dbb8Schristos struct utf8_data *text; 400e271dbb8Schristos struct grid_cell gc; 401e271dbb8Schristos 402e271dbb8Schristos memcpy(&gc, gcp, sizeof gc); 403e271dbb8Schristos 404e271dbb8Schristos va_start(ap, fmt); 405e271dbb8Schristos xvasprintf(&tmp, fmt, ap); 406e271dbb8Schristos va_end(ap); 407e271dbb8Schristos 408e271dbb8Schristos text = utf8_fromcstr(tmp); 409e271dbb8Schristos free(tmp); 410e271dbb8Schristos 411e271dbb8Schristos left = (cx + width) - s->cx; 412e271dbb8Schristos for (;;) { 413e271dbb8Schristos /* Find the end of what can fit on the line. */ 414e271dbb8Schristos at = 0; 415e271dbb8Schristos for (end = idx; text[end].size != 0; end++) { 416e271dbb8Schristos if (text[end].size == 1 && text[end].data[0] == '\n') 417e271dbb8Schristos break; 418e271dbb8Schristos if (at + text[end].width > left) 419e271dbb8Schristos break; 420e271dbb8Schristos at += text[end].width; 421e271dbb8Schristos } 422e271dbb8Schristos 423e271dbb8Schristos /* 424e271dbb8Schristos * If we're on a space, that's the end. If not, walk back to 425e271dbb8Schristos * try and find one. 426e271dbb8Schristos */ 427e271dbb8Schristos if (text[end].size == 0) 428e271dbb8Schristos next = end; 429e271dbb8Schristos else if (text[end].size == 1 && text[end].data[0] == '\n') 430e271dbb8Schristos next = end + 1; 431e271dbb8Schristos else if (text[end].size == 1 && text[end].data[0] == ' ') 432e271dbb8Schristos next = end + 1; 433e271dbb8Schristos else { 434e271dbb8Schristos for (i = end; i > idx; i--) { 435e271dbb8Schristos if (text[i].size == 1 && text[i].data[0] == ' ') 436e271dbb8Schristos break; 437e271dbb8Schristos } 438e271dbb8Schristos if (i != idx) { 439e271dbb8Schristos next = i + 1; 440e271dbb8Schristos end = i; 441e271dbb8Schristos } else 442e271dbb8Schristos next = end; 443e271dbb8Schristos } 444e271dbb8Schristos 445e271dbb8Schristos /* Print the line. */ 446e271dbb8Schristos for (i = idx; i < end; i++) { 447e271dbb8Schristos utf8_copy(&gc.data, &text[i]); 448e271dbb8Schristos screen_write_cell(ctx, &gc); 449e271dbb8Schristos } 450e271dbb8Schristos 451e271dbb8Schristos /* If at the bottom, stop. */ 452e271dbb8Schristos idx = next; 453e271dbb8Schristos if (s->cy == cy + lines - 1 || text[idx].size == 0) 454e271dbb8Schristos break; 455e271dbb8Schristos 456e271dbb8Schristos screen_write_cursormove(ctx, cx, s->cy + 1, 0); 457e271dbb8Schristos left = width; 458e271dbb8Schristos } 459e271dbb8Schristos 460e271dbb8Schristos /* 461e271dbb8Schristos * Fail if on the last line and there is more to come or at the end, or 462e271dbb8Schristos * if the text was not entirely consumed. 463e271dbb8Schristos */ 464e271dbb8Schristos if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) || 465e271dbb8Schristos text[idx].size != 0) { 466e271dbb8Schristos free(text); 467e271dbb8Schristos return (0); 468e271dbb8Schristos } 469e271dbb8Schristos free(text); 470e271dbb8Schristos 471e271dbb8Schristos /* 472e271dbb8Schristos * If no more to come, move to the next line. Otherwise, leave on 473e271dbb8Schristos * the same line (except if at the end). 474e271dbb8Schristos */ 475e271dbb8Schristos if (!more || s->cx == cx + width) 476e271dbb8Schristos screen_write_cursormove(ctx, cx, s->cy + 1, 0); 477e271dbb8Schristos return (1); 478e271dbb8Schristos } 479e271dbb8Schristos 480e271dbb8Schristos /* Write simple string (no maximum length). */ 48199e242abSchristos void 482e9a2d6faSchristos screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, 48399e242abSchristos const char *fmt, ...) 484698d5317Sjmmv { 485698d5317Sjmmv va_list ap; 486698d5317Sjmmv 487698d5317Sjmmv va_start(ap, fmt); 488e9a2d6faSchristos screen_write_vnputs(ctx, -1, gcp, fmt, ap); 489698d5317Sjmmv va_end(ap); 490698d5317Sjmmv } 491698d5317Sjmmv 492698d5317Sjmmv /* Write string with length limit (-1 for unlimited). */ 49399e242abSchristos void 49499e242abSchristos screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, 495e9a2d6faSchristos const struct grid_cell *gcp, const char *fmt, ...) 496698d5317Sjmmv { 497698d5317Sjmmv va_list ap; 498698d5317Sjmmv 499698d5317Sjmmv va_start(ap, fmt); 500e9a2d6faSchristos screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); 501698d5317Sjmmv va_end(ap); 502698d5317Sjmmv } 503698d5317Sjmmv 504698d5317Sjmmv void 505698d5317Sjmmv screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, 506e9a2d6faSchristos const struct grid_cell *gcp, const char *fmt, va_list ap) 507698d5317Sjmmv { 508e9a2d6faSchristos struct grid_cell gc; 509e9a2d6faSchristos struct utf8_data *ud = &gc.data; 510698d5317Sjmmv char *msg; 511f26e8bc9Schristos u_char *ptr; 512698d5317Sjmmv size_t left, size = 0; 513f26e8bc9Schristos enum utf8_state more; 514698d5317Sjmmv 515e9a2d6faSchristos memcpy(&gc, gcp, sizeof gc); 516698d5317Sjmmv xvasprintf(&msg, fmt, ap); 517698d5317Sjmmv 518f26e8bc9Schristos ptr = (u_char *)msg; 519698d5317Sjmmv while (*ptr != '\0') { 520e9a2d6faSchristos if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { 521698d5317Sjmmv ptr++; 522698d5317Sjmmv 523f26e8bc9Schristos left = strlen((char *)ptr); 524e9a2d6faSchristos if (left < (size_t)ud->size - 1) 525698d5317Sjmmv break; 526e9a2d6faSchristos while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) 527698d5317Sjmmv ptr++; 528698d5317Sjmmv ptr++; 529698d5317Sjmmv 530e9a2d6faSchristos if (more != UTF8_DONE) 531e9a2d6faSchristos continue; 532e9a2d6faSchristos if (maxlen > 0 && size + ud->width > (size_t)maxlen) { 533698d5317Sjmmv while (size < (size_t)maxlen) { 534e9a2d6faSchristos screen_write_putc(ctx, &gc, ' '); 535698d5317Sjmmv size++; 536698d5317Sjmmv } 537698d5317Sjmmv break; 538698d5317Sjmmv } 539e9a2d6faSchristos size += ud->width; 540e9a2d6faSchristos screen_write_cell(ctx, &gc); 541698d5317Sjmmv } else { 542698d5317Sjmmv if (maxlen > 0 && size + 1 > (size_t)maxlen) 543698d5317Sjmmv break; 544698d5317Sjmmv 54561fba46bSchristos if (*ptr == '\001') 546e9a2d6faSchristos gc.attr ^= GRID_ATTR_CHARSET; 547e271dbb8Schristos else if (*ptr == '\n') { 548e271dbb8Schristos screen_write_linefeed(ctx, 0, 8); 549e271dbb8Schristos screen_write_carriagereturn(ctx); 550e271dbb8Schristos } else if (*ptr > 0x1f && *ptr < 0x7f) { 551698d5317Sjmmv size++; 552e9a2d6faSchristos screen_write_putc(ctx, &gc, *ptr); 55361fba46bSchristos } 554698d5317Sjmmv ptr++; 555698d5317Sjmmv } 556698d5317Sjmmv } 557698d5317Sjmmv 55861fba46bSchristos free(msg); 559698d5317Sjmmv } 560698d5317Sjmmv 561c7e17de0Schristos /* 562e271dbb8Schristos * Copy from another screen but without the selection stuff. Assumes the target 563e271dbb8Schristos * region is already big enough. 564c7e17de0Schristos */ 565c7e17de0Schristos void 566c7e17de0Schristos screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, 567c7e17de0Schristos u_int px, u_int py, u_int nx, u_int ny) 568c7e17de0Schristos { 569c7e17de0Schristos struct screen *s = ctx->s; 570c7e17de0Schristos struct grid *gd = src->grid; 571c7e17de0Schristos struct grid_cell gc; 572c7e17de0Schristos u_int xx, yy, cx, cy; 573c7e17de0Schristos 574c7e17de0Schristos if (nx == 0 || ny == 0) 575c7e17de0Schristos return; 576c7e17de0Schristos 577c7e17de0Schristos cy = s->cy; 578c7e17de0Schristos for (yy = py; yy < py + ny; yy++) { 579c7e17de0Schristos if (yy >= gd->hsize + gd->sy) 580c7e17de0Schristos break; 581c7e17de0Schristos cx = s->cx; 582c7e17de0Schristos for (xx = px; xx < px + nx; xx++) { 583c7e17de0Schristos if (xx >= grid_get_line(gd, yy)->cellsize) 584c7e17de0Schristos break; 585c7e17de0Schristos grid_get_cell(gd, xx, yy, &gc); 586c7e17de0Schristos if (xx + gc.data.width > px + nx) 587c7e17de0Schristos break; 588c7e17de0Schristos grid_view_set_cell(ctx->s->grid, cx, cy, &gc); 589c7e17de0Schristos cx++; 590c7e17de0Schristos } 591c7e17de0Schristos cy++; 592c7e17de0Schristos } 593c7e17de0Schristos } 594c7e17de0Schristos 595f844e94eSwiz /* Select character set for drawing border lines. */ 596f844e94eSwiz static void 597f844e94eSwiz screen_write_box_border_set(enum box_lines lines, int cell_type, 598f844e94eSwiz struct grid_cell *gc) 599f844e94eSwiz { 600f844e94eSwiz switch (lines) { 601f844e94eSwiz case BOX_LINES_NONE: 602f844e94eSwiz break; 603f844e94eSwiz case BOX_LINES_DOUBLE: 604f844e94eSwiz gc->attr &= ~GRID_ATTR_CHARSET; 605f844e94eSwiz utf8_copy(&gc->data, tty_acs_double_borders(cell_type)); 606f844e94eSwiz break; 607f844e94eSwiz case BOX_LINES_HEAVY: 608f844e94eSwiz gc->attr &= ~GRID_ATTR_CHARSET; 609f844e94eSwiz utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type)); 610f844e94eSwiz break; 611f844e94eSwiz case BOX_LINES_ROUNDED: 612f844e94eSwiz gc->attr &= ~GRID_ATTR_CHARSET; 613f844e94eSwiz utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type)); 614f844e94eSwiz break; 615f844e94eSwiz case BOX_LINES_SIMPLE: 616f844e94eSwiz gc->attr &= ~GRID_ATTR_CHARSET; 617f844e94eSwiz utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]); 618f844e94eSwiz break; 619f844e94eSwiz case BOX_LINES_PADDED: 620f844e94eSwiz gc->attr &= ~GRID_ATTR_CHARSET; 621f844e94eSwiz utf8_set(&gc->data, PADDED_BORDERS[cell_type]); 622f844e94eSwiz break; 623f844e94eSwiz case BOX_LINES_SINGLE: 624f844e94eSwiz case BOX_LINES_DEFAULT: 625f844e94eSwiz gc->attr |= GRID_ATTR_CHARSET; 626f844e94eSwiz utf8_set(&gc->data, CELL_BORDERS[cell_type]); 627f844e94eSwiz break; 628f844e94eSwiz } 629f844e94eSwiz } 630f844e94eSwiz 631fe99a117Schristos /* Draw a horizontal line on screen. */ 632fe99a117Schristos void 633f844e94eSwiz screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right, 634f844e94eSwiz enum box_lines lines, const struct grid_cell *border_gc) 635fe99a117Schristos { 636fe99a117Schristos struct screen *s = ctx->s; 637fe99a117Schristos struct grid_cell gc; 638fe99a117Schristos u_int cx, cy, i; 639fe99a117Schristos 640fe99a117Schristos cx = s->cx; 641fe99a117Schristos cy = s->cy; 642fe99a117Schristos 643f844e94eSwiz if (border_gc != NULL) 644f844e94eSwiz memcpy(&gc, border_gc, sizeof gc); 645f844e94eSwiz else 646fe99a117Schristos memcpy(&gc, &grid_default_cell, sizeof gc); 647fe99a117Schristos gc.attr |= GRID_ATTR_CHARSET; 648fe99a117Schristos 649f844e94eSwiz if (left) 650f844e94eSwiz screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc); 651f844e94eSwiz else 652f844e94eSwiz screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); 653f844e94eSwiz screen_write_cell(ctx, &gc); 654f844e94eSwiz 655f844e94eSwiz screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); 656fe99a117Schristos for (i = 1; i < nx - 1; i++) 657f844e94eSwiz screen_write_cell(ctx, &gc); 658f844e94eSwiz 659f844e94eSwiz if (right) 660f844e94eSwiz screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc); 661f844e94eSwiz else 662f844e94eSwiz screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); 663f844e94eSwiz screen_write_cell(ctx, &gc); 664fe99a117Schristos 6650a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 666fe99a117Schristos } 667fe99a117Schristos 66846548964Swiz /* Draw a vertical line on screen. */ 669fe99a117Schristos void 670fe99a117Schristos screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) 671fe99a117Schristos { 672fe99a117Schristos struct screen *s = ctx->s; 673fe99a117Schristos struct grid_cell gc; 674fe99a117Schristos u_int cx, cy, i; 675fe99a117Schristos 676fe99a117Schristos cx = s->cx; 677fe99a117Schristos cy = s->cy; 678fe99a117Schristos 679fe99a117Schristos memcpy(&gc, &grid_default_cell, sizeof gc); 680fe99a117Schristos gc.attr |= GRID_ATTR_CHARSET; 681fe99a117Schristos 682fe99a117Schristos screen_write_putc(ctx, &gc, top ? 'w' : 'x'); 683fe99a117Schristos for (i = 1; i < ny - 1; i++) { 6840a274e86Schristos screen_write_set_cursor(ctx, cx, cy + i); 685fe99a117Schristos screen_write_putc(ctx, &gc, 'x'); 686fe99a117Schristos } 6870a274e86Schristos screen_write_set_cursor(ctx, cx, cy + ny - 1); 688fe99a117Schristos screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); 689fe99a117Schristos 6900a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 691fe99a117Schristos } 692fe99a117Schristos 69330744affSchristos /* Draw a menu on screen. */ 69430744affSchristos void 695f844e94eSwiz screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice, 696f844e94eSwiz enum box_lines lines, const struct grid_cell *menu_gc, 697f844e94eSwiz const struct grid_cell *border_gc, const struct grid_cell *choice_gc) 69830744affSchristos { 69930744affSchristos struct screen *s = ctx->s; 700e271dbb8Schristos struct grid_cell default_gc; 701e271dbb8Schristos const struct grid_cell *gc = &default_gc; 702f844e94eSwiz u_int cx, cy, i, j, width = menu->width; 70330744affSchristos const char *name; 70430744affSchristos 70530744affSchristos cx = s->cx; 70630744affSchristos cy = s->cy; 70730744affSchristos 708f844e94eSwiz memcpy(&default_gc, menu_gc, sizeof default_gc); 70930744affSchristos 710f844e94eSwiz screen_write_box(ctx, menu->width + 4, menu->count + 2, lines, 711f844e94eSwiz border_gc, menu->title); 71230744affSchristos 71330744affSchristos for (i = 0; i < menu->count; i++) { 71430744affSchristos name = menu->items[i].name; 71530744affSchristos if (name == NULL) { 71630744affSchristos screen_write_cursormove(ctx, cx, cy + 1 + i, 0); 717f844e94eSwiz screen_write_hline(ctx, width + 4, 1, 1, lines, 718f844e94eSwiz border_gc); 719f844e94eSwiz continue; 720f844e94eSwiz } 721f844e94eSwiz 72230744affSchristos if (choice >= 0 && i == (u_int)choice && *name != '-') 723e271dbb8Schristos gc = choice_gc; 724f844e94eSwiz 725f844e94eSwiz screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0); 726f844e94eSwiz for (j = 0; j < width + 2; j++) 727e271dbb8Schristos screen_write_putc(ctx, gc, ' '); 728f844e94eSwiz 72930744affSchristos screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); 73030744affSchristos if (*name == '-') { 731e271dbb8Schristos default_gc.attr |= GRID_ATTR_DIM; 732f844e94eSwiz format_draw(ctx, gc, width, name + 1, NULL, 0); 733e271dbb8Schristos default_gc.attr &= ~GRID_ATTR_DIM; 734f844e94eSwiz continue; 73530744affSchristos } 736f844e94eSwiz 737f844e94eSwiz format_draw(ctx, gc, width, name, NULL, 0); 738f844e94eSwiz gc = &default_gc; 73930744affSchristos } 74030744affSchristos 74130744affSchristos screen_write_set_cursor(ctx, cx, cy); 74230744affSchristos } 74330744affSchristos 744fe99a117Schristos /* Draw a box on screen. */ 745fe99a117Schristos void 74646548964Swiz screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny, 74746548964Swiz enum box_lines lines, const struct grid_cell *gcp, const char *title) 748fe99a117Schristos { 749fe99a117Schristos struct screen *s = ctx->s; 750fe99a117Schristos struct grid_cell gc; 751fe99a117Schristos u_int cx, cy, i; 752fe99a117Schristos 753fe99a117Schristos cx = s->cx; 754fe99a117Schristos cy = s->cy; 755fe99a117Schristos 75646548964Swiz if (gcp != NULL) 75746548964Swiz memcpy(&gc, gcp, sizeof gc); 75846548964Swiz else 759fe99a117Schristos memcpy(&gc, &grid_default_cell, sizeof gc); 76046548964Swiz 761fe99a117Schristos gc.attr |= GRID_ATTR_CHARSET; 76246548964Swiz gc.flags |= GRID_FLAG_NOPALETTE; 763fe99a117Schristos 76446548964Swiz /* Draw top border */ 76546548964Swiz screen_write_box_border_set(lines, CELL_TOPLEFT, &gc); 76646548964Swiz screen_write_cell(ctx, &gc); 76746548964Swiz screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); 768fe99a117Schristos for (i = 1; i < nx - 1; i++) 76946548964Swiz screen_write_cell(ctx, &gc); 77046548964Swiz screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc); 77146548964Swiz screen_write_cell(ctx, &gc); 772fe99a117Schristos 77346548964Swiz /* Draw bottom border */ 7740a274e86Schristos screen_write_set_cursor(ctx, cx, cy + ny - 1); 77546548964Swiz screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc); 77646548964Swiz screen_write_cell(ctx, &gc); 77746548964Swiz screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc); 778fe99a117Schristos for (i = 1; i < nx - 1; i++) 77946548964Swiz screen_write_cell(ctx, &gc); 78046548964Swiz screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc); 78146548964Swiz screen_write_cell(ctx, &gc); 782fe99a117Schristos 78346548964Swiz /* Draw sides */ 78446548964Swiz screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc); 785fe99a117Schristos for (i = 1; i < ny - 1; i++) { 78646548964Swiz /* left side */ 7870a274e86Schristos screen_write_set_cursor(ctx, cx, cy + i); 78846548964Swiz screen_write_cell(ctx, &gc); 78946548964Swiz /* right side */ 7900a274e86Schristos screen_write_set_cursor(ctx, cx + nx - 1, cy + i); 79146548964Swiz screen_write_cell(ctx, &gc); 79246548964Swiz } 79346548964Swiz 79446548964Swiz if (title != NULL) { 79546548964Swiz gc.attr &= ~GRID_ATTR_CHARSET; 79646548964Swiz screen_write_cursormove(ctx, cx + 2, cy, 0); 79746548964Swiz format_draw(ctx, &gc, nx - 4, title, NULL, 0); 798fe99a117Schristos } 799fe99a117Schristos 8000a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 801fe99a117Schristos } 802fe99a117Schristos 803c7e17de0Schristos /* 804c7e17de0Schristos * Write a preview version of a window. Assumes target area is big enough and 805c7e17de0Schristos * already cleared. 806c7e17de0Schristos */ 807fe99a117Schristos void 808fe99a117Schristos screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, 809fe99a117Schristos u_int ny) 810fe99a117Schristos { 811fe99a117Schristos struct screen *s = ctx->s; 812fe99a117Schristos struct grid_cell gc; 813fe99a117Schristos u_int cx, cy, px, py; 814fe99a117Schristos 815fe99a117Schristos cx = s->cx; 816fe99a117Schristos cy = s->cy; 817fe99a117Schristos 818fe99a117Schristos /* 819fe99a117Schristos * If the cursor is on, pick the area around the cursor, otherwise use 820fe99a117Schristos * the top left. 821fe99a117Schristos */ 822fe99a117Schristos if (src->mode & MODE_CURSOR) { 823fe99a117Schristos px = src->cx; 824fe99a117Schristos if (px < nx / 3) 825fe99a117Schristos px = 0; 826fe99a117Schristos else 827fe99a117Schristos px = px - nx / 3; 828fe99a117Schristos if (px + nx > screen_size_x(src)) { 829fe99a117Schristos if (nx > screen_size_x(src)) 830fe99a117Schristos px = 0; 831fe99a117Schristos else 832fe99a117Schristos px = screen_size_x(src) - nx; 833fe99a117Schristos } 834fe99a117Schristos py = src->cy; 835fe99a117Schristos if (py < ny / 3) 836fe99a117Schristos py = 0; 837fe99a117Schristos else 838fe99a117Schristos py = py - ny / 3; 839fe99a117Schristos if (py + ny > screen_size_y(src)) { 840fe99a117Schristos if (ny > screen_size_y(src)) 841fe99a117Schristos py = 0; 842fe99a117Schristos else 843fe99a117Schristos py = screen_size_y(src) - ny; 844fe99a117Schristos } 845fe99a117Schristos } else { 846fe99a117Schristos px = 0; 847fe99a117Schristos py = 0; 848fe99a117Schristos } 849fe99a117Schristos 850c7e17de0Schristos screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny); 851fe99a117Schristos 852fe99a117Schristos if (src->mode & MODE_CURSOR) { 853fe99a117Schristos grid_view_get_cell(src->grid, src->cx, src->cy, &gc); 854fe99a117Schristos gc.attr |= GRID_ATTR_REVERSE; 8550a274e86Schristos screen_write_set_cursor(ctx, cx + (src->cx - px), 856fe99a117Schristos cy + (src->cy - py)); 857fe99a117Schristos screen_write_cell(ctx, &gc); 858fe99a117Schristos } 859fe99a117Schristos } 860fe99a117Schristos 86161fba46bSchristos /* Set a mode. */ 86261fba46bSchristos void 86361fba46bSchristos screen_write_mode_set(struct screen_write_ctx *ctx, int mode) 86461fba46bSchristos { 86561fba46bSchristos struct screen *s = ctx->s; 86661fba46bSchristos 86761fba46bSchristos s->mode |= mode; 86846548964Swiz 86946548964Swiz if (log_get_level() != 0) 87046548964Swiz log_debug("%s: %s", __func__, screen_mode_to_string(mode)); 87161fba46bSchristos } 87261fba46bSchristos 87361fba46bSchristos /* Clear a mode. */ 87461fba46bSchristos void 87561fba46bSchristos screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) 87661fba46bSchristos { 87761fba46bSchristos struct screen *s = ctx->s; 87861fba46bSchristos 87961fba46bSchristos s->mode &= ~mode; 88046548964Swiz 88146548964Swiz if (log_get_level() != 0) 88246548964Swiz log_debug("%s: %s", __func__, screen_mode_to_string(mode)); 883698d5317Sjmmv } 884698d5317Sjmmv 885698d5317Sjmmv /* Cursor up by ny. */ 886698d5317Sjmmv void 887698d5317Sjmmv screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) 888698d5317Sjmmv { 889698d5317Sjmmv struct screen *s = ctx->s; 8900a274e86Schristos u_int cx = s->cx, cy = s->cy; 891698d5317Sjmmv 892698d5317Sjmmv if (ny == 0) 893698d5317Sjmmv ny = 1; 894698d5317Sjmmv 8950a274e86Schristos if (cy < s->rupper) { 896698d5317Sjmmv /* Above region. */ 8970a274e86Schristos if (ny > cy) 8980a274e86Schristos ny = cy; 899698d5317Sjmmv } else { 900698d5317Sjmmv /* Below region. */ 9010a274e86Schristos if (ny > cy - s->rupper) 9020a274e86Schristos ny = cy - s->rupper; 903698d5317Sjmmv } 9040a274e86Schristos if (cx == screen_size_x(s)) 9050a274e86Schristos cx--; 906698d5317Sjmmv 9070a274e86Schristos cy -= ny; 9080a274e86Schristos 9090a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 910698d5317Sjmmv } 911698d5317Sjmmv 912698d5317Sjmmv /* Cursor down by ny. */ 913698d5317Sjmmv void 914698d5317Sjmmv screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) 915698d5317Sjmmv { 916698d5317Sjmmv struct screen *s = ctx->s; 9170a274e86Schristos u_int cx = s->cx, cy = s->cy; 918698d5317Sjmmv 919698d5317Sjmmv if (ny == 0) 920698d5317Sjmmv ny = 1; 921698d5317Sjmmv 9220a274e86Schristos if (cy > s->rlower) { 923698d5317Sjmmv /* Below region. */ 9240a274e86Schristos if (ny > screen_size_y(s) - 1 - cy) 9250a274e86Schristos ny = screen_size_y(s) - 1 - cy; 926698d5317Sjmmv } else { 927698d5317Sjmmv /* Above region. */ 9280a274e86Schristos if (ny > s->rlower - cy) 9290a274e86Schristos ny = s->rlower - cy; 930698d5317Sjmmv } 9310a274e86Schristos if (cx == screen_size_x(s)) 9320a274e86Schristos cx--; 9330a274e86Schristos else if (ny == 0) 934698d5317Sjmmv return; 935698d5317Sjmmv 9360a274e86Schristos cy += ny; 9370a274e86Schristos 9380a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 939698d5317Sjmmv } 940698d5317Sjmmv 941698d5317Sjmmv /* Cursor right by nx. */ 942698d5317Sjmmv void 943698d5317Sjmmv screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) 944698d5317Sjmmv { 945698d5317Sjmmv struct screen *s = ctx->s; 9460a274e86Schristos u_int cx = s->cx, cy = s->cy; 947698d5317Sjmmv 948698d5317Sjmmv if (nx == 0) 949698d5317Sjmmv nx = 1; 950698d5317Sjmmv 9510a274e86Schristos if (nx > screen_size_x(s) - 1 - cx) 9520a274e86Schristos nx = screen_size_x(s) - 1 - cx; 953698d5317Sjmmv if (nx == 0) 954698d5317Sjmmv return; 955698d5317Sjmmv 9560a274e86Schristos cx += nx; 9570a274e86Schristos 9580a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 959698d5317Sjmmv } 960698d5317Sjmmv 961698d5317Sjmmv /* Cursor left by nx. */ 962698d5317Sjmmv void 963698d5317Sjmmv screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) 964698d5317Sjmmv { 965698d5317Sjmmv struct screen *s = ctx->s; 9660a274e86Schristos u_int cx = s->cx, cy = s->cy; 967698d5317Sjmmv 968698d5317Sjmmv if (nx == 0) 969698d5317Sjmmv nx = 1; 970698d5317Sjmmv 9710a274e86Schristos if (nx > cx) 9720a274e86Schristos nx = cx; 973698d5317Sjmmv if (nx == 0) 974698d5317Sjmmv return; 975698d5317Sjmmv 9760a274e86Schristos cx -= nx; 9770a274e86Schristos 9780a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 979698d5317Sjmmv } 980698d5317Sjmmv 981698d5317Sjmmv /* Backspace; cursor left unless at start of wrapped line when can move up. */ 982698d5317Sjmmv void 983698d5317Sjmmv screen_write_backspace(struct screen_write_ctx *ctx) 984698d5317Sjmmv { 985698d5317Sjmmv struct screen *s = ctx->s; 986698d5317Sjmmv struct grid_line *gl; 9870a274e86Schristos u_int cx = s->cx, cy = s->cy; 988698d5317Sjmmv 9890a274e86Schristos if (cx == 0) { 9900a274e86Schristos if (cy == 0) 991698d5317Sjmmv return; 9920a274e86Schristos gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); 993698d5317Sjmmv if (gl->flags & GRID_LINE_WRAPPED) { 9940a274e86Schristos cy--; 9950a274e86Schristos cx = screen_size_x(s) - 1; 996698d5317Sjmmv } 997698d5317Sjmmv } else 9980a274e86Schristos cx--; 9990a274e86Schristos 10000a274e86Schristos screen_write_set_cursor(ctx, cx, cy); 1001698d5317Sjmmv } 1002698d5317Sjmmv 1003698d5317Sjmmv /* VT100 alignment test. */ 1004698d5317Sjmmv void 1005698d5317Sjmmv screen_write_alignmenttest(struct screen_write_ctx *ctx) 1006698d5317Sjmmv { 1007698d5317Sjmmv struct screen *s = ctx->s; 1008698d5317Sjmmv struct tty_ctx ttyctx; 1009698d5317Sjmmv struct grid_cell gc; 1010698d5317Sjmmv u_int xx, yy; 1011698d5317Sjmmv 1012698d5317Sjmmv memcpy(&gc, &grid_default_cell, sizeof gc); 1013f26e8bc9Schristos utf8_set(&gc.data, 'E'); 1014698d5317Sjmmv 1015f844e94eSwiz #ifdef ENABLE_SIXEL 1016f844e94eSwiz if (image_free_all(s) && ctx->wp != NULL) 1017f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1018f844e94eSwiz #endif 1019f844e94eSwiz 1020698d5317Sjmmv for (yy = 0; yy < screen_size_y(s); yy++) { 1021698d5317Sjmmv for (xx = 0; xx < screen_size_x(s); xx++) 1022698d5317Sjmmv grid_view_set_cell(s->grid, xx, yy, &gc); 1023698d5317Sjmmv } 1024698d5317Sjmmv 10250a274e86Schristos screen_write_set_cursor(ctx, 0, 0); 1026698d5317Sjmmv 1027698d5317Sjmmv s->rupper = 0; 1028698d5317Sjmmv s->rlower = screen_size_y(s) - 1; 1029698d5317Sjmmv 1030e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 10310a274e86Schristos 1032e9a2d6faSchristos screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); 1033698d5317Sjmmv tty_write(tty_cmd_alignmenttest, &ttyctx); 1034698d5317Sjmmv } 1035698d5317Sjmmv 1036698d5317Sjmmv /* Insert nx characters. */ 1037698d5317Sjmmv void 1038e9a2d6faSchristos screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 1039698d5317Sjmmv { 1040698d5317Sjmmv struct screen *s = ctx->s; 1041698d5317Sjmmv struct tty_ctx ttyctx; 1042698d5317Sjmmv 1043698d5317Sjmmv if (nx == 0) 1044698d5317Sjmmv nx = 1; 1045698d5317Sjmmv 1046698d5317Sjmmv if (nx > screen_size_x(s) - s->cx) 1047698d5317Sjmmv nx = screen_size_x(s) - s->cx; 1048698d5317Sjmmv if (nx == 0) 1049698d5317Sjmmv return; 1050698d5317Sjmmv 1051e9a2d6faSchristos if (s->cx > screen_size_x(s) - 1) 1052e9a2d6faSchristos return; 1053698d5317Sjmmv 1054f844e94eSwiz #ifdef ENABLE_SIXEL 1055f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1056f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1057f844e94eSwiz #endif 1058f844e94eSwiz 1059e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 1060e9a2d6faSchristos ttyctx.bg = bg; 1061698d5317Sjmmv 1062e9a2d6faSchristos grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); 1063e9a2d6faSchristos 1064e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1065698d5317Sjmmv ttyctx.num = nx; 1066698d5317Sjmmv tty_write(tty_cmd_insertcharacter, &ttyctx); 1067698d5317Sjmmv } 1068698d5317Sjmmv 1069698d5317Sjmmv /* Delete nx characters. */ 1070698d5317Sjmmv void 1071e9a2d6faSchristos screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 1072698d5317Sjmmv { 1073698d5317Sjmmv struct screen *s = ctx->s; 1074698d5317Sjmmv struct tty_ctx ttyctx; 1075698d5317Sjmmv 1076698d5317Sjmmv if (nx == 0) 1077698d5317Sjmmv nx = 1; 1078698d5317Sjmmv 1079698d5317Sjmmv if (nx > screen_size_x(s) - s->cx) 1080698d5317Sjmmv nx = screen_size_x(s) - s->cx; 1081698d5317Sjmmv if (nx == 0) 1082698d5317Sjmmv return; 1083698d5317Sjmmv 1084e9a2d6faSchristos if (s->cx > screen_size_x(s) - 1) 1085e9a2d6faSchristos return; 1086698d5317Sjmmv 1087f844e94eSwiz #ifdef ENABLE_SIXEL 1088f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1089f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1090f844e94eSwiz #endif 1091f844e94eSwiz 1092e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 1093e9a2d6faSchristos ttyctx.bg = bg; 1094698d5317Sjmmv 1095e9a2d6faSchristos grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); 1096e9a2d6faSchristos 1097e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1098698d5317Sjmmv ttyctx.num = nx; 1099698d5317Sjmmv tty_write(tty_cmd_deletecharacter, &ttyctx); 1100698d5317Sjmmv } 1101698d5317Sjmmv 110261fba46bSchristos /* Clear nx characters. */ 110361fba46bSchristos void 1104fe99a117Schristos screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) 110561fba46bSchristos { 110661fba46bSchristos struct screen *s = ctx->s; 110761fba46bSchristos struct tty_ctx ttyctx; 110861fba46bSchristos 110961fba46bSchristos if (nx == 0) 111061fba46bSchristos nx = 1; 111161fba46bSchristos 111261fba46bSchristos if (nx > screen_size_x(s) - s->cx) 111361fba46bSchristos nx = screen_size_x(s) - s->cx; 111461fba46bSchristos if (nx == 0) 111561fba46bSchristos return; 111661fba46bSchristos 1117e9a2d6faSchristos if (s->cx > screen_size_x(s) - 1) 1118e9a2d6faSchristos return; 111961fba46bSchristos 1120f844e94eSwiz #ifdef ENABLE_SIXEL 1121f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1122f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1123f844e94eSwiz #endif 1124f844e94eSwiz 1125e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 1126fe99a117Schristos ttyctx.bg = bg; 112761fba46bSchristos 1128fe99a117Schristos grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); 1129e9a2d6faSchristos 1130e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 113161fba46bSchristos ttyctx.num = nx; 113261fba46bSchristos tty_write(tty_cmd_clearcharacter, &ttyctx); 113361fba46bSchristos } 113461fba46bSchristos 1135698d5317Sjmmv /* Insert ny lines. */ 1136698d5317Sjmmv void 1137e9a2d6faSchristos screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 1138698d5317Sjmmv { 1139698d5317Sjmmv struct screen *s = ctx->s; 1140e9a2d6faSchristos struct grid *gd = s->grid; 1141698d5317Sjmmv struct tty_ctx ttyctx; 1142698d5317Sjmmv 1143f844e94eSwiz #ifdef ENABLE_SIXEL 1144f844e94eSwiz u_int sy = screen_size_y(s); 1145f844e94eSwiz #endif 1146f844e94eSwiz 1147698d5317Sjmmv if (ny == 0) 1148698d5317Sjmmv ny = 1; 1149698d5317Sjmmv 1150f844e94eSwiz #ifdef ENABLE_SIXEL 1151f844e94eSwiz if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) 1152f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1153f844e94eSwiz #endif 1154f844e94eSwiz 1155698d5317Sjmmv if (s->cy < s->rupper || s->cy > s->rlower) { 1156698d5317Sjmmv if (ny > screen_size_y(s) - s->cy) 1157698d5317Sjmmv ny = screen_size_y(s) - s->cy; 1158698d5317Sjmmv if (ny == 0) 1159698d5317Sjmmv return; 1160698d5317Sjmmv 1161e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1162e9a2d6faSchristos ttyctx.bg = bg; 1163698d5317Sjmmv 1164e9a2d6faSchristos grid_view_insert_lines(gd, s->cy, ny, bg); 1165698d5317Sjmmv 1166e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1167698d5317Sjmmv ttyctx.num = ny; 1168698d5317Sjmmv tty_write(tty_cmd_insertline, &ttyctx); 1169698d5317Sjmmv return; 1170698d5317Sjmmv } 1171698d5317Sjmmv 1172698d5317Sjmmv if (ny > s->rlower + 1 - s->cy) 1173698d5317Sjmmv ny = s->rlower + 1 - s->cy; 1174698d5317Sjmmv if (ny == 0) 1175698d5317Sjmmv return; 1176698d5317Sjmmv 1177e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1178e9a2d6faSchristos ttyctx.bg = bg; 1179698d5317Sjmmv 1180698d5317Sjmmv if (s->cy < s->rupper || s->cy > s->rlower) 1181e9a2d6faSchristos grid_view_insert_lines(gd, s->cy, ny, bg); 1182698d5317Sjmmv else 1183e9a2d6faSchristos grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); 1184698d5317Sjmmv 1185e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1186e271dbb8Schristos 1187698d5317Sjmmv ttyctx.num = ny; 1188698d5317Sjmmv tty_write(tty_cmd_insertline, &ttyctx); 1189698d5317Sjmmv } 1190698d5317Sjmmv 1191698d5317Sjmmv /* Delete ny lines. */ 1192698d5317Sjmmv void 1193e9a2d6faSchristos screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) 1194698d5317Sjmmv { 1195698d5317Sjmmv struct screen *s = ctx->s; 1196e9a2d6faSchristos struct grid *gd = s->grid; 1197698d5317Sjmmv struct tty_ctx ttyctx; 1198f844e94eSwiz u_int sy = screen_size_y(s); 1199698d5317Sjmmv 1200698d5317Sjmmv if (ny == 0) 1201698d5317Sjmmv ny = 1; 1202698d5317Sjmmv 1203f844e94eSwiz #ifdef ENABLE_SIXEL 1204f844e94eSwiz if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) 1205f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1206f844e94eSwiz #endif 1207f844e94eSwiz 1208698d5317Sjmmv if (s->cy < s->rupper || s->cy > s->rlower) { 1209f844e94eSwiz if (ny > sy - s->cy) 1210f844e94eSwiz ny = sy - s->cy; 1211698d5317Sjmmv if (ny == 0) 1212698d5317Sjmmv return; 1213698d5317Sjmmv 1214e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1215e9a2d6faSchristos ttyctx.bg = bg; 1216698d5317Sjmmv 1217e9a2d6faSchristos grid_view_delete_lines(gd, s->cy, ny, bg); 1218698d5317Sjmmv 1219e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1220698d5317Sjmmv ttyctx.num = ny; 1221698d5317Sjmmv tty_write(tty_cmd_deleteline, &ttyctx); 1222698d5317Sjmmv return; 1223698d5317Sjmmv } 1224698d5317Sjmmv 1225698d5317Sjmmv if (ny > s->rlower + 1 - s->cy) 1226698d5317Sjmmv ny = s->rlower + 1 - s->cy; 1227698d5317Sjmmv if (ny == 0) 1228698d5317Sjmmv return; 1229698d5317Sjmmv 1230e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1231e9a2d6faSchristos ttyctx.bg = bg; 1232698d5317Sjmmv 1233698d5317Sjmmv if (s->cy < s->rupper || s->cy > s->rlower) 1234e9a2d6faSchristos grid_view_delete_lines(gd, s->cy, ny, bg); 1235698d5317Sjmmv else 1236e9a2d6faSchristos grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); 1237698d5317Sjmmv 1238e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1239698d5317Sjmmv ttyctx.num = ny; 1240698d5317Sjmmv tty_write(tty_cmd_deleteline, &ttyctx); 1241698d5317Sjmmv } 1242698d5317Sjmmv 1243698d5317Sjmmv /* Clear line at cursor. */ 1244698d5317Sjmmv void 1245e9a2d6faSchristos screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) 1246698d5317Sjmmv { 1247698d5317Sjmmv struct screen *s = ctx->s; 1248e9a2d6faSchristos struct grid_line *gl; 1249e9a2d6faSchristos u_int sx = screen_size_x(s); 1250e271dbb8Schristos struct screen_write_citem *ci = ctx->item; 1251698d5317Sjmmv 1252c7e17de0Schristos gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 12530a274e86Schristos if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) 1254e9a2d6faSchristos return; 1255698d5317Sjmmv 1256f844e94eSwiz #ifdef ENABLE_SIXEL 1257f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1258f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1259f844e94eSwiz #endif 1260f844e94eSwiz 1261e9a2d6faSchristos grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1262e9a2d6faSchristos 1263e9a2d6faSchristos screen_write_collect_clear(ctx, s->cy, 1); 1264e271dbb8Schristos ci->x = 0; 1265e271dbb8Schristos ci->used = sx; 1266e271dbb8Schristos ci->type = CLEAR; 1267e271dbb8Schristos ci->bg = bg; 1268e271dbb8Schristos TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); 1269e271dbb8Schristos ctx->item = screen_write_get_citem(); 1270698d5317Sjmmv } 1271698d5317Sjmmv 1272698d5317Sjmmv /* Clear to end of line from cursor. */ 1273698d5317Sjmmv void 1274e9a2d6faSchristos screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) 1275698d5317Sjmmv { 1276698d5317Sjmmv struct screen *s = ctx->s; 1277e9a2d6faSchristos struct grid_line *gl; 1278e9a2d6faSchristos u_int sx = screen_size_x(s); 1279e271dbb8Schristos struct screen_write_citem *ci = ctx->item, *before; 1280e271dbb8Schristos 1281e271dbb8Schristos if (s->cx == 0) { 1282e271dbb8Schristos screen_write_clearline(ctx, bg); 1283e271dbb8Schristos return; 1284e271dbb8Schristos } 1285698d5317Sjmmv 1286c7e17de0Schristos gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 12870a274e86Schristos if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) 1288e9a2d6faSchristos return; 1289698d5317Sjmmv 1290f844e94eSwiz #ifdef ENABLE_SIXEL 1291f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1292f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1293f844e94eSwiz #endif 1294f844e94eSwiz 1295e9a2d6faSchristos grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); 1296698d5317Sjmmv 1297e271dbb8Schristos before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL); 1298e271dbb8Schristos ci->x = s->cx; 1299e271dbb8Schristos ci->used = sx - s->cx; 1300e271dbb8Schristos ci->type = CLEAR; 1301e271dbb8Schristos ci->bg = bg; 1302e271dbb8Schristos if (before == NULL) 1303e271dbb8Schristos TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); 1304e271dbb8Schristos else 1305e271dbb8Schristos TAILQ_INSERT_BEFORE(before, ci, entry); 1306e271dbb8Schristos ctx->item = screen_write_get_citem(); 1307698d5317Sjmmv } 1308698d5317Sjmmv 1309698d5317Sjmmv /* Clear to start of line from cursor. */ 1310698d5317Sjmmv void 1311e9a2d6faSchristos screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) 1312698d5317Sjmmv { 1313698d5317Sjmmv struct screen *s = ctx->s; 1314e9a2d6faSchristos u_int sx = screen_size_x(s); 1315e271dbb8Schristos struct screen_write_citem *ci = ctx->item, *before; 1316698d5317Sjmmv 1317e271dbb8Schristos if (s->cx >= sx - 1) { 1318e271dbb8Schristos screen_write_clearline(ctx, bg); 1319e271dbb8Schristos return; 1320e271dbb8Schristos } 1321698d5317Sjmmv 1322f844e94eSwiz #ifdef ENABLE_SIXEL 1323f844e94eSwiz if (image_check_line(s, s->cy, 1) && ctx->wp != NULL) 1324f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1325f844e94eSwiz #endif 1326f844e94eSwiz 1327698d5317Sjmmv if (s->cx > sx - 1) 1328e9a2d6faSchristos grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1329698d5317Sjmmv else 1330e9a2d6faSchristos grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 1331698d5317Sjmmv 1332e271dbb8Schristos before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL); 1333e271dbb8Schristos ci->x = 0; 1334e271dbb8Schristos ci->used = s->cx + 1; 1335e271dbb8Schristos ci->type = CLEAR; 1336e271dbb8Schristos ci->bg = bg; 1337e271dbb8Schristos if (before == NULL) 1338e271dbb8Schristos TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry); 1339e271dbb8Schristos else 1340e271dbb8Schristos TAILQ_INSERT_BEFORE(before, ci, entry); 1341e271dbb8Schristos ctx->item = screen_write_get_citem(); 1342698d5317Sjmmv } 1343698d5317Sjmmv 1344698d5317Sjmmv /* Move cursor to px,py. */ 1345698d5317Sjmmv void 13460a274e86Schristos screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, 13470a274e86Schristos int origin) 1348698d5317Sjmmv { 1349698d5317Sjmmv struct screen *s = ctx->s; 1350698d5317Sjmmv 13510a274e86Schristos if (origin && py != -1 && (s->mode & MODE_ORIGIN)) { 13520a274e86Schristos if ((u_int)py > s->rlower - s->rupper) 13530a274e86Schristos py = s->rlower; 13540a274e86Schristos else 13550a274e86Schristos py += s->rupper; 13560a274e86Schristos } 13570a274e86Schristos 13580a274e86Schristos if (px != -1 && (u_int)px > screen_size_x(s) - 1) 1359698d5317Sjmmv px = screen_size_x(s) - 1; 13600a274e86Schristos if (py != -1 && (u_int)py > screen_size_y(s) - 1) 1361698d5317Sjmmv py = screen_size_y(s) - 1; 1362698d5317Sjmmv 1363e271dbb8Schristos log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py); 13640a274e86Schristos screen_write_set_cursor(ctx, px, py); 1365698d5317Sjmmv } 1366698d5317Sjmmv 1367698d5317Sjmmv /* Reverse index (up with scroll). */ 1368698d5317Sjmmv void 1369fe99a117Schristos screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) 1370698d5317Sjmmv { 1371698d5317Sjmmv struct screen *s = ctx->s; 1372698d5317Sjmmv struct tty_ctx ttyctx; 1373698d5317Sjmmv 1374e271dbb8Schristos if (s->cy == s->rupper) { 1375f844e94eSwiz #ifdef ENABLE_SIXEL 1376f844e94eSwiz if (image_free_all(s) && ctx->wp != NULL) 1377f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1378f844e94eSwiz #endif 1379f844e94eSwiz 1380e271dbb8Schristos grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); 1381e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1382e271dbb8Schristos 1383e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1384fe99a117Schristos ttyctx.bg = bg; 1385698d5317Sjmmv 1386e271dbb8Schristos tty_write(tty_cmd_reverseindex, &ttyctx); 1387e271dbb8Schristos } else if (s->cy > 0) 13880a274e86Schristos screen_write_set_cursor(ctx, -1, s->cy - 1); 1389698d5317Sjmmv 1390698d5317Sjmmv } 1391698d5317Sjmmv 1392698d5317Sjmmv /* Set scroll region. */ 1393698d5317Sjmmv void 1394f26e8bc9Schristos screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, 1395f26e8bc9Schristos u_int rlower) 1396698d5317Sjmmv { 1397698d5317Sjmmv struct screen *s = ctx->s; 1398698d5317Sjmmv 1399698d5317Sjmmv if (rupper > screen_size_y(s) - 1) 1400698d5317Sjmmv rupper = screen_size_y(s) - 1; 1401698d5317Sjmmv if (rlower > screen_size_y(s) - 1) 1402698d5317Sjmmv rlower = screen_size_y(s) - 1; 1403698d5317Sjmmv if (rupper >= rlower) /* cannot be one line */ 1404698d5317Sjmmv return; 1405698d5317Sjmmv 1406e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1407e9a2d6faSchristos 1408698d5317Sjmmv /* Cursor moves to top-left. */ 14090a274e86Schristos screen_write_set_cursor(ctx, 0, 0); 1410698d5317Sjmmv 1411698d5317Sjmmv s->rupper = rupper; 1412698d5317Sjmmv s->rlower = rlower; 1413698d5317Sjmmv } 1414698d5317Sjmmv 1415698d5317Sjmmv /* Line feed. */ 1416698d5317Sjmmv void 1417fe99a117Schristos screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) 1418698d5317Sjmmv { 1419698d5317Sjmmv struct screen *s = ctx->s; 1420e9a2d6faSchristos struct grid *gd = s->grid; 1421698d5317Sjmmv struct grid_line *gl; 1422f844e94eSwiz #ifdef ENABLE_SIXEL 1423f844e94eSwiz int redraw = 0; 1424f844e94eSwiz #endif 1425f844e94eSwiz u_int rupper = s->rupper, rlower = s->rlower; 1426698d5317Sjmmv 1427c7e17de0Schristos gl = grid_get_line(gd, gd->hsize + s->cy); 1428698d5317Sjmmv if (wrapped) 1429698d5317Sjmmv gl->flags |= GRID_LINE_WRAPPED; 1430698d5317Sjmmv 1431e9a2d6faSchristos log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1432f844e94eSwiz rupper, rlower); 1433698d5317Sjmmv 1434fe99a117Schristos if (bg != ctx->bg) { 1435e271dbb8Schristos screen_write_collect_flush(ctx, 1, __func__); 1436fe99a117Schristos ctx->bg = bg; 1437fe99a117Schristos } 1438fe99a117Schristos 1439e9a2d6faSchristos if (s->cy == s->rlower) { 1440f844e94eSwiz #ifdef ENABLE_SIXEL 1441f844e94eSwiz if (rlower == screen_size_y(s) - 1) 1442f844e94eSwiz redraw = image_scroll_up(s, 1); 1443f844e94eSwiz else 1444f844e94eSwiz redraw = image_check_line(s, rupper, rlower - rupper); 1445f844e94eSwiz if (redraw && ctx->wp != NULL) 1446f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1447f844e94eSwiz #endif 1448fe99a117Schristos grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1449e271dbb8Schristos screen_write_collect_scroll(ctx, bg); 1450e9a2d6faSchristos ctx->scrolled++; 1451e9a2d6faSchristos } else if (s->cy < screen_size_y(s) - 1) 14520a274e86Schristos screen_write_set_cursor(ctx, -1, s->cy + 1); 1453e9a2d6faSchristos } 1454e9a2d6faSchristos 1455e9a2d6faSchristos /* Scroll up. */ 1456e9a2d6faSchristos void 1457fe99a117Schristos screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) 1458e9a2d6faSchristos { 1459e9a2d6faSchristos struct screen *s = ctx->s; 1460e9a2d6faSchristos struct grid *gd = s->grid; 1461e9a2d6faSchristos u_int i; 1462e9a2d6faSchristos 1463e9a2d6faSchristos if (lines == 0) 1464e9a2d6faSchristos lines = 1; 1465e9a2d6faSchristos else if (lines > s->rlower - s->rupper + 1) 1466e9a2d6faSchristos lines = s->rlower - s->rupper + 1; 1467e9a2d6faSchristos 1468fe99a117Schristos if (bg != ctx->bg) { 1469e271dbb8Schristos screen_write_collect_flush(ctx, 1, __func__); 1470fe99a117Schristos ctx->bg = bg; 1471fe99a117Schristos } 1472fe99a117Schristos 1473f844e94eSwiz #ifdef ENABLE_SIXEL 1474f844e94eSwiz if (image_scroll_up(s, lines) && ctx->wp != NULL) 1475f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1476f844e94eSwiz #endif 1477f844e94eSwiz 1478e9a2d6faSchristos for (i = 0; i < lines; i++) { 1479fe99a117Schristos grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); 1480e271dbb8Schristos screen_write_collect_scroll(ctx, bg); 1481e9a2d6faSchristos } 1482e9a2d6faSchristos ctx->scrolled += lines; 1483698d5317Sjmmv } 1484698d5317Sjmmv 148530744affSchristos /* Scroll down. */ 148630744affSchristos void 148730744affSchristos screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) 148830744affSchristos { 148930744affSchristos struct screen *s = ctx->s; 149030744affSchristos struct grid *gd = s->grid; 149130744affSchristos struct tty_ctx ttyctx; 149230744affSchristos u_int i; 149330744affSchristos 1494e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 149530744affSchristos ttyctx.bg = bg; 149630744affSchristos 149730744affSchristos if (lines == 0) 149830744affSchristos lines = 1; 149930744affSchristos else if (lines > s->rlower - s->rupper + 1) 150030744affSchristos lines = s->rlower - s->rupper + 1; 150130744affSchristos 1502f844e94eSwiz #ifdef ENABLE_SIXEL 1503f844e94eSwiz if (image_free_all(s) && ctx->wp != NULL) 1504f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1505f844e94eSwiz #endif 1506f844e94eSwiz 150730744affSchristos for (i = 0; i < lines; i++) 150830744affSchristos grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); 150930744affSchristos 1510e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 151130744affSchristos ttyctx.num = lines; 151230744affSchristos tty_write(tty_cmd_scrolldown, &ttyctx); 151330744affSchristos } 151430744affSchristos 1515698d5317Sjmmv /* Carriage return (cursor to start of line). */ 1516698d5317Sjmmv void 1517698d5317Sjmmv screen_write_carriagereturn(struct screen_write_ctx *ctx) 1518698d5317Sjmmv { 15190a274e86Schristos screen_write_set_cursor(ctx, 0, -1); 1520698d5317Sjmmv } 1521698d5317Sjmmv 1522698d5317Sjmmv /* Clear to end of screen from cursor. */ 1523698d5317Sjmmv void 1524e9a2d6faSchristos screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) 1525698d5317Sjmmv { 1526698d5317Sjmmv struct screen *s = ctx->s; 1527e9a2d6faSchristos struct grid *gd = s->grid; 1528698d5317Sjmmv struct tty_ctx ttyctx; 1529e9a2d6faSchristos u_int sx = screen_size_x(s), sy = screen_size_y(s); 1530698d5317Sjmmv 1531f844e94eSwiz #ifdef ENABLE_SIXEL 1532f844e94eSwiz if (image_check_line(s, s->cy, sy - s->cy) && ctx->wp != NULL) 1533f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1534f844e94eSwiz #endif 1535f844e94eSwiz 1536e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1537e9a2d6faSchristos ttyctx.bg = bg; 1538698d5317Sjmmv 15390f3d2746Sjmmv /* Scroll into history if it is enabled and clearing entire screen. */ 154046548964Swiz if (s->cx == 0 && 154146548964Swiz s->cy == 0 && 154246548964Swiz (gd->flags & GRID_HISTORY) && 154346548964Swiz ctx->wp != NULL && 154446548964Swiz options_get_number(ctx->wp->options, "scroll-on-clear")) 1545e9a2d6faSchristos grid_view_clear_history(gd, bg); 15460f3d2746Sjmmv else { 1547698d5317Sjmmv if (s->cx <= sx - 1) 1548e9a2d6faSchristos grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); 1549e9a2d6faSchristos grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); 15500f3d2746Sjmmv } 1551698d5317Sjmmv 1552e9a2d6faSchristos screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); 1553e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1554698d5317Sjmmv tty_write(tty_cmd_clearendofscreen, &ttyctx); 1555698d5317Sjmmv } 1556698d5317Sjmmv 1557698d5317Sjmmv /* Clear to start of screen. */ 1558698d5317Sjmmv void 1559e9a2d6faSchristos screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) 1560698d5317Sjmmv { 1561698d5317Sjmmv struct screen *s = ctx->s; 1562698d5317Sjmmv struct tty_ctx ttyctx; 1563e9a2d6faSchristos u_int sx = screen_size_x(s); 1564698d5317Sjmmv 1565f844e94eSwiz #ifdef ENABLE_SIXEL 1566f844e94eSwiz if (image_check_line(s, 0, s->cy - 1) && ctx->wp != NULL) 1567f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1568f844e94eSwiz #endif 1569f844e94eSwiz 1570e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1571e9a2d6faSchristos ttyctx.bg = bg; 1572698d5317Sjmmv 1573698d5317Sjmmv if (s->cy > 0) 1574e9a2d6faSchristos grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); 1575698d5317Sjmmv if (s->cx > sx - 1) 1576fe99a117Schristos grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); 1577698d5317Sjmmv else 1578fe99a117Schristos grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); 1579698d5317Sjmmv 1580e9a2d6faSchristos screen_write_collect_clear(ctx, 0, s->cy); 1581e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1582698d5317Sjmmv tty_write(tty_cmd_clearstartofscreen, &ttyctx); 1583698d5317Sjmmv } 1584698d5317Sjmmv 1585698d5317Sjmmv /* Clear entire screen. */ 1586698d5317Sjmmv void 1587e9a2d6faSchristos screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) 1588698d5317Sjmmv { 1589698d5317Sjmmv struct screen *s = ctx->s; 1590698d5317Sjmmv struct tty_ctx ttyctx; 1591e9a2d6faSchristos u_int sx = screen_size_x(s), sy = screen_size_y(s); 1592698d5317Sjmmv 1593f844e94eSwiz #ifdef ENABLE_SIXEL 1594f844e94eSwiz if (image_free_all(s) && ctx->wp != NULL) 1595f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1596f844e94eSwiz #endif 1597f844e94eSwiz 1598e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1599e9a2d6faSchristos ttyctx.bg = bg; 1600698d5317Sjmmv 16010f3d2746Sjmmv /* Scroll into history if it is enabled. */ 160246548964Swiz if ((s->grid->flags & GRID_HISTORY) && 160346548964Swiz ctx->wp != NULL && 160446548964Swiz options_get_number(ctx->wp->options, "scroll-on-clear")) 1605e9a2d6faSchristos grid_view_clear_history(s->grid, bg); 1606f26e8bc9Schristos else 1607e9a2d6faSchristos grid_view_clear(s->grid, 0, 0, sx, sy, bg); 1608698d5317Sjmmv 1609e9a2d6faSchristos screen_write_collect_clear(ctx, 0, sy); 1610698d5317Sjmmv tty_write(tty_cmd_clearscreen, &ttyctx); 1611698d5317Sjmmv } 1612698d5317Sjmmv 161361fba46bSchristos /* Clear entire history. */ 161461fba46bSchristos void 161561fba46bSchristos screen_write_clearhistory(struct screen_write_ctx *ctx) 161661fba46bSchristos { 161730744affSchristos grid_clear_history(ctx->s->grid); 1618e9a2d6faSchristos } 1619e9a2d6faSchristos 162046548964Swiz /* Force a full redraw. */ 162146548964Swiz void 162246548964Swiz screen_write_fullredraw(struct screen_write_ctx *ctx) 162346548964Swiz { 162446548964Swiz struct tty_ctx ttyctx; 162546548964Swiz 162646548964Swiz screen_write_collect_flush(ctx, 0, __func__); 162746548964Swiz 162846548964Swiz screen_write_initctx(ctx, &ttyctx, 1); 1629f844e94eSwiz if (ttyctx.redraw_cb != NULL) 163046548964Swiz ttyctx.redraw_cb(&ttyctx); 163146548964Swiz } 163246548964Swiz 1633e271dbb8Schristos /* Trim collected items. */ 1634e271dbb8Schristos static struct screen_write_citem * 1635e271dbb8Schristos screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x, 1636e271dbb8Schristos u_int used, int *wrapped) 1637e271dbb8Schristos { 1638e271dbb8Schristos struct screen_write_cline *cl = &ctx->s->write_list[y]; 1639e271dbb8Schristos struct screen_write_citem *ci, *ci2, *tmp, *before = NULL; 1640e271dbb8Schristos u_int sx = x, ex = x + used - 1; 1641e271dbb8Schristos u_int csx, cex; 1642e271dbb8Schristos 1643e271dbb8Schristos if (TAILQ_EMPTY(&cl->items)) 1644e271dbb8Schristos return (NULL); 1645e271dbb8Schristos TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { 1646e271dbb8Schristos csx = ci->x; 1647e271dbb8Schristos cex = ci->x + ci->used - 1; 1648e271dbb8Schristos 1649e271dbb8Schristos /* Item is entirely before. */ 1650e271dbb8Schristos if (cex < sx) { 1651e271dbb8Schristos log_debug("%s: %p %u-%u before %u-%u", __func__, ci, 1652e271dbb8Schristos csx, cex, sx, ex); 1653e271dbb8Schristos continue; 1654e271dbb8Schristos } 1655e271dbb8Schristos 1656e271dbb8Schristos /* Item is entirely after. */ 1657e271dbb8Schristos if (csx > ex) { 1658e271dbb8Schristos log_debug("%s: %p %u-%u after %u-%u", __func__, ci, 1659e271dbb8Schristos csx, cex, sx, ex); 1660e271dbb8Schristos before = ci; 1661e271dbb8Schristos break; 1662e271dbb8Schristos } 1663e271dbb8Schristos 1664e271dbb8Schristos /* Item is entirely inside. */ 1665e271dbb8Schristos if (csx >= sx && cex <= ex) { 1666e271dbb8Schristos log_debug("%s: %p %u-%u inside %u-%u", __func__, ci, 1667e271dbb8Schristos csx, cex, sx, ex); 1668e271dbb8Schristos TAILQ_REMOVE(&cl->items, ci, entry); 1669e271dbb8Schristos screen_write_free_citem(ci); 1670e271dbb8Schristos if (csx == 0 && ci->wrapped && wrapped != NULL) 1671e271dbb8Schristos *wrapped = 1; 1672e271dbb8Schristos continue; 1673e271dbb8Schristos } 1674e271dbb8Schristos 1675e271dbb8Schristos /* Item under the start. */ 1676e271dbb8Schristos if (csx < sx && cex >= sx && cex <= ex) { 1677e271dbb8Schristos log_debug("%s: %p %u-%u start %u-%u", __func__, ci, 1678e271dbb8Schristos csx, cex, sx, ex); 1679e271dbb8Schristos ci->used = sx - csx; 1680e271dbb8Schristos log_debug("%s: %p now %u-%u", __func__, ci, ci->x, 1681e271dbb8Schristos ci->x + ci->used + 1); 1682e271dbb8Schristos continue; 1683e271dbb8Schristos } 1684e271dbb8Schristos 1685e271dbb8Schristos /* Item covers the end. */ 1686e271dbb8Schristos if (cex > ex && csx >= sx && csx <= ex) { 1687e271dbb8Schristos log_debug("%s: %p %u-%u end %u-%u", __func__, ci, 1688e271dbb8Schristos csx, cex, sx, ex); 1689e271dbb8Schristos ci->x = ex + 1; 1690e271dbb8Schristos ci->used = cex - ex; 1691e271dbb8Schristos log_debug("%s: %p now %u-%u", __func__, ci, ci->x, 1692e271dbb8Schristos ci->x + ci->used + 1); 1693e271dbb8Schristos before = ci; 1694e271dbb8Schristos break; 1695e271dbb8Schristos } 1696e271dbb8Schristos 1697e271dbb8Schristos /* Item must cover both sides. */ 1698e271dbb8Schristos log_debug("%s: %p %u-%u under %u-%u", __func__, ci, 1699e271dbb8Schristos csx, cex, sx, ex); 1700e271dbb8Schristos ci2 = screen_write_get_citem(); 1701e271dbb8Schristos ci2->type = ci->type; 1702e271dbb8Schristos ci2->bg = ci->bg; 1703e271dbb8Schristos memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc); 1704e271dbb8Schristos TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry); 1705e271dbb8Schristos 1706e271dbb8Schristos ci->used = sx - csx; 1707e271dbb8Schristos ci2->x = ex + 1; 1708e271dbb8Schristos ci2->used = cex - ex; 1709e271dbb8Schristos 1710e271dbb8Schristos log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci, 1711e271dbb8Schristos ci->x, ci->x + ci->used - 1, ci, ci2->x, 1712e271dbb8Schristos ci2->x + ci2->used - 1, ci2); 1713e271dbb8Schristos before = ci2; 1714e271dbb8Schristos break; 1715e271dbb8Schristos } 1716e271dbb8Schristos return (before); 1717e271dbb8Schristos } 1718e271dbb8Schristos 1719e271dbb8Schristos /* Clear collected lines. */ 1720e9a2d6faSchristos static void 1721e9a2d6faSchristos screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) 1722e9a2d6faSchristos { 1723e271dbb8Schristos struct screen_write_cline *cl; 1724e9a2d6faSchristos u_int i; 1725e9a2d6faSchristos 1726e9a2d6faSchristos for (i = y; i < y + n; i++) { 1727e271dbb8Schristos cl = &ctx->s->write_list[i]; 1728e271dbb8Schristos TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry); 1729e9a2d6faSchristos } 1730e9a2d6faSchristos } 1731e9a2d6faSchristos 1732e9a2d6faSchristos /* Scroll collected lines up. */ 1733e9a2d6faSchristos static void 1734e271dbb8Schristos screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg) 1735e9a2d6faSchristos { 1736e9a2d6faSchristos struct screen *s = ctx->s; 1737e271dbb8Schristos struct screen_write_cline *cl; 1738e9a2d6faSchristos u_int y; 1739e271dbb8Schristos char *saved; 1740e271dbb8Schristos struct screen_write_citem *ci; 1741e9a2d6faSchristos 1742e9a2d6faSchristos log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy, 1743e9a2d6faSchristos s->rupper, s->rlower); 1744e9a2d6faSchristos 1745e9a2d6faSchristos screen_write_collect_clear(ctx, s->rupper, 1); 1746e271dbb8Schristos saved = ctx->s->write_list[s->rupper].data; 1747e9a2d6faSchristos for (y = s->rupper; y < s->rlower; y++) { 1748e271dbb8Schristos cl = &ctx->s->write_list[y + 1]; 1749e271dbb8Schristos TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry); 1750e271dbb8Schristos ctx->s->write_list[y].data = cl->data; 1751e9a2d6faSchristos } 1752e271dbb8Schristos ctx->s->write_list[s->rlower].data = saved; 1753e271dbb8Schristos 1754e271dbb8Schristos ci = screen_write_get_citem(); 1755e271dbb8Schristos ci->x = 0; 1756e271dbb8Schristos ci->used = screen_size_x(s); 1757e271dbb8Schristos ci->type = CLEAR; 1758e271dbb8Schristos ci->bg = bg; 1759e271dbb8Schristos TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry); 1760e9a2d6faSchristos } 1761e9a2d6faSchristos 1762e9a2d6faSchristos /* Flush collected lines. */ 1763e9a2d6faSchristos static void 1764e271dbb8Schristos screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only, 1765e271dbb8Schristos const char *from) 1766e9a2d6faSchristos { 1767e9a2d6faSchristos struct screen *s = ctx->s; 1768e271dbb8Schristos struct screen_write_citem *ci, *tmp; 1769e271dbb8Schristos struct screen_write_cline *cl; 1770e271dbb8Schristos u_int y, cx, cy, last, items = 0; 1771e9a2d6faSchristos struct tty_ctx ttyctx; 1772e9a2d6faSchristos 1773e9a2d6faSchristos if (ctx->scrolled != 0) { 1774e9a2d6faSchristos log_debug("%s: scrolled %u (region %u-%u)", __func__, 1775e9a2d6faSchristos ctx->scrolled, s->rupper, s->rlower); 1776e9a2d6faSchristos if (ctx->scrolled > s->rlower - s->rupper + 1) 1777e9a2d6faSchristos ctx->scrolled = s->rlower - s->rupper + 1; 1778e9a2d6faSchristos 1779e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1780e9a2d6faSchristos ttyctx.num = ctx->scrolled; 1781fe99a117Schristos ttyctx.bg = ctx->bg; 1782e9a2d6faSchristos tty_write(tty_cmd_scrollup, &ttyctx); 1783e9a2d6faSchristos } 1784e9a2d6faSchristos ctx->scrolled = 0; 1785fe99a117Schristos ctx->bg = 8; 1786fe99a117Schristos 1787e9a2d6faSchristos if (scroll_only) 1788e9a2d6faSchristos return; 1789e9a2d6faSchristos 1790e9a2d6faSchristos cx = s->cx; cy = s->cy; 1791e9a2d6faSchristos for (y = 0; y < screen_size_y(s); y++) { 1792e271dbb8Schristos cl = &ctx->s->write_list[y]; 1793e271dbb8Schristos last = UINT_MAX; 1794e271dbb8Schristos TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) { 1795e271dbb8Schristos if (last != UINT_MAX && ci->x <= last) { 1796e271dbb8Schristos fatalx("collect list not in order: %u <= %u", 1797e271dbb8Schristos ci->x, last); 1798e271dbb8Schristos } 17990a274e86Schristos screen_write_set_cursor(ctx, ci->x, y); 1800e271dbb8Schristos if (ci->type == CLEAR) { 1801e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 1802e271dbb8Schristos ttyctx.bg = ci->bg; 1803e271dbb8Schristos ttyctx.num = ci->used; 1804e271dbb8Schristos tty_write(tty_cmd_clearcharacter, &ttyctx); 1805e271dbb8Schristos } else { 1806e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 1807e9a2d6faSchristos ttyctx.cell = &ci->gc; 1808fe99a117Schristos ttyctx.wrapped = ci->wrapped; 1809e271dbb8Schristos ttyctx.ptr = cl->data + ci->x; 1810e9a2d6faSchristos ttyctx.num = ci->used; 1811e9a2d6faSchristos tty_write(tty_cmd_cells, &ttyctx); 1812e271dbb8Schristos } 1813e9a2d6faSchristos items++; 1814e9a2d6faSchristos 1815e271dbb8Schristos TAILQ_REMOVE(&cl->items, ci, entry); 1816e271dbb8Schristos screen_write_free_citem(ci); 1817e271dbb8Schristos last = ci->x; 1818e9a2d6faSchristos } 1819e9a2d6faSchristos } 1820e9a2d6faSchristos s->cx = cx; s->cy = cy; 1821e9a2d6faSchristos 1822e271dbb8Schristos log_debug("%s: flushed %u items (%s)", __func__, items, from); 1823e9a2d6faSchristos } 1824e9a2d6faSchristos 1825e9a2d6faSchristos /* Finish and store collected cells. */ 1826e9a2d6faSchristos void 1827e9a2d6faSchristos screen_write_collect_end(struct screen_write_ctx *ctx) 1828e9a2d6faSchristos { 1829e9a2d6faSchristos struct screen *s = ctx->s; 1830e271dbb8Schristos struct screen_write_citem *ci = ctx->item, *before; 1831e271dbb8Schristos struct screen_write_cline *cl = &s->write_list[s->cy]; 1832e9a2d6faSchristos struct grid_cell gc; 1833c7e17de0Schristos u_int xx; 1834e271dbb8Schristos int wrapped = ci->wrapped; 1835e9a2d6faSchristos 1836e9a2d6faSchristos if (ci->used == 0) 1837e9a2d6faSchristos return; 1838e9a2d6faSchristos 1839e271dbb8Schristos before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used, 1840e271dbb8Schristos &wrapped); 1841e9a2d6faSchristos ci->x = s->cx; 1842e271dbb8Schristos ci->wrapped = wrapped; 1843e271dbb8Schristos if (before == NULL) 1844e271dbb8Schristos TAILQ_INSERT_TAIL(&cl->items, ci, entry); 1845e271dbb8Schristos else 1846e271dbb8Schristos TAILQ_INSERT_BEFORE(before, ci, entry); 1847e271dbb8Schristos ctx->item = screen_write_get_citem(); 1848e9a2d6faSchristos 1849e271dbb8Schristos log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used, 1850e271dbb8Schristos (int)ci->used, cl->data + ci->x, s->cx, s->cy); 1851e9a2d6faSchristos 1852c7e17de0Schristos if (s->cx != 0) { 1853c7e17de0Schristos for (xx = s->cx; xx > 0; xx--) { 1854c7e17de0Schristos grid_view_get_cell(s->grid, xx, s->cy, &gc); 1855c7e17de0Schristos if (~gc.flags & GRID_FLAG_PADDING) 1856c7e17de0Schristos break; 1857c7e17de0Schristos grid_view_set_cell(s->grid, xx, s->cy, 1858c7e17de0Schristos &grid_default_cell); 1859c7e17de0Schristos } 18600a274e86Schristos if (gc.data.width > 1) { 1861c7e17de0Schristos grid_view_set_cell(s->grid, xx, s->cy, 1862c7e17de0Schristos &grid_default_cell); 1863c7e17de0Schristos } 18640a274e86Schristos } 1865c7e17de0Schristos 1866f844e94eSwiz #ifdef ENABLE_SIXEL 1867f844e94eSwiz if (image_check_area(s, s->cx, s->cy, ci->used, 1) && ctx->wp != NULL) 1868f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 1869f844e94eSwiz #endif 1870f844e94eSwiz 1871e271dbb8Schristos grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x, 1872e271dbb8Schristos ci->used); 18730a274e86Schristos screen_write_set_cursor(ctx, s->cx + ci->used, -1); 1874c7e17de0Schristos 1875c7e17de0Schristos for (xx = s->cx; xx < screen_size_x(s); xx++) { 1876c7e17de0Schristos grid_view_get_cell(s->grid, xx, s->cy, &gc); 1877c7e17de0Schristos if (~gc.flags & GRID_FLAG_PADDING) 1878c7e17de0Schristos break; 1879c7e17de0Schristos grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); 1880c7e17de0Schristos } 1881e9a2d6faSchristos } 1882e9a2d6faSchristos 1883e9a2d6faSchristos /* Write cell data, collecting if necessary. */ 1884e9a2d6faSchristos void 1885e9a2d6faSchristos screen_write_collect_add(struct screen_write_ctx *ctx, 1886e9a2d6faSchristos const struct grid_cell *gc) 1887e9a2d6faSchristos { 1888e9a2d6faSchristos struct screen *s = ctx->s; 1889e271dbb8Schristos struct screen_write_citem *ci; 1890e9a2d6faSchristos u_int sx = screen_size_x(s); 1891e9a2d6faSchristos int collect; 1892e9a2d6faSchristos 1893e9a2d6faSchristos /* 1894e9a2d6faSchristos * Don't need to check that the attributes and whatnot are still the 1895e9a2d6faSchristos * same - input_parse will end the collection when anything that isn't 189668e6ba84Schristos * a plain character is encountered. 1897e9a2d6faSchristos */ 1898e9a2d6faSchristos 1899e9a2d6faSchristos collect = 1; 1900c7e17de0Schristos if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f) 1901e9a2d6faSchristos collect = 0; 1902e9a2d6faSchristos else if (gc->attr & GRID_ATTR_CHARSET) 1903e9a2d6faSchristos collect = 0; 1904e9a2d6faSchristos else if (~s->mode & MODE_WRAP) 1905e9a2d6faSchristos collect = 0; 1906e9a2d6faSchristos else if (s->mode & MODE_INSERT) 1907e9a2d6faSchristos collect = 0; 1908c7e17de0Schristos else if (s->sel != NULL) 1909e9a2d6faSchristos collect = 0; 1910e9a2d6faSchristos if (!collect) { 1911e9a2d6faSchristos screen_write_collect_end(ctx); 1912e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 1913e9a2d6faSchristos screen_write_cell(ctx, gc); 1914e9a2d6faSchristos return; 1915e9a2d6faSchristos } 1916e9a2d6faSchristos 1917e9a2d6faSchristos if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) 1918e9a2d6faSchristos screen_write_collect_end(ctx); 1919fe99a117Schristos ci = ctx->item; /* may have changed */ 1920fe99a117Schristos 1921e9a2d6faSchristos if (s->cx > sx - 1) { 1922e9a2d6faSchristos log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1923fe99a117Schristos ci->wrapped = 1; 1924fe99a117Schristos screen_write_linefeed(ctx, 1, 8); 19250a274e86Schristos screen_write_set_cursor(ctx, 0, -1); 1926e9a2d6faSchristos } 1927e9a2d6faSchristos 1928e9a2d6faSchristos if (ci->used == 0) 1929e9a2d6faSchristos memcpy(&ci->gc, gc, sizeof ci->gc); 1930e271dbb8Schristos if (ctx->s->write_list[s->cy].data == NULL) 1931e271dbb8Schristos ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s)); 1932e271dbb8Schristos ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0]; 193361fba46bSchristos } 193461fba46bSchristos 1935698d5317Sjmmv /* Write cell data. */ 1936698d5317Sjmmv void 193761fba46bSchristos screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) 1938698d5317Sjmmv { 1939698d5317Sjmmv struct screen *s = ctx->s; 1940698d5317Sjmmv struct grid *gd = s->grid; 194146548964Swiz const struct utf8_data *ud = &gc->data; 1942e9a2d6faSchristos struct grid_line *gl; 1943e9a2d6faSchristos struct grid_cell_entry *gce; 1944e9a2d6faSchristos struct grid_cell tmp_gc, now_gc; 1945698d5317Sjmmv struct tty_ctx ttyctx; 1946e9a2d6faSchristos u_int sx = screen_size_x(s), sy = screen_size_y(s); 1947f844e94eSwiz u_int width = ud->width, xx, not_wrap; 1948e9a2d6faSchristos int selected, skip = 1; 1949698d5317Sjmmv 1950e9a2d6faSchristos /* Ignore padding cells. */ 1951698d5317Sjmmv if (gc->flags & GRID_FLAG_PADDING) 1952698d5317Sjmmv return; 1953698d5317Sjmmv 1954f844e94eSwiz /* Get the previous cell to check for combining. */ 1955f844e94eSwiz if (screen_write_combine(ctx, gc) != 0) 195646548964Swiz return; 1957698d5317Sjmmv 1958e9a2d6faSchristos /* Flush any existing scrolling. */ 1959e271dbb8Schristos screen_write_collect_flush(ctx, 1, __func__); 1960e9a2d6faSchristos 1961e9a2d6faSchristos /* If this character doesn't fit, ignore it. */ 1962e9a2d6faSchristos if ((~s->mode & MODE_WRAP) && 1963e9a2d6faSchristos width > 1 && 1964e9a2d6faSchristos (width > sx || (s->cx != sx && s->cx > sx - width))) 1965e9a2d6faSchristos return; 1966698d5317Sjmmv 1967698d5317Sjmmv /* If in insert mode, make space for the cells. */ 1968e9a2d6faSchristos if (s->mode & MODE_INSERT) { 1969e9a2d6faSchristos grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); 1970e9a2d6faSchristos skip = 0; 1971e9a2d6faSchristos } 1972698d5317Sjmmv 1973698d5317Sjmmv /* Check this will fit on the current line and wrap if not. */ 1974e9a2d6faSchristos if ((s->mode & MODE_WRAP) && s->cx > sx - width) { 1975fe99a117Schristos log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy); 1976fe99a117Schristos screen_write_linefeed(ctx, 1, 8); 19770a274e86Schristos screen_write_set_cursor(ctx, 0, -1); 1978e271dbb8Schristos screen_write_collect_flush(ctx, 1, __func__); 1979698d5317Sjmmv } 1980698d5317Sjmmv 198161fba46bSchristos /* Sanity check cursor position. */ 1982e9a2d6faSchristos if (s->cx > sx - width || s->cy > sy - 1) 1983698d5317Sjmmv return; 1984e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 1985698d5317Sjmmv 1986698d5317Sjmmv /* Handle overwriting of UTF-8 characters. */ 1987c7e17de0Schristos gl = grid_get_line(s->grid, s->grid->hsize + s->cy); 1988e9a2d6faSchristos if (gl->flags & GRID_LINE_EXTENDED) { 1989e9a2d6faSchristos grid_view_get_cell(gd, s->cx, s->cy, &now_gc); 1990e9a2d6faSchristos if (screen_write_overwrite(ctx, &now_gc, width)) 1991e9a2d6faSchristos skip = 0; 1992e9a2d6faSchristos } 1993698d5317Sjmmv 1994698d5317Sjmmv /* 1995698d5317Sjmmv * If the new character is UTF-8 wide, fill in padding cells. Have 1996698d5317Sjmmv * already ensured there is enough room. 1997698d5317Sjmmv */ 1998e9a2d6faSchristos for (xx = s->cx + 1; xx < s->cx + width; xx++) { 1999c7e17de0Schristos log_debug("%s: new padding at %u,%u", __func__, xx, s->cy); 2000e271dbb8Schristos grid_view_set_padding(gd, xx, s->cy); 2001e9a2d6faSchristos skip = 0; 2002e9a2d6faSchristos } 2003698d5317Sjmmv 2004e9a2d6faSchristos /* If no change, do not draw. */ 2005e9a2d6faSchristos if (skip) { 2006e9a2d6faSchristos if (s->cx >= gl->cellsize) 2007e9a2d6faSchristos skip = grid_cells_equal(gc, &grid_default_cell); 2008e9a2d6faSchristos else { 2009e9a2d6faSchristos gce = &gl->celldata[s->cx]; 2010e9a2d6faSchristos if (gce->flags & GRID_FLAG_EXTENDED) 2011e9a2d6faSchristos skip = 0; 2012e9a2d6faSchristos else if (gc->flags != gce->flags) 2013e9a2d6faSchristos skip = 0; 2014e9a2d6faSchristos else if (gc->attr != gce->data.attr) 2015e9a2d6faSchristos skip = 0; 2016e9a2d6faSchristos else if (gc->fg != gce->data.fg) 2017e9a2d6faSchristos skip = 0; 2018e9a2d6faSchristos else if (gc->bg != gce->data.bg) 2019e9a2d6faSchristos skip = 0; 2020e9a2d6faSchristos else if (gc->data.width != 1) 2021e9a2d6faSchristos skip = 0; 2022e9a2d6faSchristos else if (gc->data.size != 1) 2023e9a2d6faSchristos skip = 0; 2024e9a2d6faSchristos else if (gce->data.data != gc->data.data[0]) 2025e9a2d6faSchristos skip = 0; 2026e9a2d6faSchristos } 2027e9a2d6faSchristos } 2028e9a2d6faSchristos 2029fe99a117Schristos /* Update the selected flag and set the cell. */ 2030e9a2d6faSchristos selected = screen_check_selection(s, s->cx, s->cy); 2031e9a2d6faSchristos if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { 2032e9a2d6faSchristos memcpy(&tmp_gc, gc, sizeof tmp_gc); 2033e9a2d6faSchristos tmp_gc.flags |= GRID_FLAG_SELECTED; 2034e9a2d6faSchristos grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 2035e9a2d6faSchristos } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { 2036e9a2d6faSchristos memcpy(&tmp_gc, gc, sizeof tmp_gc); 2037e9a2d6faSchristos tmp_gc.flags &= ~GRID_FLAG_SELECTED; 2038e9a2d6faSchristos grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); 2039e9a2d6faSchristos } else if (!skip) 2040698d5317Sjmmv grid_view_set_cell(gd, s->cx, s->cy, gc); 2041e9a2d6faSchristos if (selected) 2042e9a2d6faSchristos skip = 0; 2043698d5317Sjmmv 204461fba46bSchristos /* 204561fba46bSchristos * Move the cursor. If not wrapping, stick at the last character and 204661fba46bSchristos * replace it. 204761fba46bSchristos */ 2048f844e94eSwiz not_wrap = !(s->mode & MODE_WRAP); 2049f844e94eSwiz if (s->cx <= sx - not_wrap - width) 20500a274e86Schristos screen_write_set_cursor(ctx, s->cx + width, -1); 205161fba46bSchristos else 2052f844e94eSwiz screen_write_set_cursor(ctx, sx - not_wrap, -1); 2053698d5317Sjmmv 2054e9a2d6faSchristos /* Create space for character in insert mode. */ 2055e9a2d6faSchristos if (s->mode & MODE_INSERT) { 2056e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 2057698d5317Sjmmv ttyctx.num = width; 2058698d5317Sjmmv tty_write(tty_cmd_insertcharacter, &ttyctx); 2059698d5317Sjmmv } 2060e9a2d6faSchristos 2061e9a2d6faSchristos /* Write to the screen. */ 2062e9a2d6faSchristos if (!skip) { 2063e9a2d6faSchristos if (selected) { 2064e9a2d6faSchristos screen_select_cell(s, &tmp_gc, gc); 2065698d5317Sjmmv ttyctx.cell = &tmp_gc; 2066e9a2d6faSchristos } else 2067698d5317Sjmmv ttyctx.cell = gc; 2068698d5317Sjmmv tty_write(tty_cmd_cell, &ttyctx); 2069e271dbb8Schristos } 2070698d5317Sjmmv } 2071698d5317Sjmmv 2072f844e94eSwiz /* Combine a UTF-8 zero-width character onto the previous if necessary. */ 2073f844e94eSwiz static int 2074f844e94eSwiz screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc) 2075698d5317Sjmmv { 2076698d5317Sjmmv struct screen *s = ctx->s; 2077698d5317Sjmmv struct grid *gd = s->grid; 2078f844e94eSwiz const struct utf8_data *ud = &gc->data; 2079f844e94eSwiz u_int n, cx = s->cx, cy = s->cy; 2080f844e94eSwiz struct grid_cell last; 2081f844e94eSwiz struct tty_ctx ttyctx; 2082f844e94eSwiz int force_wide = 0, zero_width = 0; 2083698d5317Sjmmv 2084f844e94eSwiz /* 2085f844e94eSwiz * Is this character which makes no sense without being combined? If 2086f844e94eSwiz * this is true then flag it here and discard the character (return 1) 2087f844e94eSwiz * if we cannot combine it. 2088f844e94eSwiz */ 2089f844e94eSwiz if (utf8_is_zwj(ud)) 2090f844e94eSwiz zero_width = 1; 2091f844e94eSwiz else if (utf8_is_vs(ud)) 2092f844e94eSwiz zero_width = force_wide = 1; 2093f844e94eSwiz else if (ud->width == 0) 2094f844e94eSwiz zero_width = 1; 2095698d5317Sjmmv 2096f844e94eSwiz /* Cannot combine empty character or at left. */ 2097f844e94eSwiz if (ud->size < 2 || cx == 0) 2098f844e94eSwiz return (zero_width); 2099f844e94eSwiz log_debug("%s: character %.*s at %u,%u (width %u)", __func__, 2100f844e94eSwiz (int)ud->size, ud->data, cx, cy, ud->width); 2101698d5317Sjmmv 2102f844e94eSwiz /* Find the cell to combine with. */ 2103f844e94eSwiz n = 1; 2104f844e94eSwiz grid_view_get_cell(gd, cx - n, cy, &last); 2105f844e94eSwiz if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) { 2106f844e94eSwiz n = 2; 2107f844e94eSwiz grid_view_get_cell(gd, cx - n, cy, &last); 2108e9a2d6faSchristos } 2109f844e94eSwiz if (n != last.data.width || (last.flags & GRID_FLAG_PADDING)) 2110f844e94eSwiz return (zero_width); 2111698d5317Sjmmv 2112f844e94eSwiz /* 2113f844e94eSwiz * Check if we need to combine characters. This could be zero width 2114f844e94eSwiz * (set above), a modifier character (with an existing Unicode 2115f844e94eSwiz * character) or a previous ZWJ. 2116f844e94eSwiz */ 2117f844e94eSwiz if (!zero_width) { 2118f844e94eSwiz if (utf8_is_modifier(ud)) { 2119f844e94eSwiz if (last.data.size < 2) 2120f844e94eSwiz return (0); 2121f844e94eSwiz force_wide = 1; 2122f844e94eSwiz } else if (!utf8_has_zwj(&last.data)) 2123f844e94eSwiz return (0); 2124f844e94eSwiz } 2125e9a2d6faSchristos 2126f844e94eSwiz /* Check if this combined character would be too long. */ 2127f844e94eSwiz if (last.data.size + ud->size > sizeof last.data.data) 2128f844e94eSwiz return (0); 2129f844e94eSwiz 2130f844e94eSwiz /* Combining; flush any pending output. */ 2131f844e94eSwiz screen_write_collect_flush(ctx, 0, __func__); 2132f844e94eSwiz 2133f844e94eSwiz log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__, 2134f844e94eSwiz (int)ud->size, ud->data, (int)last.data.size, last.data.data, 2135f844e94eSwiz cx - n, cy, n, last.data.width); 2136698d5317Sjmmv 2137f26e8bc9Schristos /* Append the data. */ 2138f844e94eSwiz memcpy(last.data.data + last.data.size, ud->data, ud->size); 2139f844e94eSwiz last.data.size += ud->size; 2140f844e94eSwiz 2141f844e94eSwiz /* Force the width to 2 for modifiers and variation selector. */ 2142f844e94eSwiz if (last.data.width == 1 && force_wide) { 2143f844e94eSwiz last.data.width = 2; 2144f844e94eSwiz n = 2; 2145f844e94eSwiz cx++; 2146f844e94eSwiz } else 2147f844e94eSwiz force_wide = 0; 2148f26e8bc9Schristos 2149f26e8bc9Schristos /* Set the new cell. */ 2150f844e94eSwiz grid_view_set_cell(gd, cx - n, cy, &last); 2151f844e94eSwiz if (force_wide) 2152*890b6d91Swiz grid_view_set_padding(gd, cx - 1, cy); 2153698d5317Sjmmv 2154f844e94eSwiz /* 2155f844e94eSwiz * Redraw the combined cell. If forcing the cell to width 2, reset the 2156f844e94eSwiz * cached cursor position in the tty, since we don't really know 2157f844e94eSwiz * whether the terminal thought the character was width 1 or width 2 2158f844e94eSwiz * and what it is going to do now. 2159f844e94eSwiz */ 2160f844e94eSwiz screen_write_set_cursor(ctx, cx - n, cy); 2161f844e94eSwiz screen_write_initctx(ctx, &ttyctx, 0); 2162f844e94eSwiz ttyctx.cell = &last; 2163f844e94eSwiz ttyctx.num = force_wide; /* reset cached cursor position */ 2164f844e94eSwiz tty_write(tty_cmd_cell, &ttyctx); 2165f844e94eSwiz screen_write_set_cursor(ctx, cx, cy); 2166f844e94eSwiz 2167f844e94eSwiz return (1); 2168698d5317Sjmmv } 2169698d5317Sjmmv 2170698d5317Sjmmv /* 2171698d5317Sjmmv * UTF-8 wide characters are a bit of an annoyance. They take up more than one 2172698d5317Sjmmv * cell on the screen, so following cells must not be drawn by marking them as 2173698d5317Sjmmv * padding. 2174698d5317Sjmmv * 2175698d5317Sjmmv * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 2176698d5317Sjmmv * character, it is necessary to also overwrite any other cells which covered 2177698d5317Sjmmv * by the same character. 2178698d5317Sjmmv */ 2179e9a2d6faSchristos static int 2180e9a2d6faSchristos screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, 2181e9a2d6faSchristos u_int width) 2182698d5317Sjmmv { 2183698d5317Sjmmv struct screen *s = ctx->s; 2184698d5317Sjmmv struct grid *gd = s->grid; 2185e9a2d6faSchristos struct grid_cell tmp_gc; 2186698d5317Sjmmv u_int xx; 2187e9a2d6faSchristos int done = 0; 2188698d5317Sjmmv 2189e9a2d6faSchristos if (gc->flags & GRID_FLAG_PADDING) { 2190698d5317Sjmmv /* 2191698d5317Sjmmv * A padding cell, so clear any following and leading padding 2192698d5317Sjmmv * cells back to the character. Don't overwrite the current 2193698d5317Sjmmv * cell as that happens later anyway. 2194698d5317Sjmmv */ 2195698d5317Sjmmv xx = s->cx + 1; 2196698d5317Sjmmv while (--xx > 0) { 2197e9a2d6faSchristos grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 2198e9a2d6faSchristos if (~tmp_gc.flags & GRID_FLAG_PADDING) 2199698d5317Sjmmv break; 2200c7e17de0Schristos log_debug("%s: padding at %u,%u", __func__, xx, s->cy); 2201698d5317Sjmmv grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 2202698d5317Sjmmv } 2203698d5317Sjmmv 2204698d5317Sjmmv /* Overwrite the character at the start of this padding. */ 2205c7e17de0Schristos log_debug("%s: character at %u,%u", __func__, xx, s->cy); 2206698d5317Sjmmv grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 2207e9a2d6faSchristos done = 1; 2208698d5317Sjmmv } 2209698d5317Sjmmv 2210698d5317Sjmmv /* 2211e9a2d6faSchristos * Overwrite any padding cells that belong to any UTF-8 characters 2212698d5317Sjmmv * we'll be overwriting with the current character. 2213698d5317Sjmmv */ 2214e9a2d6faSchristos if (width != 1 || 2215e9a2d6faSchristos gc->data.width != 1 || 2216e9a2d6faSchristos gc->flags & GRID_FLAG_PADDING) { 2217698d5317Sjmmv xx = s->cx + width - 1; 2218698d5317Sjmmv while (++xx < screen_size_x(s)) { 2219e9a2d6faSchristos grid_view_get_cell(gd, xx, s->cy, &tmp_gc); 2220e9a2d6faSchristos if (~tmp_gc.flags & GRID_FLAG_PADDING) 2221698d5317Sjmmv break; 222268e6ba84Schristos log_debug("%s: overwrite at %u,%u", __func__, xx, 222368e6ba84Schristos s->cy); 2224698d5317Sjmmv grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); 2225e9a2d6faSchristos done = 1; 2226698d5317Sjmmv } 2227698d5317Sjmmv } 22280f3d2746Sjmmv 2229e9a2d6faSchristos return (done); 2230e9a2d6faSchristos } 2231e9a2d6faSchristos 2232e9a2d6faSchristos /* Set external clipboard. */ 22330f3d2746Sjmmv void 2234f844e94eSwiz screen_write_setselection(struct screen_write_ctx *ctx, const char *flags, 2235f844e94eSwiz u_char *str, u_int len) 22360f3d2746Sjmmv { 22370f3d2746Sjmmv struct tty_ctx ttyctx; 22380f3d2746Sjmmv 2239e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 22400f3d2746Sjmmv ttyctx.ptr = str; 2241f844e94eSwiz ttyctx.ptr2 = __UNCONST(flags); 22420f3d2746Sjmmv ttyctx.num = len; 22430f3d2746Sjmmv 22440f3d2746Sjmmv tty_write(tty_cmd_setselection, &ttyctx); 22450f3d2746Sjmmv } 22460f3d2746Sjmmv 2247e9a2d6faSchristos /* Write unmodified string. */ 22480f3d2746Sjmmv void 2249f844e94eSwiz screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len, 2250f844e94eSwiz int allow_invisible_panes) 22510f3d2746Sjmmv { 22520f3d2746Sjmmv struct tty_ctx ttyctx; 22530f3d2746Sjmmv 2254e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 0); 22550f3d2746Sjmmv ttyctx.ptr = str; 22560f3d2746Sjmmv ttyctx.num = len; 2257f844e94eSwiz ttyctx.allow_invisible_panes = allow_invisible_panes; 22580f3d2746Sjmmv 22590f3d2746Sjmmv tty_write(tty_cmd_rawstring, &ttyctx); 22600f3d2746Sjmmv } 2261e271dbb8Schristos 2262f844e94eSwiz #ifdef ENABLE_SIXEL 2263f844e94eSwiz /* Write a SIXEL image. */ 2264f844e94eSwiz void 2265f844e94eSwiz screen_write_sixelimage(struct screen_write_ctx *ctx, struct sixel_image *si, 2266f844e94eSwiz u_int bg) 2267f844e94eSwiz { 2268f844e94eSwiz struct screen *s = ctx->s; 2269f844e94eSwiz struct grid *gd = s->grid; 2270f844e94eSwiz struct tty_ctx ttyctx; 2271f844e94eSwiz u_int x, y, sx, sy, cx = s->cx, cy = s->cy, i, lines; 2272f844e94eSwiz struct sixel_image *new; 2273f844e94eSwiz 2274f844e94eSwiz sixel_size_in_cells(si, &x, &y); 2275f844e94eSwiz if (x > screen_size_x(s) || y > screen_size_y(s)) { 2276f844e94eSwiz if (x > screen_size_x(s) - cx) 2277f844e94eSwiz sx = screen_size_x(s) - cx; 2278f844e94eSwiz else 2279f844e94eSwiz sx = x; 2280f844e94eSwiz if (y > screen_size_y(s) - 1) 2281f844e94eSwiz sy = screen_size_y(s) - 1; 2282f844e94eSwiz else 2283f844e94eSwiz sy = y; 2284f844e94eSwiz new = sixel_scale(si, 0, 0, 0, y - sy, sx, sy, 1); 2285f844e94eSwiz sixel_free(si); 2286f844e94eSwiz si = new; 2287*890b6d91Swiz 2288*890b6d91Swiz /* Bail out if the image cannot be scaled. */ 2289*890b6d91Swiz if (si == NULL) 2290*890b6d91Swiz return; 2291f844e94eSwiz sixel_size_in_cells(si, &x, &y); 2292f844e94eSwiz } 2293f844e94eSwiz 2294f844e94eSwiz sy = screen_size_y(s) - cy; 2295f844e94eSwiz if (sy < y) { 2296f844e94eSwiz lines = y - sy + 1; 2297f844e94eSwiz if (image_scroll_up(s, lines) && ctx->wp != NULL) 2298f844e94eSwiz ctx->wp->flags |= PANE_REDRAW; 2299f844e94eSwiz for (i = 0; i < lines; i++) { 2300f844e94eSwiz grid_view_scroll_region_up(gd, 0, screen_size_y(s) - 1, 2301f844e94eSwiz bg); 2302f844e94eSwiz screen_write_collect_scroll(ctx, bg); 2303f844e94eSwiz } 2304f844e94eSwiz ctx->scrolled += lines; 2305f844e94eSwiz if (lines > cy) 2306f844e94eSwiz screen_write_cursormove(ctx, -1, 0, 0); 2307f844e94eSwiz else 2308f844e94eSwiz screen_write_cursormove(ctx, -1, cy - lines, 0); 2309f844e94eSwiz } 2310f844e94eSwiz screen_write_collect_flush(ctx, 0, __func__); 2311f844e94eSwiz 2312f844e94eSwiz screen_write_initctx(ctx, &ttyctx, 0); 2313f844e94eSwiz ttyctx.ptr = image_store(s, si); 2314f844e94eSwiz 2315f844e94eSwiz tty_write(tty_cmd_sixelimage, &ttyctx); 2316f844e94eSwiz 2317f844e94eSwiz screen_write_cursormove(ctx, 0, cy + y, 0); 2318f844e94eSwiz } 2319f844e94eSwiz #endif 2320f844e94eSwiz 2321e271dbb8Schristos /* Turn alternate screen on. */ 2322e271dbb8Schristos void 2323e271dbb8Schristos screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc, 2324e271dbb8Schristos int cursor) 2325e271dbb8Schristos { 2326e271dbb8Schristos struct tty_ctx ttyctx; 2327e271dbb8Schristos struct window_pane *wp = ctx->wp; 2328e271dbb8Schristos 2329e271dbb8Schristos if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) 2330e271dbb8Schristos return; 2331e271dbb8Schristos 2332e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 2333e271dbb8Schristos screen_alternate_on(ctx->s, gc, cursor); 2334e271dbb8Schristos 2335e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 2336f844e94eSwiz if (ttyctx.redraw_cb != NULL) 2337e271dbb8Schristos ttyctx.redraw_cb(&ttyctx); 2338e271dbb8Schristos } 2339e271dbb8Schristos 2340e271dbb8Schristos /* Turn alternate screen off. */ 2341e271dbb8Schristos void 2342e271dbb8Schristos screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc, 2343e271dbb8Schristos int cursor) 2344e271dbb8Schristos { 2345e271dbb8Schristos struct tty_ctx ttyctx; 2346e271dbb8Schristos struct window_pane *wp = ctx->wp; 2347e271dbb8Schristos 2348e271dbb8Schristos if (wp != NULL && !options_get_number(wp->options, "alternate-screen")) 2349e271dbb8Schristos return; 2350e271dbb8Schristos 2351e271dbb8Schristos screen_write_collect_flush(ctx, 0, __func__); 2352e271dbb8Schristos screen_alternate_off(ctx->s, gc, cursor); 2353e271dbb8Schristos 2354e271dbb8Schristos screen_write_initctx(ctx, &ttyctx, 1); 2355f844e94eSwiz if (ttyctx.redraw_cb != NULL) 2356e271dbb8Schristos ttyctx.redraw_cb(&ttyctx); 2357e271dbb8Schristos } 2358