Fix a few memory leaks.
[tmux-openbsd.git] / screen-write.c
blobc09d09abc5845e43b4674072ff485a07dca9d51e
1 /* $OpenBSD$ */
3 /*
4 * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
6 * Permission to use, copy, modify, and distribute this software for any
7 * purpose with or without fee is hereby granted, provided that the above
8 * copyright notice and this permission notice appear in all copies.
10 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14 * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER
15 * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
16 * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #include <sys/types.h>
21 #include <stdlib.h>
22 #include <string.h>
24 #include "tmux.h"
26 static struct screen_write_citem *screen_write_collect_trim(
27 struct screen_write_ctx *, u_int, u_int, u_int, int *);
28 static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
29 u_int);
30 static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
31 static void screen_write_collect_flush(struct screen_write_ctx *, int,
32 const char *);
34 static int screen_write_overwrite(struct screen_write_ctx *,
35 struct grid_cell *, u_int);
36 static const struct grid_cell *screen_write_combine(struct screen_write_ctx *,
37 const struct utf8_data *, u_int *);
39 struct screen_write_citem {
40 u_int x;
41 int wrapped;
43 enum { TEXT, CLEAR } type;
44 u_int used;
45 u_int bg;
47 struct grid_cell gc;
49 TAILQ_ENTRY(screen_write_citem) entry;
51 struct screen_write_cline {
52 char *data;
53 TAILQ_HEAD(, screen_write_citem) items;
55 TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist =
56 TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
58 static struct screen_write_citem *
59 screen_write_get_citem(void)
61 struct screen_write_citem *ci;
63 ci = TAILQ_FIRST(&screen_write_citem_freelist);
64 if (ci != NULL) {
65 TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
66 memset(ci, 0, sizeof *ci);
67 return (ci);
69 return (xcalloc(1, sizeof *ci));
72 static void
73 screen_write_free_citem(struct screen_write_citem *ci)
75 TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
78 static void
79 screen_write_offset_timer(__unused int fd, __unused short events, void *data)
81 struct window *w = data;
83 tty_update_window_offset(w);
86 /* Set cursor position. */
87 static void
88 screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
90 struct window_pane *wp = ctx->wp;
91 struct window *w;
92 struct screen *s = ctx->s;
93 struct timeval tv = { .tv_usec = 10000 };
95 if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
96 return;
98 if (cx != -1) {
99 if ((u_int)cx > screen_size_x(s)) /* allow last column */
100 cx = screen_size_x(s) - 1;
101 s->cx = cx;
103 if (cy != -1) {
104 if ((u_int)cy > screen_size_y(s) - 1)
105 cy = screen_size_y(s) - 1;
106 s->cy = cy;
109 if (wp == NULL)
110 return;
111 w = wp->window;
113 if (!event_initialized(&w->offset_timer))
114 evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
115 if (!evtimer_pending(&w->offset_timer, NULL))
116 evtimer_add(&w->offset_timer, &tv);
119 /* Do a full redraw. */
120 static void
121 screen_write_redraw_cb(const struct tty_ctx *ttyctx)
123 struct window_pane *wp = ttyctx->arg;
125 if (wp != NULL)
126 wp->flags |= PANE_REDRAW;
129 /* Update context for client. */
130 static int
131 screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
133 struct window_pane *wp = ttyctx->arg;
135 if (c->session->curw->window != wp->window)
136 return (0);
137 if (wp->layout_cell == NULL)
138 return (0);
140 if (wp->flags & (PANE_REDRAW|PANE_DROP))
141 return (-1);
142 if (c->flags & CLIENT_REDRAWPANES) {
144 * Redraw is already deferred to redraw another pane - redraw
145 * this one also when that happens.
147 log_debug("%s: adding %%%u to deferred redraw", __func__,
148 wp->id);
149 wp->flags |= PANE_REDRAW;
150 return (-1);
153 ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
154 &ttyctx->wsx, &ttyctx->wsy);
156 ttyctx->xoff = ttyctx->rxoff = wp->xoff;
157 ttyctx->yoff = ttyctx->ryoff = wp->yoff;
159 if (status_at_line(c) == 0)
160 ttyctx->yoff += status_line_size(c);
162 return (1);
165 /* Set up context for TTY command. */
166 static void
167 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
168 int sync)
170 struct screen *s = ctx->s;
172 memset(ttyctx, 0, sizeof *ttyctx);
174 ttyctx->s = s;
175 ttyctx->sx = screen_size_x(s);
176 ttyctx->sy = screen_size_y(s);
178 ttyctx->ocx = s->cx;
179 ttyctx->ocy = s->cy;
180 ttyctx->orlower = s->rlower;
181 ttyctx->orupper = s->rupper;
183 memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
184 if (ctx->init_ctx_cb != NULL) {
185 ctx->init_ctx_cb(ctx, ttyctx);
186 if (ttyctx->palette != NULL) {
187 ttyctx->defaults.fg = ttyctx->palette->fg;
188 ttyctx->defaults.bg = ttyctx->palette->bg;
190 } else {
191 ttyctx->redraw_cb = screen_write_redraw_cb;
192 if (ctx->wp != NULL) {
193 tty_default_colours(&ttyctx->defaults, ctx->wp);
194 ttyctx->palette = &ctx->wp->palette;
195 ttyctx->set_client_cb = screen_write_set_client_cb;
196 ttyctx->arg = ctx->wp;
200 if (~ctx->flags & SCREEN_WRITE_SYNC) {
202 * For the active pane or for an overlay (no pane), we want to
203 * only use synchronized updates if requested (commands that
204 * move the cursor); for other panes, always use it, since the
205 * cursor will have to move.
207 if (ctx->wp != NULL) {
208 if (ctx->wp != ctx->wp->window->active)
209 ttyctx->num = 1;
210 else
211 ttyctx->num = sync;
212 } else
213 ttyctx->num = 0x10|sync;
214 tty_write(tty_cmd_syncstart, ttyctx);
215 ctx->flags |= SCREEN_WRITE_SYNC;
219 /* Make write list. */
220 void
221 screen_write_make_list(struct screen *s)
223 u_int y;
225 s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
226 for (y = 0; y < screen_size_y(s); y++)
227 TAILQ_INIT(&s->write_list[y].items);
230 /* Free write list. */
231 void
232 screen_write_free_list(struct screen *s)
234 u_int y;
236 for (y = 0; y < screen_size_y(s); y++)
237 free(s->write_list[y].data);
238 free(s->write_list);
241 /* Set up for writing. */
242 static void
243 screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
245 memset(ctx, 0, sizeof *ctx);
247 ctx->s = s;
249 if (ctx->s->write_list == NULL)
250 screen_write_make_list(ctx->s);
251 ctx->item = screen_write_get_citem();
253 ctx->scrolled = 0;
254 ctx->bg = 8;
257 /* Initialize writing with a pane. */
258 void
259 screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
260 struct screen *s)
262 if (s == NULL)
263 s = wp->screen;
264 screen_write_init(ctx, s);
265 ctx->wp = wp;
267 if (log_get_level() != 0) {
268 log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
269 __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
270 wp->id, wp->xoff, wp->yoff);
274 /* Initialize writing with a callback. */
275 void
276 screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
277 screen_write_init_ctx_cb cb, void *arg)
279 screen_write_init(ctx, s);
281 ctx->init_ctx_cb = cb;
282 ctx->arg = arg;
284 if (log_get_level() != 0) {
285 log_debug("%s: size %ux%u, with callback", __func__,
286 screen_size_x(ctx->s), screen_size_y(ctx->s));
290 /* Initialize writing. */
291 void
292 screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
294 screen_write_init(ctx, s);
296 if (log_get_level() != 0) {
297 log_debug("%s: size %ux%u, no pane", __func__,
298 screen_size_x(ctx->s), screen_size_y(ctx->s));
302 /* Finish writing. */
303 void
304 screen_write_stop(struct screen_write_ctx *ctx)
306 screen_write_collect_end(ctx);
307 screen_write_collect_flush(ctx, 0, __func__);
309 screen_write_free_citem(ctx->item);
312 /* Reset screen state. */
313 void
314 screen_write_reset(struct screen_write_ctx *ctx)
316 struct screen *s = ctx->s;
318 screen_reset_tabs(s);
319 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
321 s->mode = MODE_CURSOR | MODE_WRAP;
323 screen_write_clearscreen(ctx, 8);
324 screen_write_set_cursor(ctx, 0, 0);
327 /* Write character. */
328 void
329 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
330 u_char ch)
332 struct grid_cell gc;
334 memcpy(&gc, gcp, sizeof gc);
336 utf8_set(&gc.data, ch);
337 screen_write_cell(ctx, &gc);
340 /* Calculate string length. */
341 size_t
342 screen_write_strlen(const char *fmt, ...)
344 va_list ap;
345 char *msg;
346 struct utf8_data ud;
347 u_char *ptr;
348 size_t left, size = 0;
349 enum utf8_state more;
351 va_start(ap, fmt);
352 xvasprintf(&msg, fmt, ap);
353 va_end(ap);
355 ptr = msg;
356 while (*ptr != '\0') {
357 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
358 ptr++;
360 left = strlen(ptr);
361 if (left < (size_t)ud.size - 1)
362 break;
363 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
364 ptr++;
365 ptr++;
367 if (more == UTF8_DONE)
368 size += ud.width;
369 } else {
370 if (*ptr > 0x1f && *ptr < 0x7f)
371 size++;
372 ptr++;
376 free(msg);
377 return (size);
380 /* Write string wrapped over lines. */
382 screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
383 u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
385 struct screen *s = ctx->s;
386 va_list ap;
387 char *tmp;
388 u_int cy = s->cy, i, end, next, idx = 0, at, left;
389 struct utf8_data *text;
390 struct grid_cell gc;
392 memcpy(&gc, gcp, sizeof gc);
394 va_start(ap, fmt);
395 xvasprintf(&tmp, fmt, ap);
396 va_end(ap);
398 text = utf8_fromcstr(tmp);
399 free(tmp);
401 left = (cx + width) - s->cx;
402 for (;;) {
403 /* Find the end of what can fit on the line. */
404 at = 0;
405 for (end = idx; text[end].size != 0; end++) {
406 if (text[end].size == 1 && text[end].data[0] == '\n')
407 break;
408 if (at + text[end].width > left)
409 break;
410 at += text[end].width;
414 * If we're on a space, that's the end. If not, walk back to
415 * try and find one.
417 if (text[end].size == 0)
418 next = end;
419 else if (text[end].size == 1 && text[end].data[0] == '\n')
420 next = end + 1;
421 else if (text[end].size == 1 && text[end].data[0] == ' ')
422 next = end + 1;
423 else {
424 for (i = end; i > idx; i--) {
425 if (text[i].size == 1 && text[i].data[0] == ' ')
426 break;
428 if (i != idx) {
429 next = i + 1;
430 end = i;
431 } else
432 next = end;
435 /* Print the line. */
436 for (i = idx; i < end; i++) {
437 utf8_copy(&gc.data, &text[i]);
438 screen_write_cell(ctx, &gc);
441 /* If at the bottom, stop. */
442 idx = next;
443 if (s->cy == cy + lines - 1 || text[idx].size == 0)
444 break;
446 screen_write_cursormove(ctx, cx, s->cy + 1, 0);
447 left = width;
451 * Fail if on the last line and there is more to come or at the end, or
452 * if the text was not entirely consumed.
454 if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
455 text[idx].size != 0) {
456 free(text);
457 return (0);
459 free(text);
462 * If no more to come, move to the next line. Otherwise, leave on
463 * the same line (except if at the end).
465 if (!more || s->cx == cx + width)
466 screen_write_cursormove(ctx, cx, s->cy + 1, 0);
467 return (1);
470 /* Write simple string (no maximum length). */
471 void
472 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
473 const char *fmt, ...)
475 va_list ap;
477 va_start(ap, fmt);
478 screen_write_vnputs(ctx, -1, gcp, fmt, ap);
479 va_end(ap);
482 /* Write string with length limit (-1 for unlimited). */
483 void
484 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
485 const struct grid_cell *gcp, const char *fmt, ...)
487 va_list ap;
489 va_start(ap, fmt);
490 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
491 va_end(ap);
494 void
495 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
496 const struct grid_cell *gcp, const char *fmt, va_list ap)
498 struct grid_cell gc;
499 struct utf8_data *ud = &gc.data;
500 char *msg;
501 u_char *ptr;
502 size_t left, size = 0;
503 enum utf8_state more;
505 memcpy(&gc, gcp, sizeof gc);
506 xvasprintf(&msg, fmt, ap);
508 ptr = msg;
509 while (*ptr != '\0') {
510 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
511 ptr++;
513 left = strlen(ptr);
514 if (left < (size_t)ud->size - 1)
515 break;
516 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
517 ptr++;
518 ptr++;
520 if (more != UTF8_DONE)
521 continue;
522 if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
523 while (size < (size_t)maxlen) {
524 screen_write_putc(ctx, &gc, ' ');
525 size++;
527 break;
529 size += ud->width;
530 screen_write_cell(ctx, &gc);
531 } else {
532 if (maxlen > 0 && size + 1 > (size_t)maxlen)
533 break;
535 if (*ptr == '\001')
536 gc.attr ^= GRID_ATTR_CHARSET;
537 else if (*ptr == '\n') {
538 screen_write_linefeed(ctx, 0, 8);
539 screen_write_carriagereturn(ctx);
540 } else if (*ptr > 0x1f && *ptr < 0x7f) {
541 size++;
542 screen_write_putc(ctx, &gc, *ptr);
544 ptr++;
548 free(msg);
552 * Copy from another screen but without the selection stuff. Assumes the target
553 * region is already big enough.
555 void
556 screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
557 u_int px, u_int py, u_int nx, u_int ny)
559 struct screen *s = ctx->s;
560 struct grid *gd = src->grid;
561 struct grid_cell gc;
562 u_int xx, yy, cx, cy;
564 if (nx == 0 || ny == 0)
565 return;
567 cy = s->cy;
568 for (yy = py; yy < py + ny; yy++) {
569 if (yy >= gd->hsize + gd->sy)
570 break;
571 cx = s->cx;
572 for (xx = px; xx < px + nx; xx++) {
573 if (xx >= grid_get_line(gd, yy)->cellsize)
574 break;
575 grid_get_cell(gd, xx, yy, &gc);
576 if (xx + gc.data.width > px + nx)
577 break;
578 grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
579 cx++;
581 cy++;
585 /* Draw a horizontal line on screen. */
586 void
587 screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
589 struct screen *s = ctx->s;
590 struct grid_cell gc;
591 u_int cx, cy, i;
593 cx = s->cx;
594 cy = s->cy;
596 memcpy(&gc, &grid_default_cell, sizeof gc);
597 gc.attr |= GRID_ATTR_CHARSET;
599 screen_write_putc(ctx, &gc, left ? 't' : 'q');
600 for (i = 1; i < nx - 1; i++)
601 screen_write_putc(ctx, &gc, 'q');
602 screen_write_putc(ctx, &gc, right ? 'u' : 'q');
604 screen_write_set_cursor(ctx, cx, cy);
607 /* Draw a vertical line on screen. */
608 void
609 screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
611 struct screen *s = ctx->s;
612 struct grid_cell gc;
613 u_int cx, cy, i;
615 cx = s->cx;
616 cy = s->cy;
618 memcpy(&gc, &grid_default_cell, sizeof gc);
619 gc.attr |= GRID_ATTR_CHARSET;
621 screen_write_putc(ctx, &gc, top ? 'w' : 'x');
622 for (i = 1; i < ny - 1; i++) {
623 screen_write_set_cursor(ctx, cx, cy + i);
624 screen_write_putc(ctx, &gc, 'x');
626 screen_write_set_cursor(ctx, cx, cy + ny - 1);
627 screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
629 screen_write_set_cursor(ctx, cx, cy);
632 /* Draw a menu on screen. */
633 void
634 screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu,
635 int choice, const struct grid_cell *choice_gc)
637 struct screen *s = ctx->s;
638 struct grid_cell default_gc;
639 const struct grid_cell *gc = &default_gc;
640 u_int cx, cy, i, j;
641 const char *name;
643 cx = s->cx;
644 cy = s->cy;
646 memcpy(&default_gc, &grid_default_cell, sizeof default_gc);
648 screen_write_box(ctx, menu->width + 4, menu->count + 2);
649 screen_write_cursormove(ctx, cx + 2, cy, 0);
650 format_draw(ctx, &default_gc, menu->width, menu->title, NULL);
652 for (i = 0; i < menu->count; i++) {
653 name = menu->items[i].name;
654 if (name == NULL) {
655 screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
656 screen_write_hline(ctx, menu->width + 4, 1, 1);
657 } else {
658 if (choice >= 0 && i == (u_int)choice && *name != '-')
659 gc = choice_gc;
660 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
661 for (j = 0; j < menu->width; j++)
662 screen_write_putc(ctx, gc, ' ');
663 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
664 if (*name == '-') {
665 name++;
666 default_gc.attr |= GRID_ATTR_DIM;
667 format_draw(ctx, gc, menu->width, name, NULL);
668 default_gc.attr &= ~GRID_ATTR_DIM;
669 } else
670 format_draw(ctx, gc, menu->width, name, NULL);
671 gc = &default_gc;
675 screen_write_set_cursor(ctx, cx, cy);
678 /* Draw a box on screen. */
679 void
680 screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny)
682 struct screen *s = ctx->s;
683 struct grid_cell gc;
684 u_int cx, cy, i;
686 cx = s->cx;
687 cy = s->cy;
689 memcpy(&gc, &grid_default_cell, sizeof gc);
690 gc.attr |= GRID_ATTR_CHARSET;
691 gc.flags |= GRID_FLAG_NOPALETTE;
693 screen_write_putc(ctx, &gc, 'l');
694 for (i = 1; i < nx - 1; i++)
695 screen_write_putc(ctx, &gc, 'q');
696 screen_write_putc(ctx, &gc, 'k');
698 screen_write_set_cursor(ctx, cx, cy + ny - 1);
699 screen_write_putc(ctx, &gc, 'm');
700 for (i = 1; i < nx - 1; i++)
701 screen_write_putc(ctx, &gc, 'q');
702 screen_write_putc(ctx, &gc, 'j');
704 for (i = 1; i < ny - 1; i++) {
705 screen_write_set_cursor(ctx, cx, cy + i);
706 screen_write_putc(ctx, &gc, 'x');
708 for (i = 1; i < ny - 1; i++) {
709 screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
710 screen_write_putc(ctx, &gc, 'x');
713 screen_write_set_cursor(ctx, cx, cy);
717 * Write a preview version of a window. Assumes target area is big enough and
718 * already cleared.
720 void
721 screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
722 u_int ny)
724 struct screen *s = ctx->s;
725 struct grid_cell gc;
726 u_int cx, cy, px, py;
728 cx = s->cx;
729 cy = s->cy;
732 * If the cursor is on, pick the area around the cursor, otherwise use
733 * the top left.
735 if (src->mode & MODE_CURSOR) {
736 px = src->cx;
737 if (px < nx / 3)
738 px = 0;
739 else
740 px = px - nx / 3;
741 if (px + nx > screen_size_x(src)) {
742 if (nx > screen_size_x(src))
743 px = 0;
744 else
745 px = screen_size_x(src) - nx;
747 py = src->cy;
748 if (py < ny / 3)
749 py = 0;
750 else
751 py = py - ny / 3;
752 if (py + ny > screen_size_y(src)) {
753 if (ny > screen_size_y(src))
754 py = 0;
755 else
756 py = screen_size_y(src) - ny;
758 } else {
759 px = 0;
760 py = 0;
763 screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
765 if (src->mode & MODE_CURSOR) {
766 grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
767 gc.attr |= GRID_ATTR_REVERSE;
768 screen_write_set_cursor(ctx, cx + (src->cx - px),
769 cy + (src->cy - py));
770 screen_write_cell(ctx, &gc);
774 /* Set a mode. */
775 void
776 screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
778 struct screen *s = ctx->s;
780 s->mode |= mode;
782 if (log_get_level() != 0)
783 log_debug("%s: %s", __func__, screen_mode_to_string(mode));
786 /* Clear a mode. */
787 void
788 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
790 struct screen *s = ctx->s;
792 s->mode &= ~mode;
794 if (log_get_level() != 0)
795 log_debug("%s: %s", __func__, screen_mode_to_string(mode));
798 /* Cursor up by ny. */
799 void
800 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
802 struct screen *s = ctx->s;
803 u_int cx = s->cx, cy = s->cy;
805 if (ny == 0)
806 ny = 1;
808 if (cy < s->rupper) {
809 /* Above region. */
810 if (ny > cy)
811 ny = cy;
812 } else {
813 /* Below region. */
814 if (ny > cy - s->rupper)
815 ny = cy - s->rupper;
817 if (cx == screen_size_x(s))
818 cx--;
820 cy -= ny;
822 screen_write_set_cursor(ctx, cx, cy);
825 /* Cursor down by ny. */
826 void
827 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
829 struct screen *s = ctx->s;
830 u_int cx = s->cx, cy = s->cy;
832 if (ny == 0)
833 ny = 1;
835 if (cy > s->rlower) {
836 /* Below region. */
837 if (ny > screen_size_y(s) - 1 - cy)
838 ny = screen_size_y(s) - 1 - cy;
839 } else {
840 /* Above region. */
841 if (ny > s->rlower - cy)
842 ny = s->rlower - cy;
844 if (cx == screen_size_x(s))
845 cx--;
846 else if (ny == 0)
847 return;
849 cy += ny;
851 screen_write_set_cursor(ctx, cx, cy);
854 /* Cursor right by nx. */
855 void
856 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
858 struct screen *s = ctx->s;
859 u_int cx = s->cx, cy = s->cy;
861 if (nx == 0)
862 nx = 1;
864 if (nx > screen_size_x(s) - 1 - cx)
865 nx = screen_size_x(s) - 1 - cx;
866 if (nx == 0)
867 return;
869 cx += nx;
871 screen_write_set_cursor(ctx, cx, cy);
874 /* Cursor left by nx. */
875 void
876 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
878 struct screen *s = ctx->s;
879 u_int cx = s->cx, cy = s->cy;
881 if (nx == 0)
882 nx = 1;
884 if (nx > cx)
885 nx = cx;
886 if (nx == 0)
887 return;
889 cx -= nx;
891 screen_write_set_cursor(ctx, cx, cy);
894 /* Backspace; cursor left unless at start of wrapped line when can move up. */
895 void
896 screen_write_backspace(struct screen_write_ctx *ctx)
898 struct screen *s = ctx->s;
899 struct grid_line *gl;
900 u_int cx = s->cx, cy = s->cy;
902 if (cx == 0) {
903 if (cy == 0)
904 return;
905 gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
906 if (gl->flags & GRID_LINE_WRAPPED) {
907 cy--;
908 cx = screen_size_x(s) - 1;
910 } else
911 cx--;
913 screen_write_set_cursor(ctx, cx, cy);
916 /* VT100 alignment test. */
917 void
918 screen_write_alignmenttest(struct screen_write_ctx *ctx)
920 struct screen *s = ctx->s;
921 struct tty_ctx ttyctx;
922 struct grid_cell gc;
923 u_int xx, yy;
925 memcpy(&gc, &grid_default_cell, sizeof gc);
926 utf8_set(&gc.data, 'E');
928 for (yy = 0; yy < screen_size_y(s); yy++) {
929 for (xx = 0; xx < screen_size_x(s); xx++)
930 grid_view_set_cell(s->grid, xx, yy, &gc);
933 screen_write_set_cursor(ctx, 0, 0);
935 s->rupper = 0;
936 s->rlower = screen_size_y(s) - 1;
938 screen_write_initctx(ctx, &ttyctx, 1);
940 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
941 tty_write(tty_cmd_alignmenttest, &ttyctx);
944 /* Insert nx characters. */
945 void
946 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
948 struct screen *s = ctx->s;
949 struct tty_ctx ttyctx;
951 if (nx == 0)
952 nx = 1;
954 if (nx > screen_size_x(s) - s->cx)
955 nx = screen_size_x(s) - s->cx;
956 if (nx == 0)
957 return;
959 if (s->cx > screen_size_x(s) - 1)
960 return;
962 screen_write_initctx(ctx, &ttyctx, 0);
963 ttyctx.bg = bg;
965 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
967 screen_write_collect_flush(ctx, 0, __func__);
968 ttyctx.num = nx;
969 tty_write(tty_cmd_insertcharacter, &ttyctx);
972 /* Delete nx characters. */
973 void
974 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
976 struct screen *s = ctx->s;
977 struct tty_ctx ttyctx;
979 if (nx == 0)
980 nx = 1;
982 if (nx > screen_size_x(s) - s->cx)
983 nx = screen_size_x(s) - s->cx;
984 if (nx == 0)
985 return;
987 if (s->cx > screen_size_x(s) - 1)
988 return;
990 screen_write_initctx(ctx, &ttyctx, 0);
991 ttyctx.bg = bg;
993 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
995 screen_write_collect_flush(ctx, 0, __func__);
996 ttyctx.num = nx;
997 tty_write(tty_cmd_deletecharacter, &ttyctx);
1000 /* Clear nx characters. */
1001 void
1002 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1004 struct screen *s = ctx->s;
1005 struct tty_ctx ttyctx;
1007 if (nx == 0)
1008 nx = 1;
1010 if (nx > screen_size_x(s) - s->cx)
1011 nx = screen_size_x(s) - s->cx;
1012 if (nx == 0)
1013 return;
1015 if (s->cx > screen_size_x(s) - 1)
1016 return;
1018 screen_write_initctx(ctx, &ttyctx, 0);
1019 ttyctx.bg = bg;
1021 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1023 screen_write_collect_flush(ctx, 0, __func__);
1024 ttyctx.num = nx;
1025 tty_write(tty_cmd_clearcharacter, &ttyctx);
1028 /* Insert ny lines. */
1029 void
1030 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1032 struct screen *s = ctx->s;
1033 struct grid *gd = s->grid;
1034 struct tty_ctx ttyctx;
1036 if (ny == 0)
1037 ny = 1;
1039 if (s->cy < s->rupper || s->cy > s->rlower) {
1040 if (ny > screen_size_y(s) - s->cy)
1041 ny = screen_size_y(s) - s->cy;
1042 if (ny == 0)
1043 return;
1045 screen_write_initctx(ctx, &ttyctx, 1);
1046 ttyctx.bg = bg;
1048 grid_view_insert_lines(gd, s->cy, ny, bg);
1050 screen_write_collect_flush(ctx, 0, __func__);
1051 ttyctx.num = ny;
1052 tty_write(tty_cmd_insertline, &ttyctx);
1053 return;
1056 if (ny > s->rlower + 1 - s->cy)
1057 ny = s->rlower + 1 - s->cy;
1058 if (ny == 0)
1059 return;
1061 screen_write_initctx(ctx, &ttyctx, 1);
1062 ttyctx.bg = bg;
1064 if (s->cy < s->rupper || s->cy > s->rlower)
1065 grid_view_insert_lines(gd, s->cy, ny, bg);
1066 else
1067 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1069 screen_write_collect_flush(ctx, 0, __func__);
1071 ttyctx.num = ny;
1072 tty_write(tty_cmd_insertline, &ttyctx);
1075 /* Delete ny lines. */
1076 void
1077 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1079 struct screen *s = ctx->s;
1080 struct grid *gd = s->grid;
1081 struct tty_ctx ttyctx;
1083 if (ny == 0)
1084 ny = 1;
1086 if (s->cy < s->rupper || s->cy > s->rlower) {
1087 if (ny > screen_size_y(s) - s->cy)
1088 ny = screen_size_y(s) - s->cy;
1089 if (ny == 0)
1090 return;
1092 screen_write_initctx(ctx, &ttyctx, 1);
1093 ttyctx.bg = bg;
1095 grid_view_delete_lines(gd, s->cy, ny, bg);
1097 screen_write_collect_flush(ctx, 0, __func__);
1098 ttyctx.num = ny;
1099 tty_write(tty_cmd_deleteline, &ttyctx);
1100 return;
1103 if (ny > s->rlower + 1 - s->cy)
1104 ny = s->rlower + 1 - s->cy;
1105 if (ny == 0)
1106 return;
1108 screen_write_initctx(ctx, &ttyctx, 1);
1109 ttyctx.bg = bg;
1111 if (s->cy < s->rupper || s->cy > s->rlower)
1112 grid_view_delete_lines(gd, s->cy, ny, bg);
1113 else
1114 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1116 screen_write_collect_flush(ctx, 0, __func__);
1117 ttyctx.num = ny;
1118 tty_write(tty_cmd_deleteline, &ttyctx);
1121 /* Clear line at cursor. */
1122 void
1123 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1125 struct screen *s = ctx->s;
1126 struct grid_line *gl;
1127 u_int sx = screen_size_x(s);
1128 struct screen_write_citem *ci = ctx->item;
1130 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1131 if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1132 return;
1134 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1136 screen_write_collect_clear(ctx, s->cy, 1);
1137 ci->x = 0;
1138 ci->used = sx;
1139 ci->type = CLEAR;
1140 ci->bg = bg;
1141 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1142 ctx->item = screen_write_get_citem();
1145 /* Clear to end of line from cursor. */
1146 void
1147 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1149 struct screen *s = ctx->s;
1150 struct grid_line *gl;
1151 u_int sx = screen_size_x(s);
1152 struct screen_write_citem *ci = ctx->item, *before;
1154 if (s->cx == 0) {
1155 screen_write_clearline(ctx, bg);
1156 return;
1159 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1160 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1161 return;
1163 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1165 before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1166 ci->x = s->cx;
1167 ci->used = sx - s->cx;
1168 ci->type = CLEAR;
1169 ci->bg = bg;
1170 if (before == NULL)
1171 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1172 else
1173 TAILQ_INSERT_BEFORE(before, ci, entry);
1174 ctx->item = screen_write_get_citem();
1177 /* Clear to start of line from cursor. */
1178 void
1179 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1181 struct screen *s = ctx->s;
1182 u_int sx = screen_size_x(s);
1183 struct screen_write_citem *ci = ctx->item, *before;
1185 if (s->cx >= sx - 1) {
1186 screen_write_clearline(ctx, bg);
1187 return;
1190 if (s->cx > sx - 1)
1191 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1192 else
1193 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1195 before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1196 ci->x = 0;
1197 ci->used = s->cx + 1;
1198 ci->type = CLEAR;
1199 ci->bg = bg;
1200 if (before == NULL)
1201 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1202 else
1203 TAILQ_INSERT_BEFORE(before, ci, entry);
1204 ctx->item = screen_write_get_citem();
1207 /* Move cursor to px,py. */
1208 void
1209 screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1210 int origin)
1212 struct screen *s = ctx->s;
1214 if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1215 if ((u_int)py > s->rlower - s->rupper)
1216 py = s->rlower;
1217 else
1218 py += s->rupper;
1221 if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1222 px = screen_size_x(s) - 1;
1223 if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1224 py = screen_size_y(s) - 1;
1226 log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1227 screen_write_set_cursor(ctx, px, py);
1230 /* Reverse index (up with scroll). */
1231 void
1232 screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1234 struct screen *s = ctx->s;
1235 struct tty_ctx ttyctx;
1237 if (s->cy == s->rupper) {
1238 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1239 screen_write_collect_flush(ctx, 0, __func__);
1241 screen_write_initctx(ctx, &ttyctx, 1);
1242 ttyctx.bg = bg;
1244 tty_write(tty_cmd_reverseindex, &ttyctx);
1245 } else if (s->cy > 0)
1246 screen_write_set_cursor(ctx, -1, s->cy - 1);
1250 /* Set scroll region. */
1251 void
1252 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1253 u_int rlower)
1255 struct screen *s = ctx->s;
1257 if (rupper > screen_size_y(s) - 1)
1258 rupper = screen_size_y(s) - 1;
1259 if (rlower > screen_size_y(s) - 1)
1260 rlower = screen_size_y(s) - 1;
1261 if (rupper >= rlower) /* cannot be one line */
1262 return;
1264 screen_write_collect_flush(ctx, 0, __func__);
1266 /* Cursor moves to top-left. */
1267 screen_write_set_cursor(ctx, 0, 0);
1269 s->rupper = rupper;
1270 s->rlower = rlower;
1273 /* Line feed. */
1274 void
1275 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1277 struct screen *s = ctx->s;
1278 struct grid *gd = s->grid;
1279 struct grid_line *gl;
1281 gl = grid_get_line(gd, gd->hsize + s->cy);
1282 if (wrapped)
1283 gl->flags |= GRID_LINE_WRAPPED;
1285 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1286 s->rupper, s->rlower);
1288 if (bg != ctx->bg) {
1289 screen_write_collect_flush(ctx, 1, __func__);
1290 ctx->bg = bg;
1293 if (s->cy == s->rlower) {
1294 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1295 screen_write_collect_scroll(ctx, bg);
1296 ctx->scrolled++;
1297 } else if (s->cy < screen_size_y(s) - 1)
1298 screen_write_set_cursor(ctx, -1, s->cy + 1);
1301 /* Scroll up. */
1302 void
1303 screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1305 struct screen *s = ctx->s;
1306 struct grid *gd = s->grid;
1307 u_int i;
1309 if (lines == 0)
1310 lines = 1;
1311 else if (lines > s->rlower - s->rupper + 1)
1312 lines = s->rlower - s->rupper + 1;
1314 if (bg != ctx->bg) {
1315 screen_write_collect_flush(ctx, 1, __func__);
1316 ctx->bg = bg;
1319 for (i = 0; i < lines; i++) {
1320 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1321 screen_write_collect_scroll(ctx, bg);
1323 ctx->scrolled += lines;
1326 /* Scroll down. */
1327 void
1328 screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1330 struct screen *s = ctx->s;
1331 struct grid *gd = s->grid;
1332 struct tty_ctx ttyctx;
1333 u_int i;
1335 screen_write_initctx(ctx, &ttyctx, 1);
1336 ttyctx.bg = bg;
1338 if (lines == 0)
1339 lines = 1;
1340 else if (lines > s->rlower - s->rupper + 1)
1341 lines = s->rlower - s->rupper + 1;
1343 for (i = 0; i < lines; i++)
1344 grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1346 screen_write_collect_flush(ctx, 0, __func__);
1347 ttyctx.num = lines;
1348 tty_write(tty_cmd_scrolldown, &ttyctx);
1351 /* Carriage return (cursor to start of line). */
1352 void
1353 screen_write_carriagereturn(struct screen_write_ctx *ctx)
1355 screen_write_set_cursor(ctx, 0, -1);
1358 /* Clear to end of screen from cursor. */
1359 void
1360 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1362 struct screen *s = ctx->s;
1363 struct grid *gd = s->grid;
1364 struct tty_ctx ttyctx;
1365 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1367 screen_write_initctx(ctx, &ttyctx, 1);
1368 ttyctx.bg = bg;
1370 /* Scroll into history if it is enabled and clearing entire screen. */
1371 if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY))
1372 grid_view_clear_history(gd, bg);
1373 else {
1374 if (s->cx <= sx - 1)
1375 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1376 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1379 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1380 screen_write_collect_flush(ctx, 0, __func__);
1381 tty_write(tty_cmd_clearendofscreen, &ttyctx);
1384 /* Clear to start of screen. */
1385 void
1386 screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1388 struct screen *s = ctx->s;
1389 struct tty_ctx ttyctx;
1390 u_int sx = screen_size_x(s);
1392 screen_write_initctx(ctx, &ttyctx, 1);
1393 ttyctx.bg = bg;
1395 if (s->cy > 0)
1396 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1397 if (s->cx > sx - 1)
1398 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1399 else
1400 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1402 screen_write_collect_clear(ctx, 0, s->cy);
1403 screen_write_collect_flush(ctx, 0, __func__);
1404 tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1407 /* Clear entire screen. */
1408 void
1409 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1411 struct screen *s = ctx->s;
1412 struct tty_ctx ttyctx;
1413 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1415 screen_write_initctx(ctx, &ttyctx, 1);
1416 ttyctx.bg = bg;
1418 /* Scroll into history if it is enabled. */
1419 if (s->grid->flags & GRID_HISTORY)
1420 grid_view_clear_history(s->grid, bg);
1421 else
1422 grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1424 screen_write_collect_clear(ctx, 0, sy);
1425 tty_write(tty_cmd_clearscreen, &ttyctx);
1428 /* Clear entire history. */
1429 void
1430 screen_write_clearhistory(struct screen_write_ctx *ctx)
1432 grid_clear_history(ctx->s->grid);
1435 /* Force a full redraw. */
1436 void
1437 screen_write_fullredraw(struct screen_write_ctx *ctx)
1439 struct tty_ctx ttyctx;
1441 screen_write_collect_flush(ctx, 0, __func__);
1443 screen_write_initctx(ctx, &ttyctx, 1);
1444 ttyctx.redraw_cb(&ttyctx);
1447 /* Trim collected items. */
1448 static struct screen_write_citem *
1449 screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1450 u_int used, int *wrapped)
1452 struct screen_write_cline *cl = &ctx->s->write_list[y];
1453 struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
1454 u_int sx = x, ex = x + used - 1;
1455 u_int csx, cex;
1457 if (TAILQ_EMPTY(&cl->items))
1458 return (NULL);
1459 TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1460 csx = ci->x;
1461 cex = ci->x + ci->used - 1;
1463 /* Item is entirely before. */
1464 if (cex < sx) {
1465 log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1466 csx, cex, sx, ex);
1467 continue;
1470 /* Item is entirely after. */
1471 if (csx > ex) {
1472 log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1473 csx, cex, sx, ex);
1474 before = ci;
1475 break;
1478 /* Item is entirely inside. */
1479 if (csx >= sx && cex <= ex) {
1480 log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1481 csx, cex, sx, ex);
1482 TAILQ_REMOVE(&cl->items, ci, entry);
1483 screen_write_free_citem(ci);
1484 if (csx == 0 && ci->wrapped && wrapped != NULL)
1485 *wrapped = 1;
1486 continue;
1489 /* Item under the start. */
1490 if (csx < sx && cex >= sx && cex <= ex) {
1491 log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1492 csx, cex, sx, ex);
1493 ci->used = sx - csx;
1494 log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1495 ci->x + ci->used + 1);
1496 continue;
1499 /* Item covers the end. */
1500 if (cex > ex && csx >= sx && csx <= ex) {
1501 log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1502 csx, cex, sx, ex);
1503 ci->x = ex + 1;
1504 ci->used = cex - ex;
1505 log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1506 ci->x + ci->used + 1);
1507 before = ci;
1508 break;
1511 /* Item must cover both sides. */
1512 log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1513 csx, cex, sx, ex);
1514 ci2 = screen_write_get_citem();
1515 ci2->type = ci->type;
1516 ci2->bg = ci->bg;
1517 memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1518 TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1520 ci->used = sx - csx;
1521 ci2->x = ex + 1;
1522 ci2->used = cex - ex;
1524 log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1525 ci->x, ci->x + ci->used - 1, ci, ci2->x,
1526 ci2->x + ci2->used - 1, ci2);
1527 before = ci2;
1528 break;
1530 return (before);
1533 /* Clear collected lines. */
1534 static void
1535 screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1537 struct screen_write_cline *cl;
1538 u_int i;
1540 for (i = y; i < y + n; i++) {
1541 cl = &ctx->s->write_list[i];
1542 TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1546 /* Scroll collected lines up. */
1547 static void
1548 screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1550 struct screen *s = ctx->s;
1551 struct screen_write_cline *cl;
1552 u_int y;
1553 char *saved;
1554 struct screen_write_citem *ci;
1556 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1557 s->rupper, s->rlower);
1559 screen_write_collect_clear(ctx, s->rupper, 1);
1560 saved = ctx->s->write_list[s->rupper].data;
1561 for (y = s->rupper; y < s->rlower; y++) {
1562 cl = &ctx->s->write_list[y + 1];
1563 TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1564 ctx->s->write_list[y].data = cl->data;
1566 ctx->s->write_list[s->rlower].data = saved;
1568 ci = screen_write_get_citem();
1569 ci->x = 0;
1570 ci->used = screen_size_x(s);
1571 ci->type = CLEAR;
1572 ci->bg = bg;
1573 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1576 /* Flush collected lines. */
1577 static void
1578 screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1579 const char *from)
1581 struct screen *s = ctx->s;
1582 struct screen_write_citem *ci, *tmp;
1583 struct screen_write_cline *cl;
1584 u_int y, cx, cy, last, items = 0;
1585 struct tty_ctx ttyctx;
1587 if (ctx->scrolled != 0) {
1588 log_debug("%s: scrolled %u (region %u-%u)", __func__,
1589 ctx->scrolled, s->rupper, s->rlower);
1590 if (ctx->scrolled > s->rlower - s->rupper + 1)
1591 ctx->scrolled = s->rlower - s->rupper + 1;
1593 screen_write_initctx(ctx, &ttyctx, 1);
1594 ttyctx.num = ctx->scrolled;
1595 ttyctx.bg = ctx->bg;
1596 tty_write(tty_cmd_scrollup, &ttyctx);
1598 ctx->scrolled = 0;
1599 ctx->bg = 8;
1601 if (scroll_only)
1602 return;
1604 cx = s->cx; cy = s->cy;
1605 for (y = 0; y < screen_size_y(s); y++) {
1606 cl = &ctx->s->write_list[y];
1607 last = UINT_MAX;
1608 TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1609 if (last != UINT_MAX && ci->x <= last) {
1610 fatalx("collect list not in order: %u <= %u",
1611 ci->x, last);
1613 screen_write_set_cursor(ctx, ci->x, y);
1614 if (ci->type == CLEAR) {
1615 screen_write_initctx(ctx, &ttyctx, 1);
1616 ttyctx.bg = ci->bg;
1617 ttyctx.num = ci->used;
1618 tty_write(tty_cmd_clearcharacter, &ttyctx);
1619 } else {
1620 screen_write_initctx(ctx, &ttyctx, 0);
1621 ttyctx.cell = &ci->gc;
1622 ttyctx.wrapped = ci->wrapped;
1623 ttyctx.ptr = cl->data + ci->x;
1624 ttyctx.num = ci->used;
1625 tty_write(tty_cmd_cells, &ttyctx);
1627 items++;
1629 TAILQ_REMOVE(&cl->items, ci, entry);
1630 screen_write_free_citem(ci);
1631 last = ci->x;
1634 s->cx = cx; s->cy = cy;
1636 log_debug("%s: flushed %u items (%s)", __func__, items, from);
1639 /* Finish and store collected cells. */
1640 void
1641 screen_write_collect_end(struct screen_write_ctx *ctx)
1643 struct screen *s = ctx->s;
1644 struct screen_write_citem *ci = ctx->item, *before;
1645 struct screen_write_cline *cl = &s->write_list[s->cy];
1646 struct grid_cell gc;
1647 u_int xx;
1648 int wrapped = ci->wrapped;
1650 if (ci->used == 0)
1651 return;
1653 before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1654 &wrapped);
1655 ci->x = s->cx;
1656 ci->wrapped = wrapped;
1657 if (before == NULL)
1658 TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1659 else
1660 TAILQ_INSERT_BEFORE(before, ci, entry);
1661 ctx->item = screen_write_get_citem();
1663 log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1664 (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1666 if (s->cx != 0) {
1667 for (xx = s->cx; xx > 0; xx--) {
1668 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1669 if (~gc.flags & GRID_FLAG_PADDING)
1670 break;
1671 grid_view_set_cell(s->grid, xx, s->cy,
1672 &grid_default_cell);
1674 if (gc.data.width > 1) {
1675 grid_view_set_cell(s->grid, xx, s->cy,
1676 &grid_default_cell);
1680 grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1681 ci->used);
1682 screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1684 for (xx = s->cx; xx < screen_size_x(s); xx++) {
1685 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1686 if (~gc.flags & GRID_FLAG_PADDING)
1687 break;
1688 grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1692 /* Write cell data, collecting if necessary. */
1693 void
1694 screen_write_collect_add(struct screen_write_ctx *ctx,
1695 const struct grid_cell *gc)
1697 struct screen *s = ctx->s;
1698 struct screen_write_citem *ci;
1699 u_int sx = screen_size_x(s);
1700 int collect;
1703 * Don't need to check that the attributes and whatnot are still the
1704 * same - input_parse will end the collection when anything that isn't
1705 * a plain character is encountered.
1708 collect = 1;
1709 if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1710 collect = 0;
1711 else if (gc->attr & GRID_ATTR_CHARSET)
1712 collect = 0;
1713 else if (~s->mode & MODE_WRAP)
1714 collect = 0;
1715 else if (s->mode & MODE_INSERT)
1716 collect = 0;
1717 else if (s->sel != NULL)
1718 collect = 0;
1719 if (!collect) {
1720 screen_write_collect_end(ctx);
1721 screen_write_collect_flush(ctx, 0, __func__);
1722 screen_write_cell(ctx, gc);
1723 return;
1726 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1727 screen_write_collect_end(ctx);
1728 ci = ctx->item; /* may have changed */
1730 if (s->cx > sx - 1) {
1731 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1732 ci->wrapped = 1;
1733 screen_write_linefeed(ctx, 1, 8);
1734 screen_write_set_cursor(ctx, 0, -1);
1737 if (ci->used == 0)
1738 memcpy(&ci->gc, gc, sizeof ci->gc);
1739 if (ctx->s->write_list[s->cy].data == NULL)
1740 ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1741 ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
1744 /* Write cell data. */
1745 void
1746 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1748 struct screen *s = ctx->s;
1749 struct grid *gd = s->grid;
1750 const struct utf8_data *ud = &gc->data;
1751 const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 };
1752 struct grid_line *gl;
1753 struct grid_cell_entry *gce;
1754 struct grid_cell tmp_gc, now_gc;
1755 struct tty_ctx ttyctx;
1756 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1757 u_int width = gc->data.width, xx, last, cx, cy;
1758 int selected, skip = 1;
1760 /* Ignore padding cells. */
1761 if (gc->flags & GRID_FLAG_PADDING)
1762 return;
1765 * If this is a zero width joiner, set the flag so the next character
1766 * will be treated as zero width and appended. Note that we assume a
1767 * ZWJ will not change the width - the width of the first character is
1768 * used.
1770 if (ud->size == 3 && memcmp(ud->data, "\342\200\215", 3) == 0) {
1771 log_debug("zero width joiner at %u,%u", s->cx, s->cy);
1772 ctx->flags |= SCREEN_WRITE_ZWJ;
1773 return;
1777 * If the width is zero, combine onto the previous character. We always
1778 * combine with the cell to the left of the cursor position. In theory,
1779 * the application could have moved the cursor somewhere else, but if
1780 * they are silly enough to do that, who cares?
1782 if (ctx->flags & SCREEN_WRITE_ZWJ) {
1783 screen_write_collect_flush(ctx, 0, __func__);
1784 screen_write_combine(ctx, &zwj, &xx);
1786 if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) {
1787 ctx->flags &= ~SCREEN_WRITE_ZWJ;
1788 screen_write_collect_flush(ctx, 0, __func__);
1789 if ((gc = screen_write_combine(ctx, ud, &xx)) != NULL) {
1790 cx = s->cx; cy = s->cy;
1791 screen_write_set_cursor(ctx, xx, s->cy);
1792 screen_write_initctx(ctx, &ttyctx, 0);
1793 ttyctx.cell = gc;
1794 tty_write(tty_cmd_cell, &ttyctx);
1795 s->cx = cx; s->cy = cy;
1797 return;
1800 /* Flush any existing scrolling. */
1801 screen_write_collect_flush(ctx, 1, __func__);
1803 /* If this character doesn't fit, ignore it. */
1804 if ((~s->mode & MODE_WRAP) &&
1805 width > 1 &&
1806 (width > sx || (s->cx != sx && s->cx > sx - width)))
1807 return;
1809 /* If in insert mode, make space for the cells. */
1810 if (s->mode & MODE_INSERT) {
1811 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1812 skip = 0;
1815 /* Check this will fit on the current line and wrap if not. */
1816 if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1817 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1818 screen_write_linefeed(ctx, 1, 8);
1819 screen_write_set_cursor(ctx, 0, -1);
1820 screen_write_collect_flush(ctx, 1, __func__);
1823 /* Sanity check cursor position. */
1824 if (s->cx > sx - width || s->cy > sy - 1)
1825 return;
1826 screen_write_initctx(ctx, &ttyctx, 0);
1828 /* Handle overwriting of UTF-8 characters. */
1829 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1830 if (gl->flags & GRID_LINE_EXTENDED) {
1831 grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
1832 if (screen_write_overwrite(ctx, &now_gc, width))
1833 skip = 0;
1837 * If the new character is UTF-8 wide, fill in padding cells. Have
1838 * already ensured there is enough room.
1840 for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1841 log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
1842 grid_view_set_padding(gd, xx, s->cy);
1843 skip = 0;
1846 /* If no change, do not draw. */
1847 if (skip) {
1848 if (s->cx >= gl->cellsize)
1849 skip = grid_cells_equal(gc, &grid_default_cell);
1850 else {
1851 gce = &gl->celldata[s->cx];
1852 if (gce->flags & GRID_FLAG_EXTENDED)
1853 skip = 0;
1854 else if (gc->flags != gce->flags)
1855 skip = 0;
1856 else if (gc->attr != gce->data.attr)
1857 skip = 0;
1858 else if (gc->fg != gce->data.fg)
1859 skip = 0;
1860 else if (gc->bg != gce->data.bg)
1861 skip = 0;
1862 else if (gc->data.width != 1)
1863 skip = 0;
1864 else if (gc->data.size != 1)
1865 skip = 0;
1866 else if (gce->data.data != gc->data.data[0])
1867 skip = 0;
1871 /* Update the selected flag and set the cell. */
1872 selected = screen_check_selection(s, s->cx, s->cy);
1873 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
1874 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1875 tmp_gc.flags |= GRID_FLAG_SELECTED;
1876 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1877 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
1878 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1879 tmp_gc.flags &= ~GRID_FLAG_SELECTED;
1880 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1881 } else if (!skip)
1882 grid_view_set_cell(gd, s->cx, s->cy, gc);
1883 if (selected)
1884 skip = 0;
1887 * Move the cursor. If not wrapping, stick at the last character and
1888 * replace it.
1890 last = !(s->mode & MODE_WRAP);
1891 if (s->cx <= sx - last - width)
1892 screen_write_set_cursor(ctx, s->cx + width, -1);
1893 else
1894 screen_write_set_cursor(ctx, sx - last, -1);
1896 /* Create space for character in insert mode. */
1897 if (s->mode & MODE_INSERT) {
1898 screen_write_collect_flush(ctx, 0, __func__);
1899 ttyctx.num = width;
1900 tty_write(tty_cmd_insertcharacter, &ttyctx);
1903 /* Write to the screen. */
1904 if (!skip) {
1905 if (selected) {
1906 screen_select_cell(s, &tmp_gc, gc);
1907 ttyctx.cell = &tmp_gc;
1908 } else
1909 ttyctx.cell = gc;
1910 tty_write(tty_cmd_cell, &ttyctx);
1914 /* Combine a UTF-8 zero-width character onto the previous. */
1915 static const struct grid_cell *
1916 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
1917 u_int *xx)
1919 struct screen *s = ctx->s;
1920 struct grid *gd = s->grid;
1921 static struct grid_cell gc;
1922 u_int n;
1924 /* Can't combine if at 0. */
1925 if (s->cx == 0)
1926 return (NULL);
1928 /* Empty data is out. */
1929 if (ud->size == 0)
1930 fatalx("UTF-8 data empty");
1932 /* Retrieve the previous cell. */
1933 for (n = 1; n <= s->cx; n++) {
1934 grid_view_get_cell(gd, s->cx - n, s->cy, &gc);
1935 if (~gc.flags & GRID_FLAG_PADDING)
1936 break;
1938 if (n > s->cx)
1939 return (NULL);
1940 *xx = s->cx - n;
1942 /* Check there is enough space. */
1943 if (gc.data.size + ud->size > sizeof gc.data.data)
1944 return (NULL);
1946 log_debug("%s: %.*s onto %.*s at %u,%u", __func__, (int)ud->size,
1947 ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy);
1949 /* Append the data. */
1950 memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
1951 gc.data.size += ud->size;
1953 /* Set the new cell. */
1954 grid_view_set_cell(gd, *xx, s->cy, &gc);
1956 return (&gc);
1960 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
1961 * cell on the screen, so following cells must not be drawn by marking them as
1962 * padding.
1964 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
1965 * character, it is necessary to also overwrite any other cells which covered
1966 * by the same character.
1968 static int
1969 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
1970 u_int width)
1972 struct screen *s = ctx->s;
1973 struct grid *gd = s->grid;
1974 struct grid_cell tmp_gc;
1975 u_int xx;
1976 int done = 0;
1978 if (gc->flags & GRID_FLAG_PADDING) {
1980 * A padding cell, so clear any following and leading padding
1981 * cells back to the character. Don't overwrite the current
1982 * cell as that happens later anyway.
1984 xx = s->cx + 1;
1985 while (--xx > 0) {
1986 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
1987 if (~tmp_gc.flags & GRID_FLAG_PADDING)
1988 break;
1989 log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
1990 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1993 /* Overwrite the character at the start of this padding. */
1994 log_debug("%s: character at %u,%u", __func__, xx, s->cy);
1995 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1996 done = 1;
2000 * Overwrite any padding cells that belong to any UTF-8 characters
2001 * we'll be overwriting with the current character.
2003 if (width != 1 ||
2004 gc->data.width != 1 ||
2005 gc->flags & GRID_FLAG_PADDING) {
2006 xx = s->cx + width - 1;
2007 while (++xx < screen_size_x(s)) {
2008 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2009 if (~tmp_gc.flags & GRID_FLAG_PADDING)
2010 break;
2011 log_debug("%s: overwrite at %u,%u", __func__, xx,
2012 s->cy);
2013 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2014 done = 1;
2018 return (done);
2021 /* Set external clipboard. */
2022 void
2023 screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len)
2025 struct tty_ctx ttyctx;
2027 screen_write_initctx(ctx, &ttyctx, 0);
2028 ttyctx.ptr = str;
2029 ttyctx.num = len;
2031 tty_write(tty_cmd_setselection, &ttyctx);
2034 /* Write unmodified string. */
2035 void
2036 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len)
2038 struct tty_ctx ttyctx;
2040 screen_write_initctx(ctx, &ttyctx, 0);
2041 ttyctx.ptr = str;
2042 ttyctx.num = len;
2044 tty_write(tty_cmd_rawstring, &ttyctx);
2047 /* Turn alternate screen on. */
2048 void
2049 screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2050 int cursor)
2052 struct tty_ctx ttyctx;
2053 struct window_pane *wp = ctx->wp;
2055 if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2056 return;
2058 screen_write_collect_flush(ctx, 0, __func__);
2059 screen_alternate_on(ctx->s, gc, cursor);
2061 screen_write_initctx(ctx, &ttyctx, 1);
2062 ttyctx.redraw_cb(&ttyctx);
2065 /* Turn alternate screen off. */
2066 void
2067 screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2068 int cursor)
2070 struct tty_ctx ttyctx;
2071 struct window_pane *wp = ctx->wp;
2073 if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2074 return;
2076 screen_write_collect_flush(ctx, 0, __func__);
2077 screen_alternate_off(ctx->s, gc, cursor);
2079 screen_write_initctx(ctx, &ttyctx, 1);
2080 ttyctx.redraw_cb(&ttyctx);