Ignore the user keys range when checking if a key is Unicode.
[tmux-openbsd.git] / screen-write.c
blob8a4400523ce14d1b594624e815658088ec114ba8
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 *, 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 (ttyctx->allow_invisible_panes) {
136 if (session_has(c->session, wp->window))
137 return (1);
138 return (0);
141 if (c->session->curw->window != wp->window)
142 return (0);
143 if (wp->layout_cell == NULL)
144 return (0);
146 if (wp->flags & (PANE_REDRAW|PANE_DROP))
147 return (-1);
148 if (c->flags & CLIENT_REDRAWPANES) {
150 * Redraw is already deferred to redraw another pane - redraw
151 * this one also when that happens.
153 log_debug("%s: adding %%%u to deferred redraw", __func__,
154 wp->id);
155 wp->flags |= PANE_REDRAW;
156 return (-1);
159 ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
160 &ttyctx->wsx, &ttyctx->wsy);
162 ttyctx->xoff = ttyctx->rxoff = wp->xoff;
163 ttyctx->yoff = ttyctx->ryoff = wp->yoff;
165 if (status_at_line(c) == 0)
166 ttyctx->yoff += status_line_size(c);
168 return (1);
171 /* Set up context for TTY command. */
172 static void
173 screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
174 int sync)
176 struct screen *s = ctx->s;
178 memset(ttyctx, 0, sizeof *ttyctx);
180 ttyctx->s = s;
181 ttyctx->sx = screen_size_x(s);
182 ttyctx->sy = screen_size_y(s);
184 ttyctx->ocx = s->cx;
185 ttyctx->ocy = s->cy;
186 ttyctx->orlower = s->rlower;
187 ttyctx->orupper = s->rupper;
189 memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
190 if (ctx->init_ctx_cb != NULL) {
191 ctx->init_ctx_cb(ctx, ttyctx);
192 if (ttyctx->palette != NULL) {
193 if (ttyctx->defaults.fg == 8)
194 ttyctx->defaults.fg = ttyctx->palette->fg;
195 if (ttyctx->defaults.bg == 8)
196 ttyctx->defaults.bg = ttyctx->palette->bg;
198 } else {
199 ttyctx->redraw_cb = screen_write_redraw_cb;
200 if (ctx->wp != NULL) {
201 tty_default_colours(&ttyctx->defaults, ctx->wp);
202 ttyctx->palette = &ctx->wp->palette;
203 ttyctx->set_client_cb = screen_write_set_client_cb;
204 ttyctx->arg = ctx->wp;
208 if (~ctx->flags & SCREEN_WRITE_SYNC) {
210 * For the active pane or for an overlay (no pane), we want to
211 * only use synchronized updates if requested (commands that
212 * move the cursor); for other panes, always use it, since the
213 * cursor will have to move.
215 if (ctx->wp != NULL) {
216 if (ctx->wp != ctx->wp->window->active)
217 ttyctx->num = 1;
218 else
219 ttyctx->num = sync;
220 } else
221 ttyctx->num = 0x10|sync;
222 tty_write(tty_cmd_syncstart, ttyctx);
223 ctx->flags |= SCREEN_WRITE_SYNC;
227 /* Make write list. */
228 void
229 screen_write_make_list(struct screen *s)
231 u_int y;
233 s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
234 for (y = 0; y < screen_size_y(s); y++)
235 TAILQ_INIT(&s->write_list[y].items);
238 /* Free write list. */
239 void
240 screen_write_free_list(struct screen *s)
242 u_int y;
244 for (y = 0; y < screen_size_y(s); y++)
245 free(s->write_list[y].data);
246 free(s->write_list);
249 /* Set up for writing. */
250 static void
251 screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
253 memset(ctx, 0, sizeof *ctx);
255 ctx->s = s;
257 if (ctx->s->write_list == NULL)
258 screen_write_make_list(ctx->s);
259 ctx->item = screen_write_get_citem();
261 ctx->scrolled = 0;
262 ctx->bg = 8;
265 /* Initialize writing with a pane. */
266 void
267 screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
268 struct screen *s)
270 if (s == NULL)
271 s = wp->screen;
272 screen_write_init(ctx, s);
273 ctx->wp = wp;
275 if (log_get_level() != 0) {
276 log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
277 __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
278 wp->id, wp->xoff, wp->yoff);
282 /* Initialize writing with a callback. */
283 void
284 screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
285 screen_write_init_ctx_cb cb, void *arg)
287 screen_write_init(ctx, s);
289 ctx->init_ctx_cb = cb;
290 ctx->arg = arg;
292 if (log_get_level() != 0) {
293 log_debug("%s: size %ux%u, with callback", __func__,
294 screen_size_x(ctx->s), screen_size_y(ctx->s));
298 /* Initialize writing. */
299 void
300 screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
302 screen_write_init(ctx, s);
304 if (log_get_level() != 0) {
305 log_debug("%s: size %ux%u, no pane", __func__,
306 screen_size_x(ctx->s), screen_size_y(ctx->s));
310 /* Finish writing. */
311 void
312 screen_write_stop(struct screen_write_ctx *ctx)
314 screen_write_collect_end(ctx);
315 screen_write_collect_flush(ctx, 0, __func__);
317 screen_write_free_citem(ctx->item);
320 /* Reset screen state. */
321 void
322 screen_write_reset(struct screen_write_ctx *ctx)
324 struct screen *s = ctx->s;
326 screen_reset_tabs(s);
327 screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
329 s->mode = MODE_CURSOR | MODE_WRAP;
331 screen_write_clearscreen(ctx, 8);
332 screen_write_set_cursor(ctx, 0, 0);
335 /* Write character. */
336 void
337 screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
338 u_char ch)
340 struct grid_cell gc;
342 memcpy(&gc, gcp, sizeof gc);
344 utf8_set(&gc.data, ch);
345 screen_write_cell(ctx, &gc);
348 /* Calculate string length. */
349 size_t
350 screen_write_strlen(const char *fmt, ...)
352 va_list ap;
353 char *msg;
354 struct utf8_data ud;
355 u_char *ptr;
356 size_t left, size = 0;
357 enum utf8_state more;
359 va_start(ap, fmt);
360 xvasprintf(&msg, fmt, ap);
361 va_end(ap);
363 ptr = msg;
364 while (*ptr != '\0') {
365 if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
366 ptr++;
368 left = strlen(ptr);
369 if (left < (size_t)ud.size - 1)
370 break;
371 while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
372 ptr++;
373 ptr++;
375 if (more == UTF8_DONE)
376 size += ud.width;
377 } else {
378 if (*ptr > 0x1f && *ptr < 0x7f)
379 size++;
380 ptr++;
384 free(msg);
385 return (size);
388 /* Write string wrapped over lines. */
390 screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
391 u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
393 struct screen *s = ctx->s;
394 va_list ap;
395 char *tmp;
396 u_int cy = s->cy, i, end, next, idx = 0, at, left;
397 struct utf8_data *text;
398 struct grid_cell gc;
400 memcpy(&gc, gcp, sizeof gc);
402 va_start(ap, fmt);
403 xvasprintf(&tmp, fmt, ap);
404 va_end(ap);
406 text = utf8_fromcstr(tmp);
407 free(tmp);
409 left = (cx + width) - s->cx;
410 for (;;) {
411 /* Find the end of what can fit on the line. */
412 at = 0;
413 for (end = idx; text[end].size != 0; end++) {
414 if (text[end].size == 1 && text[end].data[0] == '\n')
415 break;
416 if (at + text[end].width > left)
417 break;
418 at += text[end].width;
422 * If we're on a space, that's the end. If not, walk back to
423 * try and find one.
425 if (text[end].size == 0)
426 next = end;
427 else if (text[end].size == 1 && text[end].data[0] == '\n')
428 next = end + 1;
429 else if (text[end].size == 1 && text[end].data[0] == ' ')
430 next = end + 1;
431 else {
432 for (i = end; i > idx; i--) {
433 if (text[i].size == 1 && text[i].data[0] == ' ')
434 break;
436 if (i != idx) {
437 next = i + 1;
438 end = i;
439 } else
440 next = end;
443 /* Print the line. */
444 for (i = idx; i < end; i++) {
445 utf8_copy(&gc.data, &text[i]);
446 screen_write_cell(ctx, &gc);
449 /* If at the bottom, stop. */
450 idx = next;
451 if (s->cy == cy + lines - 1 || text[idx].size == 0)
452 break;
454 screen_write_cursormove(ctx, cx, s->cy + 1, 0);
455 left = width;
459 * Fail if on the last line and there is more to come or at the end, or
460 * if the text was not entirely consumed.
462 if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
463 text[idx].size != 0) {
464 free(text);
465 return (0);
467 free(text);
470 * If no more to come, move to the next line. Otherwise, leave on
471 * the same line (except if at the end).
473 if (!more || s->cx == cx + width)
474 screen_write_cursormove(ctx, cx, s->cy + 1, 0);
475 return (1);
478 /* Write simple string (no maximum length). */
479 void
480 screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
481 const char *fmt, ...)
483 va_list ap;
485 va_start(ap, fmt);
486 screen_write_vnputs(ctx, -1, gcp, fmt, ap);
487 va_end(ap);
490 /* Write string with length limit (-1 for unlimited). */
491 void
492 screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
493 const struct grid_cell *gcp, const char *fmt, ...)
495 va_list ap;
497 va_start(ap, fmt);
498 screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
499 va_end(ap);
502 void
503 screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
504 const struct grid_cell *gcp, const char *fmt, va_list ap)
506 struct grid_cell gc;
507 struct utf8_data *ud = &gc.data;
508 char *msg;
509 u_char *ptr;
510 size_t left, size = 0;
511 enum utf8_state more;
513 memcpy(&gc, gcp, sizeof gc);
514 xvasprintf(&msg, fmt, ap);
516 ptr = msg;
517 while (*ptr != '\0') {
518 if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
519 ptr++;
521 left = strlen(ptr);
522 if (left < (size_t)ud->size - 1)
523 break;
524 while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
525 ptr++;
526 ptr++;
528 if (more != UTF8_DONE)
529 continue;
530 if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
531 while (size < (size_t)maxlen) {
532 screen_write_putc(ctx, &gc, ' ');
533 size++;
535 break;
537 size += ud->width;
538 screen_write_cell(ctx, &gc);
539 } else {
540 if (maxlen > 0 && size + 1 > (size_t)maxlen)
541 break;
543 if (*ptr == '\001')
544 gc.attr ^= GRID_ATTR_CHARSET;
545 else if (*ptr == '\n') {
546 screen_write_linefeed(ctx, 0, 8);
547 screen_write_carriagereturn(ctx);
548 } else if (*ptr > 0x1f && *ptr < 0x7f) {
549 size++;
550 screen_write_putc(ctx, &gc, *ptr);
552 ptr++;
556 free(msg);
560 * Copy from another screen but without the selection stuff. Assumes the target
561 * region is already big enough.
563 void
564 screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
565 u_int px, u_int py, u_int nx, u_int ny)
567 struct screen *s = ctx->s;
568 struct grid *gd = src->grid;
569 struct grid_cell gc;
570 u_int xx, yy, cx, cy;
572 if (nx == 0 || ny == 0)
573 return;
575 cy = s->cy;
576 for (yy = py; yy < py + ny; yy++) {
577 if (yy >= gd->hsize + gd->sy)
578 break;
579 cx = s->cx;
580 for (xx = px; xx < px + nx; xx++) {
581 if (xx >= grid_get_line(gd, yy)->cellsize)
582 break;
583 grid_get_cell(gd, xx, yy, &gc);
584 if (xx + gc.data.width > px + nx)
585 break;
586 grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
587 cx++;
589 cy++;
593 /* Draw a horizontal line on screen. */
594 void
595 screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right)
597 struct screen *s = ctx->s;
598 struct grid_cell gc;
599 u_int cx, cy, i;
601 cx = s->cx;
602 cy = s->cy;
604 memcpy(&gc, &grid_default_cell, sizeof gc);
605 gc.attr |= GRID_ATTR_CHARSET;
607 screen_write_putc(ctx, &gc, left ? 't' : 'q');
608 for (i = 1; i < nx - 1; i++)
609 screen_write_putc(ctx, &gc, 'q');
610 screen_write_putc(ctx, &gc, right ? 'u' : 'q');
612 screen_write_set_cursor(ctx, cx, cy);
615 /* Draw a vertical line on screen. */
616 void
617 screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
619 struct screen *s = ctx->s;
620 struct grid_cell gc;
621 u_int cx, cy, i;
623 cx = s->cx;
624 cy = s->cy;
626 memcpy(&gc, &grid_default_cell, sizeof gc);
627 gc.attr |= GRID_ATTR_CHARSET;
629 screen_write_putc(ctx, &gc, top ? 'w' : 'x');
630 for (i = 1; i < ny - 1; i++) {
631 screen_write_set_cursor(ctx, cx, cy + i);
632 screen_write_putc(ctx, &gc, 'x');
634 screen_write_set_cursor(ctx, cx, cy + ny - 1);
635 screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
637 screen_write_set_cursor(ctx, cx, cy);
640 /* Draw a menu on screen. */
641 void
642 screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu,
643 int choice, const struct grid_cell *choice_gc)
645 struct screen *s = ctx->s;
646 struct grid_cell default_gc;
647 const struct grid_cell *gc = &default_gc;
648 u_int cx, cy, i, j;
649 const char *name;
651 cx = s->cx;
652 cy = s->cy;
654 memcpy(&default_gc, &grid_default_cell, sizeof default_gc);
656 screen_write_box(ctx, menu->width + 4, menu->count + 2,
657 BOX_LINES_DEFAULT, &default_gc, menu->title);
659 for (i = 0; i < menu->count; i++) {
660 name = menu->items[i].name;
661 if (name == NULL) {
662 screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
663 screen_write_hline(ctx, menu->width + 4, 1, 1);
664 } else {
665 if (choice >= 0 && i == (u_int)choice && *name != '-')
666 gc = choice_gc;
667 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
668 for (j = 0; j < menu->width; j++)
669 screen_write_putc(ctx, gc, ' ');
670 screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
671 if (*name == '-') {
672 name++;
673 default_gc.attr |= GRID_ATTR_DIM;
674 format_draw(ctx, gc, menu->width, name, NULL,
676 default_gc.attr &= ~GRID_ATTR_DIM;
677 } else
678 format_draw(ctx, gc, menu->width, name, NULL,
679 gc == choice_gc);
680 gc = &default_gc;
684 screen_write_set_cursor(ctx, cx, cy);
687 static void
688 screen_write_box_border_set(enum box_lines box_lines, int cell_type,
689 struct grid_cell *gc)
691 switch (box_lines) {
692 case BOX_LINES_NONE:
693 break;
694 case BOX_LINES_DOUBLE:
695 gc->attr &= ~GRID_ATTR_CHARSET;
696 utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
697 break;
698 case BOX_LINES_HEAVY:
699 gc->attr &= ~GRID_ATTR_CHARSET;
700 utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
701 break;
702 case BOX_LINES_ROUNDED:
703 gc->attr &= ~GRID_ATTR_CHARSET;
704 utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
705 break;
706 case BOX_LINES_SIMPLE:
707 gc->attr &= ~GRID_ATTR_CHARSET;
708 utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
709 break;
710 case BOX_LINES_PADDED:
711 gc->attr &= ~GRID_ATTR_CHARSET;
712 utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
713 break;
714 case BOX_LINES_SINGLE:
715 case BOX_LINES_DEFAULT:
716 gc->attr |= GRID_ATTR_CHARSET;
717 utf8_set(&gc->data, CELL_BORDERS[cell_type]);
718 break;
722 /* Draw a box on screen. */
723 void
724 screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
725 enum box_lines lines, const struct grid_cell *gcp, const char *title)
727 struct screen *s = ctx->s;
728 struct grid_cell gc;
729 u_int cx, cy, i;
731 cx = s->cx;
732 cy = s->cy;
734 if (gcp != NULL)
735 memcpy(&gc, gcp, sizeof gc);
736 else
737 memcpy(&gc, &grid_default_cell, sizeof gc);
739 gc.attr |= GRID_ATTR_CHARSET;
740 gc.flags |= GRID_FLAG_NOPALETTE;
742 /* Draw top border */
743 screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
744 screen_write_cell(ctx, &gc);
745 screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
746 for (i = 1; i < nx - 1; i++)
747 screen_write_cell(ctx, &gc);
748 screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
749 screen_write_cell(ctx, &gc);
751 /* Draw bottom border */
752 screen_write_set_cursor(ctx, cx, cy + ny - 1);
753 screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
754 screen_write_cell(ctx, &gc);
755 screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
756 for (i = 1; i < nx - 1; i++)
757 screen_write_cell(ctx, &gc);
758 screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
759 screen_write_cell(ctx, &gc);
761 /* Draw sides */
762 screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
763 for (i = 1; i < ny - 1; i++) {
764 /* left side */
765 screen_write_set_cursor(ctx, cx, cy + i);
766 screen_write_cell(ctx, &gc);
767 /* right side */
768 screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
769 screen_write_cell(ctx, &gc);
772 if (title != NULL) {
773 gc.attr &= ~GRID_ATTR_CHARSET;
774 screen_write_cursormove(ctx, cx + 2, cy, 0);
775 format_draw(ctx, &gc, nx - 4, title, NULL, 0);
778 screen_write_set_cursor(ctx, cx, cy);
782 * Write a preview version of a window. Assumes target area is big enough and
783 * already cleared.
785 void
786 screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
787 u_int ny)
789 struct screen *s = ctx->s;
790 struct grid_cell gc;
791 u_int cx, cy, px, py;
793 cx = s->cx;
794 cy = s->cy;
797 * If the cursor is on, pick the area around the cursor, otherwise use
798 * the top left.
800 if (src->mode & MODE_CURSOR) {
801 px = src->cx;
802 if (px < nx / 3)
803 px = 0;
804 else
805 px = px - nx / 3;
806 if (px + nx > screen_size_x(src)) {
807 if (nx > screen_size_x(src))
808 px = 0;
809 else
810 px = screen_size_x(src) - nx;
812 py = src->cy;
813 if (py < ny / 3)
814 py = 0;
815 else
816 py = py - ny / 3;
817 if (py + ny > screen_size_y(src)) {
818 if (ny > screen_size_y(src))
819 py = 0;
820 else
821 py = screen_size_y(src) - ny;
823 } else {
824 px = 0;
825 py = 0;
828 screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
830 if (src->mode & MODE_CURSOR) {
831 grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
832 gc.attr |= GRID_ATTR_REVERSE;
833 screen_write_set_cursor(ctx, cx + (src->cx - px),
834 cy + (src->cy - py));
835 screen_write_cell(ctx, &gc);
839 /* Set a mode. */
840 void
841 screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
843 struct screen *s = ctx->s;
845 s->mode |= mode;
847 if (log_get_level() != 0)
848 log_debug("%s: %s", __func__, screen_mode_to_string(mode));
851 /* Clear a mode. */
852 void
853 screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
855 struct screen *s = ctx->s;
857 s->mode &= ~mode;
859 if (log_get_level() != 0)
860 log_debug("%s: %s", __func__, screen_mode_to_string(mode));
863 /* Cursor up by ny. */
864 void
865 screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
867 struct screen *s = ctx->s;
868 u_int cx = s->cx, cy = s->cy;
870 if (ny == 0)
871 ny = 1;
873 if (cy < s->rupper) {
874 /* Above region. */
875 if (ny > cy)
876 ny = cy;
877 } else {
878 /* Below region. */
879 if (ny > cy - s->rupper)
880 ny = cy - s->rupper;
882 if (cx == screen_size_x(s))
883 cx--;
885 cy -= ny;
887 screen_write_set_cursor(ctx, cx, cy);
890 /* Cursor down by ny. */
891 void
892 screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
894 struct screen *s = ctx->s;
895 u_int cx = s->cx, cy = s->cy;
897 if (ny == 0)
898 ny = 1;
900 if (cy > s->rlower) {
901 /* Below region. */
902 if (ny > screen_size_y(s) - 1 - cy)
903 ny = screen_size_y(s) - 1 - cy;
904 } else {
905 /* Above region. */
906 if (ny > s->rlower - cy)
907 ny = s->rlower - cy;
909 if (cx == screen_size_x(s))
910 cx--;
911 else if (ny == 0)
912 return;
914 cy += ny;
916 screen_write_set_cursor(ctx, cx, cy);
919 /* Cursor right by nx. */
920 void
921 screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
923 struct screen *s = ctx->s;
924 u_int cx = s->cx, cy = s->cy;
926 if (nx == 0)
927 nx = 1;
929 if (nx > screen_size_x(s) - 1 - cx)
930 nx = screen_size_x(s) - 1 - cx;
931 if (nx == 0)
932 return;
934 cx += nx;
936 screen_write_set_cursor(ctx, cx, cy);
939 /* Cursor left by nx. */
940 void
941 screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
943 struct screen *s = ctx->s;
944 u_int cx = s->cx, cy = s->cy;
946 if (nx == 0)
947 nx = 1;
949 if (nx > cx)
950 nx = cx;
951 if (nx == 0)
952 return;
954 cx -= nx;
956 screen_write_set_cursor(ctx, cx, cy);
959 /* Backspace; cursor left unless at start of wrapped line when can move up. */
960 void
961 screen_write_backspace(struct screen_write_ctx *ctx)
963 struct screen *s = ctx->s;
964 struct grid_line *gl;
965 u_int cx = s->cx, cy = s->cy;
967 if (cx == 0) {
968 if (cy == 0)
969 return;
970 gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
971 if (gl->flags & GRID_LINE_WRAPPED) {
972 cy--;
973 cx = screen_size_x(s) - 1;
975 } else
976 cx--;
978 screen_write_set_cursor(ctx, cx, cy);
981 /* VT100 alignment test. */
982 void
983 screen_write_alignmenttest(struct screen_write_ctx *ctx)
985 struct screen *s = ctx->s;
986 struct tty_ctx ttyctx;
987 struct grid_cell gc;
988 u_int xx, yy;
990 memcpy(&gc, &grid_default_cell, sizeof gc);
991 utf8_set(&gc.data, 'E');
993 for (yy = 0; yy < screen_size_y(s); yy++) {
994 for (xx = 0; xx < screen_size_x(s); xx++)
995 grid_view_set_cell(s->grid, xx, yy, &gc);
998 screen_write_set_cursor(ctx, 0, 0);
1000 s->rupper = 0;
1001 s->rlower = screen_size_y(s) - 1;
1003 screen_write_initctx(ctx, &ttyctx, 1);
1005 screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1006 tty_write(tty_cmd_alignmenttest, &ttyctx);
1009 /* Insert nx characters. */
1010 void
1011 screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1013 struct screen *s = ctx->s;
1014 struct tty_ctx ttyctx;
1016 if (nx == 0)
1017 nx = 1;
1019 if (nx > screen_size_x(s) - s->cx)
1020 nx = screen_size_x(s) - s->cx;
1021 if (nx == 0)
1022 return;
1024 if (s->cx > screen_size_x(s) - 1)
1025 return;
1027 screen_write_initctx(ctx, &ttyctx, 0);
1028 ttyctx.bg = bg;
1030 grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1032 screen_write_collect_flush(ctx, 0, __func__);
1033 ttyctx.num = nx;
1034 tty_write(tty_cmd_insertcharacter, &ttyctx);
1037 /* Delete nx characters. */
1038 void
1039 screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1041 struct screen *s = ctx->s;
1042 struct tty_ctx ttyctx;
1044 if (nx == 0)
1045 nx = 1;
1047 if (nx > screen_size_x(s) - s->cx)
1048 nx = screen_size_x(s) - s->cx;
1049 if (nx == 0)
1050 return;
1052 if (s->cx > screen_size_x(s) - 1)
1053 return;
1055 screen_write_initctx(ctx, &ttyctx, 0);
1056 ttyctx.bg = bg;
1058 grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1060 screen_write_collect_flush(ctx, 0, __func__);
1061 ttyctx.num = nx;
1062 tty_write(tty_cmd_deletecharacter, &ttyctx);
1065 /* Clear nx characters. */
1066 void
1067 screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1069 struct screen *s = ctx->s;
1070 struct tty_ctx ttyctx;
1072 if (nx == 0)
1073 nx = 1;
1075 if (nx > screen_size_x(s) - s->cx)
1076 nx = screen_size_x(s) - s->cx;
1077 if (nx == 0)
1078 return;
1080 if (s->cx > screen_size_x(s) - 1)
1081 return;
1083 screen_write_initctx(ctx, &ttyctx, 0);
1084 ttyctx.bg = bg;
1086 grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1088 screen_write_collect_flush(ctx, 0, __func__);
1089 ttyctx.num = nx;
1090 tty_write(tty_cmd_clearcharacter, &ttyctx);
1093 /* Insert ny lines. */
1094 void
1095 screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1097 struct screen *s = ctx->s;
1098 struct grid *gd = s->grid;
1099 struct tty_ctx ttyctx;
1101 if (ny == 0)
1102 ny = 1;
1104 if (s->cy < s->rupper || s->cy > s->rlower) {
1105 if (ny > screen_size_y(s) - s->cy)
1106 ny = screen_size_y(s) - s->cy;
1107 if (ny == 0)
1108 return;
1110 screen_write_initctx(ctx, &ttyctx, 1);
1111 ttyctx.bg = bg;
1113 grid_view_insert_lines(gd, s->cy, ny, bg);
1115 screen_write_collect_flush(ctx, 0, __func__);
1116 ttyctx.num = ny;
1117 tty_write(tty_cmd_insertline, &ttyctx);
1118 return;
1121 if (ny > s->rlower + 1 - s->cy)
1122 ny = s->rlower + 1 - s->cy;
1123 if (ny == 0)
1124 return;
1126 screen_write_initctx(ctx, &ttyctx, 1);
1127 ttyctx.bg = bg;
1129 if (s->cy < s->rupper || s->cy > s->rlower)
1130 grid_view_insert_lines(gd, s->cy, ny, bg);
1131 else
1132 grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1134 screen_write_collect_flush(ctx, 0, __func__);
1136 ttyctx.num = ny;
1137 tty_write(tty_cmd_insertline, &ttyctx);
1140 /* Delete ny lines. */
1141 void
1142 screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1144 struct screen *s = ctx->s;
1145 struct grid *gd = s->grid;
1146 struct tty_ctx ttyctx;
1148 if (ny == 0)
1149 ny = 1;
1151 if (s->cy < s->rupper || s->cy > s->rlower) {
1152 if (ny > screen_size_y(s) - s->cy)
1153 ny = screen_size_y(s) - s->cy;
1154 if (ny == 0)
1155 return;
1157 screen_write_initctx(ctx, &ttyctx, 1);
1158 ttyctx.bg = bg;
1160 grid_view_delete_lines(gd, s->cy, ny, bg);
1162 screen_write_collect_flush(ctx, 0, __func__);
1163 ttyctx.num = ny;
1164 tty_write(tty_cmd_deleteline, &ttyctx);
1165 return;
1168 if (ny > s->rlower + 1 - s->cy)
1169 ny = s->rlower + 1 - s->cy;
1170 if (ny == 0)
1171 return;
1173 screen_write_initctx(ctx, &ttyctx, 1);
1174 ttyctx.bg = bg;
1176 if (s->cy < s->rupper || s->cy > s->rlower)
1177 grid_view_delete_lines(gd, s->cy, ny, bg);
1178 else
1179 grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1181 screen_write_collect_flush(ctx, 0, __func__);
1182 ttyctx.num = ny;
1183 tty_write(tty_cmd_deleteline, &ttyctx);
1186 /* Clear line at cursor. */
1187 void
1188 screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1190 struct screen *s = ctx->s;
1191 struct grid_line *gl;
1192 u_int sx = screen_size_x(s);
1193 struct screen_write_citem *ci = ctx->item;
1195 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1196 if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1197 return;
1199 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1201 screen_write_collect_clear(ctx, s->cy, 1);
1202 ci->x = 0;
1203 ci->used = sx;
1204 ci->type = CLEAR;
1205 ci->bg = bg;
1206 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1207 ctx->item = screen_write_get_citem();
1210 /* Clear to end of line from cursor. */
1211 void
1212 screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1214 struct screen *s = ctx->s;
1215 struct grid_line *gl;
1216 u_int sx = screen_size_x(s);
1217 struct screen_write_citem *ci = ctx->item, *before;
1219 if (s->cx == 0) {
1220 screen_write_clearline(ctx, bg);
1221 return;
1224 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1225 if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1226 return;
1228 grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1230 before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1231 ci->x = s->cx;
1232 ci->used = sx - s->cx;
1233 ci->type = CLEAR;
1234 ci->bg = bg;
1235 if (before == NULL)
1236 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1237 else
1238 TAILQ_INSERT_BEFORE(before, ci, entry);
1239 ctx->item = screen_write_get_citem();
1242 /* Clear to start of line from cursor. */
1243 void
1244 screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1246 struct screen *s = ctx->s;
1247 u_int sx = screen_size_x(s);
1248 struct screen_write_citem *ci = ctx->item, *before;
1250 if (s->cx >= sx - 1) {
1251 screen_write_clearline(ctx, bg);
1252 return;
1255 if (s->cx > sx - 1)
1256 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1257 else
1258 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1260 before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1261 ci->x = 0;
1262 ci->used = s->cx + 1;
1263 ci->type = CLEAR;
1264 ci->bg = bg;
1265 if (before == NULL)
1266 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1267 else
1268 TAILQ_INSERT_BEFORE(before, ci, entry);
1269 ctx->item = screen_write_get_citem();
1272 /* Move cursor to px,py. */
1273 void
1274 screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1275 int origin)
1277 struct screen *s = ctx->s;
1279 if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1280 if ((u_int)py > s->rlower - s->rupper)
1281 py = s->rlower;
1282 else
1283 py += s->rupper;
1286 if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1287 px = screen_size_x(s) - 1;
1288 if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1289 py = screen_size_y(s) - 1;
1291 log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1292 screen_write_set_cursor(ctx, px, py);
1295 /* Reverse index (up with scroll). */
1296 void
1297 screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1299 struct screen *s = ctx->s;
1300 struct tty_ctx ttyctx;
1302 if (s->cy == s->rupper) {
1303 grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1304 screen_write_collect_flush(ctx, 0, __func__);
1306 screen_write_initctx(ctx, &ttyctx, 1);
1307 ttyctx.bg = bg;
1309 tty_write(tty_cmd_reverseindex, &ttyctx);
1310 } else if (s->cy > 0)
1311 screen_write_set_cursor(ctx, -1, s->cy - 1);
1315 /* Set scroll region. */
1316 void
1317 screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1318 u_int rlower)
1320 struct screen *s = ctx->s;
1322 if (rupper > screen_size_y(s) - 1)
1323 rupper = screen_size_y(s) - 1;
1324 if (rlower > screen_size_y(s) - 1)
1325 rlower = screen_size_y(s) - 1;
1326 if (rupper >= rlower) /* cannot be one line */
1327 return;
1329 screen_write_collect_flush(ctx, 0, __func__);
1331 /* Cursor moves to top-left. */
1332 screen_write_set_cursor(ctx, 0, 0);
1334 s->rupper = rupper;
1335 s->rlower = rlower;
1338 /* Line feed. */
1339 void
1340 screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1342 struct screen *s = ctx->s;
1343 struct grid *gd = s->grid;
1344 struct grid_line *gl;
1346 gl = grid_get_line(gd, gd->hsize + s->cy);
1347 if (wrapped)
1348 gl->flags |= GRID_LINE_WRAPPED;
1350 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1351 s->rupper, s->rlower);
1353 if (bg != ctx->bg) {
1354 screen_write_collect_flush(ctx, 1, __func__);
1355 ctx->bg = bg;
1358 if (s->cy == s->rlower) {
1359 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1360 screen_write_collect_scroll(ctx, bg);
1361 ctx->scrolled++;
1362 } else if (s->cy < screen_size_y(s) - 1)
1363 screen_write_set_cursor(ctx, -1, s->cy + 1);
1366 /* Scroll up. */
1367 void
1368 screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1370 struct screen *s = ctx->s;
1371 struct grid *gd = s->grid;
1372 u_int i;
1374 if (lines == 0)
1375 lines = 1;
1376 else if (lines > s->rlower - s->rupper + 1)
1377 lines = s->rlower - s->rupper + 1;
1379 if (bg != ctx->bg) {
1380 screen_write_collect_flush(ctx, 1, __func__);
1381 ctx->bg = bg;
1384 for (i = 0; i < lines; i++) {
1385 grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1386 screen_write_collect_scroll(ctx, bg);
1388 ctx->scrolled += lines;
1391 /* Scroll down. */
1392 void
1393 screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1395 struct screen *s = ctx->s;
1396 struct grid *gd = s->grid;
1397 struct tty_ctx ttyctx;
1398 u_int i;
1400 screen_write_initctx(ctx, &ttyctx, 1);
1401 ttyctx.bg = bg;
1403 if (lines == 0)
1404 lines = 1;
1405 else if (lines > s->rlower - s->rupper + 1)
1406 lines = s->rlower - s->rupper + 1;
1408 for (i = 0; i < lines; i++)
1409 grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1411 screen_write_collect_flush(ctx, 0, __func__);
1412 ttyctx.num = lines;
1413 tty_write(tty_cmd_scrolldown, &ttyctx);
1416 /* Carriage return (cursor to start of line). */
1417 void
1418 screen_write_carriagereturn(struct screen_write_ctx *ctx)
1420 screen_write_set_cursor(ctx, 0, -1);
1423 /* Clear to end of screen from cursor. */
1424 void
1425 screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1427 struct screen *s = ctx->s;
1428 struct grid *gd = s->grid;
1429 struct tty_ctx ttyctx;
1430 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1432 screen_write_initctx(ctx, &ttyctx, 1);
1433 ttyctx.bg = bg;
1435 /* Scroll into history if it is enabled and clearing entire screen. */
1436 if (s->cx == 0 &&
1437 s->cy == 0 &&
1438 (gd->flags & GRID_HISTORY) &&
1439 ctx->wp != NULL &&
1440 options_get_number(ctx->wp->options, "scroll-on-clear"))
1441 grid_view_clear_history(gd, bg);
1442 else {
1443 if (s->cx <= sx - 1)
1444 grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1445 grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1448 screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1449 screen_write_collect_flush(ctx, 0, __func__);
1450 tty_write(tty_cmd_clearendofscreen, &ttyctx);
1453 /* Clear to start of screen. */
1454 void
1455 screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1457 struct screen *s = ctx->s;
1458 struct tty_ctx ttyctx;
1459 u_int sx = screen_size_x(s);
1461 screen_write_initctx(ctx, &ttyctx, 1);
1462 ttyctx.bg = bg;
1464 if (s->cy > 0)
1465 grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1466 if (s->cx > sx - 1)
1467 grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1468 else
1469 grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1471 screen_write_collect_clear(ctx, 0, s->cy);
1472 screen_write_collect_flush(ctx, 0, __func__);
1473 tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1476 /* Clear entire screen. */
1477 void
1478 screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1480 struct screen *s = ctx->s;
1481 struct tty_ctx ttyctx;
1482 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1484 screen_write_initctx(ctx, &ttyctx, 1);
1485 ttyctx.bg = bg;
1487 /* Scroll into history if it is enabled. */
1488 if ((s->grid->flags & GRID_HISTORY) &&
1489 ctx->wp != NULL &&
1490 options_get_number(ctx->wp->options, "scroll-on-clear"))
1491 grid_view_clear_history(s->grid, bg);
1492 else
1493 grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1495 screen_write_collect_clear(ctx, 0, sy);
1496 tty_write(tty_cmd_clearscreen, &ttyctx);
1499 /* Clear entire history. */
1500 void
1501 screen_write_clearhistory(struct screen_write_ctx *ctx)
1503 grid_clear_history(ctx->s->grid);
1506 /* Force a full redraw. */
1507 void
1508 screen_write_fullredraw(struct screen_write_ctx *ctx)
1510 struct tty_ctx ttyctx;
1512 screen_write_collect_flush(ctx, 0, __func__);
1514 screen_write_initctx(ctx, &ttyctx, 1);
1515 if (ttyctx.redraw_cb != NULL)
1516 ttyctx.redraw_cb(&ttyctx);
1519 /* Trim collected items. */
1520 static struct screen_write_citem *
1521 screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1522 u_int used, int *wrapped)
1524 struct screen_write_cline *cl = &ctx->s->write_list[y];
1525 struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
1526 u_int sx = x, ex = x + used - 1;
1527 u_int csx, cex;
1529 if (TAILQ_EMPTY(&cl->items))
1530 return (NULL);
1531 TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1532 csx = ci->x;
1533 cex = ci->x + ci->used - 1;
1535 /* Item is entirely before. */
1536 if (cex < sx) {
1537 log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1538 csx, cex, sx, ex);
1539 continue;
1542 /* Item is entirely after. */
1543 if (csx > ex) {
1544 log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1545 csx, cex, sx, ex);
1546 before = ci;
1547 break;
1550 /* Item is entirely inside. */
1551 if (csx >= sx && cex <= ex) {
1552 log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1553 csx, cex, sx, ex);
1554 TAILQ_REMOVE(&cl->items, ci, entry);
1555 screen_write_free_citem(ci);
1556 if (csx == 0 && ci->wrapped && wrapped != NULL)
1557 *wrapped = 1;
1558 continue;
1561 /* Item under the start. */
1562 if (csx < sx && cex >= sx && cex <= ex) {
1563 log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1564 csx, cex, sx, ex);
1565 ci->used = sx - csx;
1566 log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1567 ci->x + ci->used + 1);
1568 continue;
1571 /* Item covers the end. */
1572 if (cex > ex && csx >= sx && csx <= ex) {
1573 log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1574 csx, cex, sx, ex);
1575 ci->x = ex + 1;
1576 ci->used = cex - ex;
1577 log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1578 ci->x + ci->used + 1);
1579 before = ci;
1580 break;
1583 /* Item must cover both sides. */
1584 log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1585 csx, cex, sx, ex);
1586 ci2 = screen_write_get_citem();
1587 ci2->type = ci->type;
1588 ci2->bg = ci->bg;
1589 memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1590 TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1592 ci->used = sx - csx;
1593 ci2->x = ex + 1;
1594 ci2->used = cex - ex;
1596 log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1597 ci->x, ci->x + ci->used - 1, ci, ci2->x,
1598 ci2->x + ci2->used - 1, ci2);
1599 before = ci2;
1600 break;
1602 return (before);
1605 /* Clear collected lines. */
1606 static void
1607 screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1609 struct screen_write_cline *cl;
1610 u_int i;
1612 for (i = y; i < y + n; i++) {
1613 cl = &ctx->s->write_list[i];
1614 TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1618 /* Scroll collected lines up. */
1619 static void
1620 screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1622 struct screen *s = ctx->s;
1623 struct screen_write_cline *cl;
1624 u_int y;
1625 char *saved;
1626 struct screen_write_citem *ci;
1628 log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1629 s->rupper, s->rlower);
1631 screen_write_collect_clear(ctx, s->rupper, 1);
1632 saved = ctx->s->write_list[s->rupper].data;
1633 for (y = s->rupper; y < s->rlower; y++) {
1634 cl = &ctx->s->write_list[y + 1];
1635 TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1636 ctx->s->write_list[y].data = cl->data;
1638 ctx->s->write_list[s->rlower].data = saved;
1640 ci = screen_write_get_citem();
1641 ci->x = 0;
1642 ci->used = screen_size_x(s);
1643 ci->type = CLEAR;
1644 ci->bg = bg;
1645 TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1648 /* Flush collected lines. */
1649 static void
1650 screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1651 const char *from)
1653 struct screen *s = ctx->s;
1654 struct screen_write_citem *ci, *tmp;
1655 struct screen_write_cline *cl;
1656 u_int y, cx, cy, last, items = 0;
1657 struct tty_ctx ttyctx;
1659 if (ctx->scrolled != 0) {
1660 log_debug("%s: scrolled %u (region %u-%u)", __func__,
1661 ctx->scrolled, s->rupper, s->rlower);
1662 if (ctx->scrolled > s->rlower - s->rupper + 1)
1663 ctx->scrolled = s->rlower - s->rupper + 1;
1665 screen_write_initctx(ctx, &ttyctx, 1);
1666 ttyctx.num = ctx->scrolled;
1667 ttyctx.bg = ctx->bg;
1668 tty_write(tty_cmd_scrollup, &ttyctx);
1670 ctx->scrolled = 0;
1671 ctx->bg = 8;
1673 if (scroll_only)
1674 return;
1676 cx = s->cx; cy = s->cy;
1677 for (y = 0; y < screen_size_y(s); y++) {
1678 cl = &ctx->s->write_list[y];
1679 last = UINT_MAX;
1680 TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1681 if (last != UINT_MAX && ci->x <= last) {
1682 fatalx("collect list not in order: %u <= %u",
1683 ci->x, last);
1685 screen_write_set_cursor(ctx, ci->x, y);
1686 if (ci->type == CLEAR) {
1687 screen_write_initctx(ctx, &ttyctx, 1);
1688 ttyctx.bg = ci->bg;
1689 ttyctx.num = ci->used;
1690 tty_write(tty_cmd_clearcharacter, &ttyctx);
1691 } else {
1692 screen_write_initctx(ctx, &ttyctx, 0);
1693 ttyctx.cell = &ci->gc;
1694 ttyctx.wrapped = ci->wrapped;
1695 ttyctx.ptr = cl->data + ci->x;
1696 ttyctx.num = ci->used;
1697 tty_write(tty_cmd_cells, &ttyctx);
1699 items++;
1701 TAILQ_REMOVE(&cl->items, ci, entry);
1702 screen_write_free_citem(ci);
1703 last = ci->x;
1706 s->cx = cx; s->cy = cy;
1708 log_debug("%s: flushed %u items (%s)", __func__, items, from);
1711 /* Finish and store collected cells. */
1712 void
1713 screen_write_collect_end(struct screen_write_ctx *ctx)
1715 struct screen *s = ctx->s;
1716 struct screen_write_citem *ci = ctx->item, *before;
1717 struct screen_write_cline *cl = &s->write_list[s->cy];
1718 struct grid_cell gc;
1719 u_int xx;
1720 int wrapped = ci->wrapped;
1722 if (ci->used == 0)
1723 return;
1725 before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1726 &wrapped);
1727 ci->x = s->cx;
1728 ci->wrapped = wrapped;
1729 if (before == NULL)
1730 TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1731 else
1732 TAILQ_INSERT_BEFORE(before, ci, entry);
1733 ctx->item = screen_write_get_citem();
1735 log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1736 (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1738 if (s->cx != 0) {
1739 for (xx = s->cx; xx > 0; xx--) {
1740 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1741 if (~gc.flags & GRID_FLAG_PADDING)
1742 break;
1743 grid_view_set_cell(s->grid, xx, s->cy,
1744 &grid_default_cell);
1746 if (gc.data.width > 1) {
1747 grid_view_set_cell(s->grid, xx, s->cy,
1748 &grid_default_cell);
1752 grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1753 ci->used);
1754 screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1756 for (xx = s->cx; xx < screen_size_x(s); xx++) {
1757 grid_view_get_cell(s->grid, xx, s->cy, &gc);
1758 if (~gc.flags & GRID_FLAG_PADDING)
1759 break;
1760 grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1764 /* Write cell data, collecting if necessary. */
1765 void
1766 screen_write_collect_add(struct screen_write_ctx *ctx,
1767 const struct grid_cell *gc)
1769 struct screen *s = ctx->s;
1770 struct screen_write_citem *ci;
1771 u_int sx = screen_size_x(s);
1772 int collect;
1775 * Don't need to check that the attributes and whatnot are still the
1776 * same - input_parse will end the collection when anything that isn't
1777 * a plain character is encountered.
1780 collect = 1;
1781 if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1782 collect = 0;
1783 else if (gc->attr & GRID_ATTR_CHARSET)
1784 collect = 0;
1785 else if (~s->mode & MODE_WRAP)
1786 collect = 0;
1787 else if (s->mode & MODE_INSERT)
1788 collect = 0;
1789 else if (s->sel != NULL)
1790 collect = 0;
1791 if (!collect) {
1792 screen_write_collect_end(ctx);
1793 screen_write_collect_flush(ctx, 0, __func__);
1794 screen_write_cell(ctx, gc);
1795 return;
1798 if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1799 screen_write_collect_end(ctx);
1800 ci = ctx->item; /* may have changed */
1802 if (s->cx > sx - 1) {
1803 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1804 ci->wrapped = 1;
1805 screen_write_linefeed(ctx, 1, 8);
1806 screen_write_set_cursor(ctx, 0, -1);
1809 if (ci->used == 0)
1810 memcpy(&ci->gc, gc, sizeof ci->gc);
1811 if (ctx->s->write_list[s->cy].data == NULL)
1812 ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1813 ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
1816 /* Write cell data. */
1817 void
1818 screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1820 struct screen *s = ctx->s;
1821 struct grid *gd = s->grid;
1822 const struct utf8_data *ud = &gc->data;
1823 const struct utf8_data zwj = { "\342\200\215", 0, 3, 0 };
1824 struct grid_line *gl;
1825 struct grid_cell_entry *gce;
1826 struct grid_cell tmp_gc, now_gc;
1827 struct tty_ctx ttyctx;
1828 u_int sx = screen_size_x(s), sy = screen_size_y(s);
1829 u_int width = gc->data.width, xx, last, cx, cy;
1830 int selected, skip = 1;
1832 /* Ignore padding cells. */
1833 if (gc->flags & GRID_FLAG_PADDING)
1834 return;
1837 * If this is a zero width joiner, set the flag so the next character
1838 * will be treated as zero width and appended. Note that we assume a
1839 * ZWJ will not change the width - the width of the first character is
1840 * used.
1842 if (ud->size == 3 && memcmp(ud->data, "\342\200\215", 3) == 0) {
1843 log_debug("zero width joiner at %u,%u", s->cx, s->cy);
1844 ctx->flags |= SCREEN_WRITE_ZWJ;
1845 return;
1849 * If the width is zero, combine onto the previous character. We always
1850 * combine with the cell to the left of the cursor position. In theory,
1851 * the application could have moved the cursor somewhere else, but if
1852 * they are silly enough to do that, who cares?
1854 if (ctx->flags & SCREEN_WRITE_ZWJ) {
1855 screen_write_collect_flush(ctx, 0, __func__);
1856 screen_write_combine(ctx, &zwj, &xx, &cx);
1858 if (width == 0 || (ctx->flags & SCREEN_WRITE_ZWJ)) {
1859 ctx->flags &= ~SCREEN_WRITE_ZWJ;
1860 screen_write_collect_flush(ctx, 0, __func__);
1861 if ((gc = screen_write_combine(ctx, ud, &xx, &cx)) != NULL) {
1862 cy = s->cy;
1863 screen_write_set_cursor(ctx, xx, s->cy);
1864 screen_write_initctx(ctx, &ttyctx, 0);
1865 ttyctx.cell = gc;
1866 tty_write(tty_cmd_cell, &ttyctx);
1867 s->cx = cx; s->cy = cy;
1869 return;
1872 /* Flush any existing scrolling. */
1873 screen_write_collect_flush(ctx, 1, __func__);
1875 /* If this character doesn't fit, ignore it. */
1876 if ((~s->mode & MODE_WRAP) &&
1877 width > 1 &&
1878 (width > sx || (s->cx != sx && s->cx > sx - width)))
1879 return;
1881 /* If in insert mode, make space for the cells. */
1882 if (s->mode & MODE_INSERT) {
1883 grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1884 skip = 0;
1887 /* Check this will fit on the current line and wrap if not. */
1888 if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1889 log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1890 screen_write_linefeed(ctx, 1, 8);
1891 screen_write_set_cursor(ctx, 0, -1);
1892 screen_write_collect_flush(ctx, 1, __func__);
1895 /* Sanity check cursor position. */
1896 if (s->cx > sx - width || s->cy > sy - 1)
1897 return;
1898 screen_write_initctx(ctx, &ttyctx, 0);
1900 /* Handle overwriting of UTF-8 characters. */
1901 gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1902 if (gl->flags & GRID_LINE_EXTENDED) {
1903 grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
1904 if (screen_write_overwrite(ctx, &now_gc, width))
1905 skip = 0;
1909 * If the new character is UTF-8 wide, fill in padding cells. Have
1910 * already ensured there is enough room.
1912 for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1913 log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
1914 grid_view_set_padding(gd, xx, s->cy);
1915 skip = 0;
1918 /* If no change, do not draw. */
1919 if (skip) {
1920 if (s->cx >= gl->cellsize)
1921 skip = grid_cells_equal(gc, &grid_default_cell);
1922 else {
1923 gce = &gl->celldata[s->cx];
1924 if (gce->flags & GRID_FLAG_EXTENDED)
1925 skip = 0;
1926 else if (gc->flags != gce->flags)
1927 skip = 0;
1928 else if (gc->attr != gce->data.attr)
1929 skip = 0;
1930 else if (gc->fg != gce->data.fg)
1931 skip = 0;
1932 else if (gc->bg != gce->data.bg)
1933 skip = 0;
1934 else if (gc->data.width != 1)
1935 skip = 0;
1936 else if (gc->data.size != 1)
1937 skip = 0;
1938 else if (gce->data.data != gc->data.data[0])
1939 skip = 0;
1943 /* Update the selected flag and set the cell. */
1944 selected = screen_check_selection(s, s->cx, s->cy);
1945 if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
1946 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1947 tmp_gc.flags |= GRID_FLAG_SELECTED;
1948 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1949 } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
1950 memcpy(&tmp_gc, gc, sizeof tmp_gc);
1951 tmp_gc.flags &= ~GRID_FLAG_SELECTED;
1952 grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1953 } else if (!skip)
1954 grid_view_set_cell(gd, s->cx, s->cy, gc);
1955 if (selected)
1956 skip = 0;
1959 * Move the cursor. If not wrapping, stick at the last character and
1960 * replace it.
1962 last = !(s->mode & MODE_WRAP);
1963 if (s->cx <= sx - last - width)
1964 screen_write_set_cursor(ctx, s->cx + width, -1);
1965 else
1966 screen_write_set_cursor(ctx, sx - last, -1);
1968 /* Create space for character in insert mode. */
1969 if (s->mode & MODE_INSERT) {
1970 screen_write_collect_flush(ctx, 0, __func__);
1971 ttyctx.num = width;
1972 tty_write(tty_cmd_insertcharacter, &ttyctx);
1975 /* Write to the screen. */
1976 if (!skip) {
1977 if (selected) {
1978 screen_select_cell(s, &tmp_gc, gc);
1979 ttyctx.cell = &tmp_gc;
1980 } else
1981 ttyctx.cell = gc;
1982 tty_write(tty_cmd_cell, &ttyctx);
1986 /* Combine a UTF-8 zero-width character onto the previous. */
1987 static const struct grid_cell *
1988 screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud,
1989 u_int *xx, u_int *cx)
1991 struct screen *s = ctx->s;
1992 struct grid *gd = s->grid;
1993 static struct grid_cell gc;
1994 u_int n, width;
1996 /* Can't combine if at 0. */
1997 if (s->cx == 0) {
1998 *xx = 0;
1999 return (NULL);
2001 *xx = s->cx;
2003 /* Empty data is out. */
2004 if (ud->size == 0)
2005 fatalx("UTF-8 data empty");
2007 /* Retrieve the previous cell. */
2008 for (n = 1; n <= s->cx; n++) {
2009 grid_view_get_cell(gd, s->cx - n, s->cy, &gc);
2010 if (~gc.flags & GRID_FLAG_PADDING)
2011 break;
2013 if (n > s->cx)
2014 return (NULL);
2016 /* Check there is enough space. */
2017 if (gc.data.size + ud->size > sizeof gc.data.data)
2018 return (NULL);
2019 (*xx) -= n;
2021 log_debug("%s: %.*s onto %.*s at %u,%u (width %u)", __func__,
2022 (int)ud->size, ud->data, (int)gc.data.size, gc.data.data, *xx,
2023 s->cy, gc.data.width);
2025 /* Append the data. */
2026 memcpy(gc.data.data + gc.data.size, ud->data, ud->size);
2027 gc.data.size += ud->size;
2028 width = gc.data.width;
2030 /* If this is U+FE0F VARIATION SELECTOR-16, force the width to 2. */
2031 if (gc.data.width == 1 &&
2032 ud->size == 3 &&
2033 memcmp(ud->data, "\357\270\217", 3) == 0) {
2034 grid_view_set_padding(gd, (*xx) + 1, s->cy);
2035 gc.data.width = 2;
2036 width += 2;
2039 /* Set the new cell. */
2040 grid_view_set_cell(gd, *xx, s->cy, &gc);
2042 *cx = (*xx) + width;
2043 log_debug("%s: character at %u; cursor at %u", __func__, *xx, *cx);
2044 return (&gc);
2048 * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2049 * cell on the screen, so following cells must not be drawn by marking them as
2050 * padding.
2052 * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2053 * character, it is necessary to also overwrite any other cells which covered
2054 * by the same character.
2056 static int
2057 screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2058 u_int width)
2060 struct screen *s = ctx->s;
2061 struct grid *gd = s->grid;
2062 struct grid_cell tmp_gc;
2063 u_int xx;
2064 int done = 0;
2066 if (gc->flags & GRID_FLAG_PADDING) {
2068 * A padding cell, so clear any following and leading padding
2069 * cells back to the character. Don't overwrite the current
2070 * cell as that happens later anyway.
2072 xx = s->cx + 1;
2073 while (--xx > 0) {
2074 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2075 if (~tmp_gc.flags & GRID_FLAG_PADDING)
2076 break;
2077 log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
2078 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2081 /* Overwrite the character at the start of this padding. */
2082 log_debug("%s: character at %u,%u", __func__, xx, s->cy);
2083 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2084 done = 1;
2088 * Overwrite any padding cells that belong to any UTF-8 characters
2089 * we'll be overwriting with the current character.
2091 if (width != 1 ||
2092 gc->data.width != 1 ||
2093 gc->flags & GRID_FLAG_PADDING) {
2094 xx = s->cx + width - 1;
2095 while (++xx < screen_size_x(s)) {
2096 grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2097 if (~tmp_gc.flags & GRID_FLAG_PADDING)
2098 break;
2099 log_debug("%s: overwrite at %u,%u", __func__, xx,
2100 s->cy);
2101 grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2102 done = 1;
2106 return (done);
2109 /* Set external clipboard. */
2110 void
2111 screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
2112 u_char *str, u_int len)
2114 struct tty_ctx ttyctx;
2116 screen_write_initctx(ctx, &ttyctx, 0);
2117 ttyctx.ptr = str;
2118 ttyctx.ptr2 = (void *)flags;
2119 ttyctx.num = len;
2121 tty_write(tty_cmd_setselection, &ttyctx);
2124 /* Write unmodified string. */
2125 void
2126 screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2127 int allow_invisible_panes)
2129 struct tty_ctx ttyctx;
2131 screen_write_initctx(ctx, &ttyctx, 0);
2132 ttyctx.ptr = str;
2133 ttyctx.num = len;
2134 ttyctx.allow_invisible_panes = allow_invisible_panes;
2136 tty_write(tty_cmd_rawstring, &ttyctx);
2139 /* Turn alternate screen on. */
2140 void
2141 screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2142 int cursor)
2144 struct tty_ctx ttyctx;
2145 struct window_pane *wp = ctx->wp;
2147 if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2148 return;
2150 screen_write_collect_flush(ctx, 0, __func__);
2151 screen_alternate_on(ctx->s, gc, cursor);
2153 screen_write_initctx(ctx, &ttyctx, 1);
2154 if (ttyctx.redraw_cb != NULL)
2155 ttyctx.redraw_cb(&ttyctx);
2158 /* Turn alternate screen off. */
2159 void
2160 screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2161 int cursor)
2163 struct tty_ctx ttyctx;
2164 struct window_pane *wp = ctx->wp;
2166 if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2167 return;
2169 screen_write_collect_flush(ctx, 0, __func__);
2170 screen_alternate_off(ctx->s, gc, cursor);
2172 screen_write_initctx(ctx, &ttyctx, 1);
2173 if (ttyctx.redraw_cb != NULL)
2174 ttyctx.redraw_cb(&ttyctx);