Annotation of src/usr.bin/tmux/screen-write.c, Revision 1.224
1.224 ! nicm 1: /* $OpenBSD: screen-write.c,v 1.223 2023/10/23 08:12:00 nicm Exp $ */
1.1 nicm 2:
3: /*
1.84 nicm 4: * Copyright (c) 2007 Nicholas Marriott <nicholas.marriott@gmail.com>
1.1 nicm 5: *
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.
9: *
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.
17: */
18:
19: #include <sys/types.h>
20:
1.56 nicm 21: #include <stdlib.h>
1.1 nicm 22: #include <string.h>
23:
24: #include "tmux.h"
25:
1.193 nicm 26: static struct screen_write_citem *screen_write_collect_trim(
27: struct screen_write_ctx *, u_int, u_int, u_int, int *);
1.109 nicm 28: static void screen_write_collect_clear(struct screen_write_ctx *, u_int,
29: u_int);
1.193 nicm 30: static void screen_write_collect_scroll(struct screen_write_ctx *, u_int);
1.164 nicm 31: static void screen_write_collect_flush(struct screen_write_ctx *, int,
32: const char *);
1.89 nicm 33: static int screen_write_overwrite(struct screen_write_ctx *,
34: struct grid_cell *, u_int);
1.222 nicm 35: static int screen_write_combine(struct screen_write_ctx *,
36: const struct grid_cell *);
1.1 nicm 37:
1.193 nicm 38: struct screen_write_citem {
39: u_int x;
40: int wrapped;
41:
42: enum { TEXT, CLEAR } type;
43: u_int used;
44: u_int bg;
1.109 nicm 45:
1.193 nicm 46: struct grid_cell gc;
1.109 nicm 47:
1.193 nicm 48: TAILQ_ENTRY(screen_write_citem) entry;
1.109 nicm 49: };
1.193 nicm 50: struct screen_write_cline {
51: char *data;
52: TAILQ_HEAD(, screen_write_citem) items;
1.109 nicm 53: };
1.193 nicm 54: TAILQ_HEAD(, screen_write_citem) screen_write_citem_freelist =
55: TAILQ_HEAD_INITIALIZER(screen_write_citem_freelist);
56:
57: static struct screen_write_citem *
58: screen_write_get_citem(void)
59: {
60: struct screen_write_citem *ci;
61:
62: ci = TAILQ_FIRST(&screen_write_citem_freelist);
63: if (ci != NULL) {
64: TAILQ_REMOVE(&screen_write_citem_freelist, ci, entry);
65: memset(ci, 0, sizeof *ci);
66: return (ci);
67: }
68: return (xcalloc(1, sizeof *ci));
69: }
70:
71: static void
72: screen_write_free_citem(struct screen_write_citem *ci)
73: {
74: TAILQ_INSERT_TAIL(&screen_write_citem_freelist, ci, entry);
75: }
1.92 nicm 76:
1.139 nicm 77: static void
78: screen_write_offset_timer(__unused int fd, __unused short events, void *data)
79: {
80: struct window *w = data;
81:
82: tty_update_window_offset(w);
83: }
84:
85: /* Set cursor position. */
86: static void
87: screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy)
88: {
89: struct window_pane *wp = ctx->wp;
90: struct window *w;
91: struct screen *s = ctx->s;
92: struct timeval tv = { .tv_usec = 10000 };
93:
94: if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy)
95: return;
96:
1.144 nicm 97: if (cx != -1) {
1.145 nicm 98: if ((u_int)cx > screen_size_x(s)) /* allow last column */
1.144 nicm 99: cx = screen_size_x(s) - 1;
1.139 nicm 100: s->cx = cx;
1.144 nicm 101: }
102: if (cy != -1) {
103: if ((u_int)cy > screen_size_y(s) - 1)
104: cy = screen_size_y(s) - 1;
1.139 nicm 105: s->cy = cy;
1.144 nicm 106: }
1.139 nicm 107:
108: if (wp == NULL)
109: return;
110: w = wp->window;
111:
112: if (!event_initialized(&w->offset_timer))
113: evtimer_set(&w->offset_timer, screen_write_offset_timer, w);
114: if (!evtimer_pending(&w->offset_timer, NULL))
115: evtimer_add(&w->offset_timer, &tv);
116: }
117:
1.178 nicm 118: /* Do a full redraw. */
119: static void
120: screen_write_redraw_cb(const struct tty_ctx *ttyctx)
121: {
122: struct window_pane *wp = ttyctx->arg;
123:
1.185 nicm 124: if (wp != NULL)
125: wp->flags |= PANE_REDRAW;
1.178 nicm 126: }
127:
128: /* Update context for client. */
129: static int
130: screen_write_set_client_cb(struct tty_ctx *ttyctx, struct client *c)
131: {
132: struct window_pane *wp = ttyctx->arg;
1.214 nicm 133:
134: if (ttyctx->allow_invisible_panes) {
135: if (session_has(c->session, wp->window))
136: return (1);
137: return (0);
138: }
1.178 nicm 139:
140: if (c->session->curw->window != wp->window)
141: return (0);
142: if (wp->layout_cell == NULL)
143: return (0);
144:
145: if (wp->flags & (PANE_REDRAW|PANE_DROP))
146: return (-1);
147: if (c->flags & CLIENT_REDRAWPANES) {
148: /*
149: * Redraw is already deferred to redraw another pane - redraw
150: * this one also when that happens.
151: */
1.193 nicm 152: log_debug("%s: adding %%%u to deferred redraw", __func__,
153: wp->id);
1.178 nicm 154: wp->flags |= PANE_REDRAW;
155: return (-1);
156: }
157:
158: ttyctx->bigger = tty_window_offset(&c->tty, &ttyctx->wox, &ttyctx->woy,
159: &ttyctx->wsx, &ttyctx->wsy);
160:
161: ttyctx->xoff = ttyctx->rxoff = wp->xoff;
162: ttyctx->yoff = ttyctx->ryoff = wp->yoff;
163:
164: if (status_at_line(c) == 0)
165: ttyctx->yoff += status_line_size(c);
166:
167: return (1);
168: }
169:
1.163 nicm 170: /* Set up context for TTY command. */
171: static void
172: screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx,
173: int sync)
174: {
175: struct screen *s = ctx->s;
176:
177: memset(ttyctx, 0, sizeof *ttyctx);
178:
1.178 nicm 179: ttyctx->s = s;
1.177 nicm 180: ttyctx->sx = screen_size_x(s);
181: ttyctx->sy = screen_size_y(s);
1.163 nicm 182:
183: ttyctx->ocx = s->cx;
184: ttyctx->ocy = s->cy;
185: ttyctx->orlower = s->rlower;
186: ttyctx->orupper = s->rupper;
187:
1.197 nicm 188: memcpy(&ttyctx->defaults, &grid_default_cell, sizeof ttyctx->defaults);
189: if (ctx->init_ctx_cb != NULL) {
1.178 nicm 190: ctx->init_ctx_cb(ctx, ttyctx);
1.197 nicm 191: if (ttyctx->palette != NULL) {
1.204 nicm 192: if (ttyctx->defaults.fg == 8)
1.203 nicm 193: ttyctx->defaults.fg = ttyctx->palette->fg;
1.204 nicm 194: if (ttyctx->defaults.bg == 8)
1.203 nicm 195: ttyctx->defaults.bg = ttyctx->palette->bg;
1.197 nicm 196: }
197: } else {
1.178 nicm 198: ttyctx->redraw_cb = screen_write_redraw_cb;
1.197 nicm 199: if (ctx->wp != NULL) {
200: tty_default_colours(&ttyctx->defaults, ctx->wp);
201: ttyctx->palette = &ctx->wp->palette;
1.178 nicm 202: ttyctx->set_client_cb = screen_write_set_client_cb;
1.197 nicm 203: ttyctx->arg = ctx->wp;
204: }
1.178 nicm 205: }
206:
1.199 nicm 207: if (~ctx->flags & SCREEN_WRITE_SYNC) {
208: /*
209: * For the active pane or for an overlay (no pane), we want to
210: * only use synchronized updates if requested (commands that
211: * move the cursor); for other panes, always use it, since the
212: * cursor will have to move.
213: */
214: if (ctx->wp != NULL) {
215: if (ctx->wp != ctx->wp->window->active)
216: ttyctx->num = 1;
217: else
218: ttyctx->num = sync;
219: } else
220: ttyctx->num = 0x10|sync;
1.163 nicm 221: tty_write(tty_cmd_syncstart, ttyctx);
1.180 nicm 222: ctx->flags |= SCREEN_WRITE_SYNC;
1.163 nicm 223: }
224: }
225:
1.170 nicm 226: /* Make write list. */
227: void
228: screen_write_make_list(struct screen *s)
229: {
230: u_int y;
231:
232: s->write_list = xcalloc(screen_size_y(s), sizeof *s->write_list);
233: for (y = 0; y < screen_size_y(s); y++)
234: TAILQ_INIT(&s->write_list[y].items);
235: }
236:
237: /* Free write list. */
238: void
239: screen_write_free_list(struct screen *s)
240: {
241: u_int y;
242:
243: for (y = 0; y < screen_size_y(s); y++)
244: free(s->write_list[y].data);
245: free(s->write_list);
246: }
247:
1.178 nicm 248: /* Set up for writing. */
249: static void
250: screen_write_init(struct screen_write_ctx *ctx, struct screen *s)
1.1 nicm 251: {
1.109 nicm 252: memset(ctx, 0, sizeof *ctx);
1.92 nicm 253:
1.178 nicm 254: ctx->s = s;
1.92 nicm 255:
1.170 nicm 256: if (ctx->s->write_list == NULL)
257: screen_write_make_list(ctx->s);
1.193 nicm 258: ctx->item = screen_write_get_citem();
1.92 nicm 259:
1.122 nicm 260: ctx->scrolled = 0;
261: ctx->bg = 8;
1.178 nicm 262: }
263:
264: /* Initialize writing with a pane. */
265: void
266: screen_write_start_pane(struct screen_write_ctx *ctx, struct window_pane *wp,
267: struct screen *s)
268: {
269: if (s == NULL)
270: s = wp->screen;
271: screen_write_init(ctx, s);
272: ctx->wp = wp;
273:
274: if (log_get_level() != 0) {
275: log_debug("%s: size %ux%u, pane %%%u (at %u,%u)",
276: __func__, screen_size_x(ctx->s), screen_size_y(ctx->s),
277: wp->id, wp->xoff, wp->yoff);
278: }
279: }
280:
281: /* Initialize writing with a callback. */
282: void
283: screen_write_start_callback(struct screen_write_ctx *ctx, struct screen *s,
284: screen_write_init_ctx_cb cb, void *arg)
285: {
286: screen_write_init(ctx, s);
287:
288: ctx->init_ctx_cb = cb;
289: ctx->arg = arg;
290:
291: if (log_get_level() != 0) {
292: log_debug("%s: size %ux%u, with callback", __func__,
293: screen_size_x(ctx->s), screen_size_y(ctx->s));
294: }
295: }
296:
297: /* Initialize writing. */
298: void
299: screen_write_start(struct screen_write_ctx *ctx, struct screen *s)
300: {
301: screen_write_init(ctx, s);
1.122 nicm 302:
1.158 nicm 303: if (log_get_level() != 0) {
1.178 nicm 304: log_debug("%s: size %ux%u, no pane", __func__,
305: screen_size_x(ctx->s), screen_size_y(ctx->s));
1.139 nicm 306: }
1.1 nicm 307: }
308:
309: /* Finish writing. */
310: void
1.92 nicm 311: screen_write_stop(struct screen_write_ctx *ctx)
312: {
1.109 nicm 313: screen_write_collect_end(ctx);
1.164 nicm 314: screen_write_collect_flush(ctx, 0, __func__);
1.92 nicm 315:
1.193 nicm 316: screen_write_free_citem(ctx->item);
1.52 nicm 317: }
318:
319: /* Reset screen state. */
320: void
321: screen_write_reset(struct screen_write_ctx *ctx)
322: {
1.61 nicm 323: struct screen *s = ctx->s;
1.52 nicm 324:
1.61 nicm 325: screen_reset_tabs(s);
326: screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1);
1.63 nicm 327:
1.215 nicm 328: s->mode = MODE_CURSOR|MODE_WRAP;
329: if (options_get_number(global_options, "extended-keys") == 2)
330: s->mode |= MODE_KEXTENDED;
1.52 nicm 331:
1.99 nicm 332: screen_write_clearscreen(ctx, 8);
1.144 nicm 333: screen_write_set_cursor(ctx, 0, 0);
1.1 nicm 334: }
335:
336: /* Write character. */
337: void
1.86 nicm 338: screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
1.69 nicm 339: u_char ch)
1.1 nicm 340: {
1.86 nicm 341: struct grid_cell gc;
342:
343: memcpy(&gc, gcp, sizeof gc);
344:
345: utf8_set(&gc.data, ch);
346: screen_write_cell(ctx, &gc);
1.1 nicm 347: }
348:
1.2 nicm 349: /* Calculate string length. */
1.71 nicm 350: size_t
1.75 nicm 351: screen_write_strlen(const char *fmt, ...)
1.2 nicm 352: {
1.36 nicm 353: va_list ap;
354: char *msg;
1.76 nicm 355: struct utf8_data ud;
1.36 nicm 356: u_char *ptr;
357: size_t left, size = 0;
1.79 nicm 358: enum utf8_state more;
1.2 nicm 359:
360: va_start(ap, fmt);
361: xvasprintf(&msg, fmt, ap);
362: va_end(ap);
363:
364: ptr = msg;
365: while (*ptr != '\0') {
1.79 nicm 366: if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) {
1.36 nicm 367: ptr++;
1.2 nicm 368:
369: left = strlen(ptr);
1.77 nicm 370: if (left < (size_t)ud.size - 1)
1.36 nicm 371: break;
1.79 nicm 372: while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE)
1.2 nicm 373: ptr++;
1.36 nicm 374: ptr++;
375:
1.79 nicm 376: if (more == UTF8_DONE)
1.78 nicm 377: size += ud.width;
1.2 nicm 378: } else {
1.75 nicm 379: if (*ptr > 0x1f && *ptr < 0x7f)
380: size++;
1.2 nicm 381: ptr++;
382: }
1.7 ray 383: }
1.2 nicm 384:
1.56 nicm 385: free(msg);
1.2 nicm 386: return (size);
387: }
388:
1.179 nicm 389: /* Write string wrapped over lines. */
390: int
391: screen_write_text(struct screen_write_ctx *ctx, u_int cx, u_int width,
392: u_int lines, int more, const struct grid_cell *gcp, const char *fmt, ...)
393: {
394: struct screen *s = ctx->s;
395: va_list ap;
396: char *tmp;
397: u_int cy = s->cy, i, end, next, idx = 0, at, left;
398: struct utf8_data *text;
399: struct grid_cell gc;
400:
401: memcpy(&gc, gcp, sizeof gc);
402:
403: va_start(ap, fmt);
404: xvasprintf(&tmp, fmt, ap);
405: va_end(ap);
406:
407: text = utf8_fromcstr(tmp);
408: free(tmp);
409:
410: left = (cx + width) - s->cx;
411: for (;;) {
412: /* Find the end of what can fit on the line. */
413: at = 0;
414: for (end = idx; text[end].size != 0; end++) {
415: if (text[end].size == 1 && text[end].data[0] == '\n')
416: break;
417: if (at + text[end].width > left)
418: break;
419: at += text[end].width;
420: }
421:
422: /*
423: * If we're on a space, that's the end. If not, walk back to
424: * try and find one.
425: */
426: if (text[end].size == 0)
427: next = end;
428: else if (text[end].size == 1 && text[end].data[0] == '\n')
429: next = end + 1;
430: else if (text[end].size == 1 && text[end].data[0] == ' ')
431: next = end + 1;
432: else {
433: for (i = end; i > idx; i--) {
434: if (text[i].size == 1 && text[i].data[0] == ' ')
435: break;
436: }
437: if (i != idx) {
438: next = i + 1;
439: end = i;
440: } else
441: next = end;
442: }
443:
444: /* Print the line. */
445: for (i = idx; i < end; i++) {
446: utf8_copy(&gc.data, &text[i]);
447: screen_write_cell(ctx, &gc);
448: }
449:
450: /* If at the bottom, stop. */
451: idx = next;
452: if (s->cy == cy + lines - 1 || text[idx].size == 0)
453: break;
454:
455: screen_write_cursormove(ctx, cx, s->cy + 1, 0);
456: left = width;
457: }
458:
459: /*
460: * Fail if on the last line and there is more to come or at the end, or
461: * if the text was not entirely consumed.
462: */
463: if ((s->cy == cy + lines - 1 && (!more || s->cx == cx + width)) ||
464: text[idx].size != 0) {
465: free(text);
466: return (0);
467: }
468: free(text);
469:
470: /*
471: * If no more to come, move to the next line. Otherwise, leave on
472: * the same line (except if at the end).
473: */
474: if (!more || s->cx == cx + width)
475: screen_write_cursormove(ctx, cx, s->cy + 1, 0);
476: return (1);
477: }
478:
479: /* Write simple string (no maximum length). */
1.71 nicm 480: void
1.86 nicm 481: screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp,
1.71 nicm 482: const char *fmt, ...)
1.1 nicm 483: {
484: va_list ap;
485:
486: va_start(ap, fmt);
1.86 nicm 487: screen_write_vnputs(ctx, -1, gcp, fmt, ap);
1.2 nicm 488: va_end(ap);
489: }
490:
491: /* Write string with length limit (-1 for unlimited). */
1.71 nicm 492: void
493: screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen,
1.86 nicm 494: const struct grid_cell *gcp, const char *fmt, ...)
1.2 nicm 495: {
496: va_list ap;
497:
498: va_start(ap, fmt);
1.86 nicm 499: screen_write_vnputs(ctx, maxlen, gcp, fmt, ap);
1.2 nicm 500: va_end(ap);
501: }
502:
503: void
1.3 nicm 504: screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
1.86 nicm 505: const struct grid_cell *gcp, const char *fmt, va_list ap)
1.2 nicm 506: {
1.86 nicm 507: struct grid_cell gc;
508: struct utf8_data *ud = &gc.data;
1.36 nicm 509: char *msg;
510: u_char *ptr;
511: size_t left, size = 0;
1.79 nicm 512: enum utf8_state more;
1.2 nicm 513:
1.86 nicm 514: memcpy(&gc, gcp, sizeof gc);
1.1 nicm 515: xvasprintf(&msg, fmt, ap);
516:
1.2 nicm 517: ptr = msg;
518: while (*ptr != '\0') {
1.86 nicm 519: if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) {
1.36 nicm 520: ptr++;
1.2 nicm 521:
522: left = strlen(ptr);
1.86 nicm 523: if (left < (size_t)ud->size - 1)
1.36 nicm 524: break;
1.86 nicm 525: while ((more = utf8_append(ud, *ptr)) == UTF8_MORE)
1.2 nicm 526: ptr++;
1.36 nicm 527: ptr++;
1.7 ray 528:
1.86 nicm 529: if (more != UTF8_DONE)
530: continue;
531: if (maxlen > 0 && size + ud->width > (size_t)maxlen) {
532: while (size < (size_t)maxlen) {
533: screen_write_putc(ctx, &gc, ' ');
534: size++;
1.2 nicm 535: }
1.86 nicm 536: break;
1.2 nicm 537: }
1.86 nicm 538: size += ud->width;
539: screen_write_cell(ctx, &gc);
1.2 nicm 540: } else {
1.86 nicm 541: if (maxlen > 0 && size + 1 > (size_t)maxlen)
1.2 nicm 542: break;
543:
1.57 nicm 544: if (*ptr == '\001')
1.86 nicm 545: gc.attr ^= GRID_ATTR_CHARSET;
1.187 nicm 546: else if (*ptr == '\n') {
547: screen_write_linefeed(ctx, 0, 8);
548: screen_write_carriagereturn(ctx);
549: } else if (*ptr > 0x1f && *ptr < 0x7f) {
1.57 nicm 550: size++;
1.86 nicm 551: screen_write_putc(ctx, &gc, *ptr);
1.75 nicm 552: }
1.24 nicm 553: ptr++;
554: }
555: }
556:
1.56 nicm 557: free(msg);
1.1 nicm 558: }
559:
1.132 nicm 560: /*
1.176 nicm 561: * Copy from another screen but without the selection stuff. Assumes the target
562: * region is already big enough.
1.132 nicm 563: */
564: void
565: screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src,
566: u_int px, u_int py, u_int nx, u_int ny)
567: {
568: struct screen *s = ctx->s;
569: struct grid *gd = src->grid;
570: struct grid_cell gc;
571: u_int xx, yy, cx, cy;
1.96 nicm 572:
1.132 nicm 573: if (nx == 0 || ny == 0)
574: return;
575:
576: cy = s->cy;
577: for (yy = py; yy < py + ny; yy++) {
1.134 nicm 578: if (yy >= gd->hsize + gd->sy)
579: break;
1.132 nicm 580: cx = s->cx;
581: for (xx = px; xx < px + nx; xx++) {
1.137 nicm 582: if (xx >= grid_get_line(gd, yy)->cellsize)
1.132 nicm 583: break;
584: grid_get_cell(gd, xx, yy, &gc);
1.135 nicm 585: if (xx + gc.data.width > px + nx)
586: break;
1.150 nicm 587: grid_view_set_cell(ctx->s->grid, cx, cy, &gc);
1.132 nicm 588: cx++;
589: }
1.1 nicm 590: cy++;
1.125 nicm 591: }
592: }
593:
1.216 nicm 594: /* Select character set for drawing border lines. */
595: static void
596: screen_write_box_border_set(enum box_lines lines, int cell_type,
597: struct grid_cell *gc)
598: {
599: switch (lines) {
600: case BOX_LINES_NONE:
601: break;
602: case BOX_LINES_DOUBLE:
603: gc->attr &= ~GRID_ATTR_CHARSET;
604: utf8_copy(&gc->data, tty_acs_double_borders(cell_type));
605: break;
606: case BOX_LINES_HEAVY:
607: gc->attr &= ~GRID_ATTR_CHARSET;
608: utf8_copy(&gc->data, tty_acs_heavy_borders(cell_type));
609: break;
610: case BOX_LINES_ROUNDED:
611: gc->attr &= ~GRID_ATTR_CHARSET;
612: utf8_copy(&gc->data, tty_acs_rounded_borders(cell_type));
613: break;
614: case BOX_LINES_SIMPLE:
615: gc->attr &= ~GRID_ATTR_CHARSET;
616: utf8_set(&gc->data, SIMPLE_BORDERS[cell_type]);
617: break;
618: case BOX_LINES_PADDED:
619: gc->attr &= ~GRID_ATTR_CHARSET;
620: utf8_set(&gc->data, PADDED_BORDERS[cell_type]);
621: break;
622: case BOX_LINES_SINGLE:
623: case BOX_LINES_DEFAULT:
624: gc->attr |= GRID_ATTR_CHARSET;
625: utf8_set(&gc->data, CELL_BORDERS[cell_type]);
626: break;
627: }
628: }
629:
1.129 nicm 630: /* Draw a horizontal line on screen. */
1.125 nicm 631: void
1.216 nicm 632: screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right,
633: enum box_lines lines, const struct grid_cell *border_gc)
1.125 nicm 634: {
635: struct screen *s = ctx->s;
636: struct grid_cell gc;
637: u_int cx, cy, i;
638:
639: cx = s->cx;
640: cy = s->cy;
641:
1.216 nicm 642: if (border_gc != NULL)
643: memcpy(&gc, border_gc, sizeof gc);
644: else
645: memcpy(&gc, &grid_default_cell, sizeof gc);
1.125 nicm 646: gc.attr |= GRID_ATTR_CHARSET;
647:
1.216 nicm 648: if (left)
649: screen_write_box_border_set(lines, CELL_LEFTJOIN, &gc);
650: else
651: screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
652: screen_write_cell(ctx, &gc);
653:
654: screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
1.125 nicm 655: for (i = 1; i < nx - 1; i++)
1.216 nicm 656: screen_write_cell(ctx, &gc);
657:
658: if (right)
659: screen_write_box_border_set(lines, CELL_RIGHTJOIN, &gc);
660: else
661: screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
662: screen_write_cell(ctx, &gc);
1.129 nicm 663:
1.144 nicm 664: screen_write_set_cursor(ctx, cx, cy);
1.129 nicm 665: }
666:
1.195 nicm 667: /* Draw a vertical line on screen. */
1.129 nicm 668: void
1.130 nicm 669: screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom)
1.129 nicm 670: {
671: struct screen *s = ctx->s;
672: struct grid_cell gc;
673: u_int cx, cy, i;
674:
675: cx = s->cx;
676: cy = s->cy;
677:
678: memcpy(&gc, &grid_default_cell, sizeof gc);
679: gc.attr |= GRID_ATTR_CHARSET;
680:
681: screen_write_putc(ctx, &gc, top ? 'w' : 'x');
682: for (i = 1; i < ny - 1; i++) {
1.144 nicm 683: screen_write_set_cursor(ctx, cx, cy + i);
1.129 nicm 684: screen_write_putc(ctx, &gc, 'x');
685: }
1.144 nicm 686: screen_write_set_cursor(ctx, cx, cy + ny - 1);
1.129 nicm 687: screen_write_putc(ctx, &gc, bottom ? 'v' : 'x');
1.152 nicm 688:
689: screen_write_set_cursor(ctx, cx, cy);
690: }
691:
692: /* Draw a menu on screen. */
693: void
1.216 nicm 694: screen_write_menu(struct screen_write_ctx *ctx, struct menu *menu, int choice,
695: enum box_lines lines, const struct grid_cell *menu_gc,
696: const struct grid_cell *border_gc, const struct grid_cell *choice_gc)
1.152 nicm 697: {
698: struct screen *s = ctx->s;
1.161 nicm 699: struct grid_cell default_gc;
700: const struct grid_cell *gc = &default_gc;
1.216 nicm 701: u_int cx, cy, i, j, width = menu->width;
1.153 nicm 702: const char *name;
1.152 nicm 703:
704: cx = s->cx;
705: cy = s->cy;
706:
1.216 nicm 707: memcpy(&default_gc, menu_gc, sizeof default_gc);
1.152 nicm 708:
1.216 nicm 709: screen_write_box(ctx, menu->width + 4, menu->count + 2, lines,
710: border_gc, menu->title);
1.152 nicm 711:
712: for (i = 0; i < menu->count; i++) {
1.153 nicm 713: name = menu->items[i].name;
714: if (name == NULL) {
1.152 nicm 715: screen_write_cursormove(ctx, cx, cy + 1 + i, 0);
1.217 nicm 716: screen_write_hline(ctx, width + 4, 1, 1, lines,
717: border_gc);
1.216 nicm 718: continue;
719: }
720:
721: if (choice >= 0 && i == (u_int)choice && *name != '-')
722: gc = choice_gc;
723:
1.217 nicm 724: screen_write_cursormove(ctx, cx + 1, cy + 1 + i, 0);
725: for (j = 0; j < width + 2; j++)
1.216 nicm 726: screen_write_putc(ctx, gc, ' ');
727:
728: screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0);
729: if (*name == '-') {
730: default_gc.attr |= GRID_ATTR_DIM;
731: format_draw(ctx, gc, width, name + 1, NULL, 0);
732: default_gc.attr &= ~GRID_ATTR_DIM;
733: continue;
1.152 nicm 734: }
1.216 nicm 735:
1.218 nicm 736: format_draw(ctx, gc, width, name, NULL, 0);
1.216 nicm 737: gc = &default_gc;
1.152 nicm 738: }
1.125 nicm 739:
1.144 nicm 740: screen_write_set_cursor(ctx, cx, cy);
1.201 nicm 741: }
742:
1.125 nicm 743: /* Draw a box on screen. */
744: void
1.200 nicm 745: screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny,
1.202 nicm 746: enum box_lines lines, const struct grid_cell *gcp, const char *title)
1.125 nicm 747: {
748: struct screen *s = ctx->s;
1.200 nicm 749: struct grid_cell gc;
1.125 nicm 750: u_int cx, cy, i;
751:
752: cx = s->cx;
753: cy = s->cy;
754:
1.200 nicm 755: if (gcp != NULL)
756: memcpy(&gc, gcp, sizeof gc);
757: else
758: memcpy(&gc, &grid_default_cell, sizeof gc);
1.202 nicm 759:
1.125 nicm 760: gc.attr |= GRID_ATTR_CHARSET;
1.197 nicm 761: gc.flags |= GRID_FLAG_NOPALETTE;
1.125 nicm 762:
1.201 nicm 763: /* Draw top border */
1.202 nicm 764: screen_write_box_border_set(lines, CELL_TOPLEFT, &gc);
1.201 nicm 765: screen_write_cell(ctx, &gc);
1.202 nicm 766: screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
1.125 nicm 767: for (i = 1; i < nx - 1; i++)
1.201 nicm 768: screen_write_cell(ctx, &gc);
1.202 nicm 769: screen_write_box_border_set(lines, CELL_TOPRIGHT, &gc);
1.201 nicm 770: screen_write_cell(ctx, &gc);
1.125 nicm 771:
1.201 nicm 772: /* Draw bottom border */
1.144 nicm 773: screen_write_set_cursor(ctx, cx, cy + ny - 1);
1.202 nicm 774: screen_write_box_border_set(lines, CELL_BOTTOMLEFT, &gc);
1.201 nicm 775: screen_write_cell(ctx, &gc);
1.202 nicm 776: screen_write_box_border_set(lines, CELL_LEFTRIGHT, &gc);
1.125 nicm 777: for (i = 1; i < nx - 1; i++)
1.201 nicm 778: screen_write_cell(ctx, &gc);
1.202 nicm 779: screen_write_box_border_set(lines, CELL_BOTTOMRIGHT, &gc);
1.201 nicm 780: screen_write_cell(ctx, &gc);
1.125 nicm 781:
1.201 nicm 782: /* Draw sides */
1.202 nicm 783: screen_write_box_border_set(lines, CELL_TOPBOTTOM, &gc);
1.125 nicm 784: for (i = 1; i < ny - 1; i++) {
1.201 nicm 785: /* left side */
1.144 nicm 786: screen_write_set_cursor(ctx, cx, cy + i);
1.201 nicm 787: screen_write_cell(ctx, &gc);
788: /* right side */
1.144 nicm 789: screen_write_set_cursor(ctx, cx + nx - 1, cy + i);
1.201 nicm 790: screen_write_cell(ctx, &gc);
1.202 nicm 791: }
792:
793: if (title != NULL) {
794: gc.attr &= ~GRID_ATTR_CHARSET;
795: screen_write_cursormove(ctx, cx + 2, cy, 0);
1.205 nicm 796: format_draw(ctx, &gc, nx - 4, title, NULL, 0);
1.125 nicm 797: }
798:
1.144 nicm 799: screen_write_set_cursor(ctx, cx, cy);
1.125 nicm 800: }
801:
1.132 nicm 802: /*
803: * Write a preview version of a window. Assumes target area is big enough and
804: * already cleared.
805: */
1.125 nicm 806: void
807: screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx,
808: u_int ny)
809: {
810: struct screen *s = ctx->s;
811: struct grid_cell gc;
812: u_int cx, cy, px, py;
813:
814: cx = s->cx;
815: cy = s->cy;
816:
817: /*
818: * If the cursor is on, pick the area around the cursor, otherwise use
819: * the top left.
820: */
821: if (src->mode & MODE_CURSOR) {
822: px = src->cx;
823: if (px < nx / 3)
824: px = 0;
825: else
826: px = px - nx / 3;
827: if (px + nx > screen_size_x(src)) {
828: if (nx > screen_size_x(src))
829: px = 0;
830: else
831: px = screen_size_x(src) - nx;
832: }
833: py = src->cy;
834: if (py < ny / 3)
835: py = 0;
836: else
837: py = py - ny / 3;
838: if (py + ny > screen_size_y(src)) {
839: if (ny > screen_size_y(src))
840: py = 0;
841: else
842: py = screen_size_y(src) - ny;
843: }
844: } else {
845: px = 0;
846: py = 0;
847: }
848:
1.132 nicm 849: screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny);
1.125 nicm 850:
851: if (src->mode & MODE_CURSOR) {
852: grid_view_get_cell(src->grid, src->cx, src->cy, &gc);
853: gc.attr |= GRID_ATTR_REVERSE;
1.144 nicm 854: screen_write_set_cursor(ctx, cx + (src->cx - px),
1.125 nicm 855: cy + (src->cy - py));
856: screen_write_cell(ctx, &gc);
1.1 nicm 857: }
858: }
859:
1.61 nicm 860: /* Set a mode. */
861: void
862: screen_write_mode_set(struct screen_write_ctx *ctx, int mode)
863: {
864: struct screen *s = ctx->s;
865:
866: s->mode |= mode;
1.194 nicm 867:
868: if (log_get_level() != 0)
869: log_debug("%s: %s", __func__, screen_mode_to_string(mode));
1.61 nicm 870: }
871:
872: /* Clear a mode. */
873: void
874: screen_write_mode_clear(struct screen_write_ctx *ctx, int mode)
875: {
876: struct screen *s = ctx->s;
877:
878: s->mode &= ~mode;
1.194 nicm 879:
880: if (log_get_level() != 0)
881: log_debug("%s: %s", __func__, screen_mode_to_string(mode));
1.61 nicm 882: }
883:
1.1 nicm 884: /* Cursor up by ny. */
885: void
886: screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
887: {
888: struct screen *s = ctx->s;
1.139 nicm 889: u_int cx = s->cx, cy = s->cy;
1.1 nicm 890:
891: if (ny == 0)
892: ny = 1;
893:
1.139 nicm 894: if (cy < s->rupper) {
1.12 nicm 895: /* Above region. */
1.139 nicm 896: if (ny > cy)
897: ny = cy;
1.12 nicm 898: } else {
899: /* Below region. */
1.139 nicm 900: if (ny > cy - s->rupper)
901: ny = cy - s->rupper;
1.12 nicm 902: }
1.139 nicm 903: if (cx == screen_size_x(s))
904: cx--;
905:
906: cy -= ny;
1.1 nicm 907:
1.139 nicm 908: screen_write_set_cursor(ctx, cx, cy);
1.1 nicm 909: }
910:
911: /* Cursor down by ny. */
912: void
913: screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
914: {
915: struct screen *s = ctx->s;
1.139 nicm 916: u_int cx = s->cx, cy = s->cy;
1.1 nicm 917:
918: if (ny == 0)
919: ny = 1;
920:
1.139 nicm 921: if (cy > s->rlower) {
1.12 nicm 922: /* Below region. */
1.139 nicm 923: if (ny > screen_size_y(s) - 1 - cy)
924: ny = screen_size_y(s) - 1 - cy;
1.12 nicm 925: } else {
926: /* Above region. */
1.139 nicm 927: if (ny > s->rlower - cy)
928: ny = s->rlower - cy;
1.12 nicm 929: }
1.139 nicm 930: if (cx == screen_size_x(s))
931: cx--;
932: else if (ny == 0)
1.1 nicm 933: return;
934:
1.139 nicm 935: cy += ny;
936:
937: screen_write_set_cursor(ctx, cx, cy);
1.1 nicm 938: }
939:
1.101 nicm 940: /* Cursor right by nx. */
1.1 nicm 941: void
942: screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
943: {
944: struct screen *s = ctx->s;
1.139 nicm 945: u_int cx = s->cx, cy = s->cy;
1.1 nicm 946:
947: if (nx == 0)
948: nx = 1;
949:
1.139 nicm 950: if (nx > screen_size_x(s) - 1 - cx)
951: nx = screen_size_x(s) - 1 - cx;
1.1 nicm 952: if (nx == 0)
953: return;
954:
1.139 nicm 955: cx += nx;
956:
957: screen_write_set_cursor(ctx, cx, cy);
1.1 nicm 958: }
959:
960: /* Cursor left by nx. */
961: void
962: screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
963: {
964: struct screen *s = ctx->s;
1.139 nicm 965: u_int cx = s->cx, cy = s->cy;
1.1 nicm 966:
967: if (nx == 0)
968: nx = 1;
969:
1.139 nicm 970: if (nx > cx)
971: nx = cx;
1.1 nicm 972: if (nx == 0)
973: return;
974:
1.139 nicm 975: cx -= nx;
976:
977: screen_write_set_cursor(ctx, cx, cy);
1.5 nicm 978: }
979:
1.29 nicm 980: /* Backspace; cursor left unless at start of wrapped line when can move up. */
981: void
982: screen_write_backspace(struct screen_write_ctx *ctx)
983: {
984: struct screen *s = ctx->s;
985: struct grid_line *gl;
1.139 nicm 986: u_int cx = s->cx, cy = s->cy;
1.29 nicm 987:
1.139 nicm 988: if (cx == 0) {
989: if (cy == 0)
1.29 nicm 990: return;
1.139 nicm 991: gl = grid_get_line(s->grid, s->grid->hsize + cy - 1);
1.29 nicm 992: if (gl->flags & GRID_LINE_WRAPPED) {
1.139 nicm 993: cy--;
994: cx = screen_size_x(s) - 1;
1.29 nicm 995: }
996: } else
1.139 nicm 997: cx--;
998:
999: screen_write_set_cursor(ctx, cx, cy);
1.29 nicm 1000: }
1001:
1.5 nicm 1002: /* VT100 alignment test. */
1003: void
1004: screen_write_alignmenttest(struct screen_write_ctx *ctx)
1005: {
1006: struct screen *s = ctx->s;
1.17 nicm 1007: struct tty_ctx ttyctx;
1.5 nicm 1008: struct grid_cell gc;
1009: u_int xx, yy;
1010:
1011: memcpy(&gc, &grid_default_cell, sizeof gc);
1.77 nicm 1012: utf8_set(&gc.data, 'E');
1.7 ray 1013:
1.5 nicm 1014: for (yy = 0; yy < screen_size_y(s); yy++) {
1015: for (xx = 0; xx < screen_size_x(s); xx++)
1016: grid_view_set_cell(s->grid, xx, yy, &gc);
1017: }
1.7 ray 1018:
1.139 nicm 1019: screen_write_set_cursor(ctx, 0, 0);
1.5 nicm 1020:
1021: s->rupper = 0;
1022: s->rlower = screen_size_y(s) - 1;
1.143 nicm 1023:
1.163 nicm 1024: screen_write_initctx(ctx, &ttyctx, 1);
1.5 nicm 1025:
1.109 nicm 1026: screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1);
1.17 nicm 1027: tty_write(tty_cmd_alignmenttest, &ttyctx);
1.1 nicm 1028: }
1029:
1030: /* Insert nx characters. */
1031: void
1.99 nicm 1032: screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1.1 nicm 1033: {
1034: struct screen *s = ctx->s;
1.17 nicm 1035: struct tty_ctx ttyctx;
1.1 nicm 1036:
1037: if (nx == 0)
1038: nx = 1;
1039:
1.9 nicm 1040: if (nx > screen_size_x(s) - s->cx)
1041: nx = screen_size_x(s) - s->cx;
1.1 nicm 1042: if (nx == 0)
1043: return;
1044:
1.107 nicm 1045: if (s->cx > screen_size_x(s) - 1)
1046: return;
1047:
1.163 nicm 1048: screen_write_initctx(ctx, &ttyctx, 0);
1.107 nicm 1049: ttyctx.bg = bg;
1.1 nicm 1050:
1.107 nicm 1051: grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg);
1.1 nicm 1052:
1.164 nicm 1053: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1054: ttyctx.num = nx;
1055: tty_write(tty_cmd_insertcharacter, &ttyctx);
1.1 nicm 1056: }
1057:
1058: /* Delete nx characters. */
1059: void
1.99 nicm 1060: screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1.1 nicm 1061: {
1062: struct screen *s = ctx->s;
1.17 nicm 1063: struct tty_ctx ttyctx;
1.1 nicm 1064:
1065: if (nx == 0)
1066: nx = 1;
1067:
1.9 nicm 1068: if (nx > screen_size_x(s) - s->cx)
1069: nx = screen_size_x(s) - s->cx;
1.1 nicm 1070: if (nx == 0)
1071: return;
1072:
1.107 nicm 1073: if (s->cx > screen_size_x(s) - 1)
1074: return;
1075:
1.163 nicm 1076: screen_write_initctx(ctx, &ttyctx, 0);
1.107 nicm 1077: ttyctx.bg = bg;
1.1 nicm 1078:
1.107 nicm 1079: grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg);
1.1 nicm 1080:
1.164 nicm 1081: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1082: ttyctx.num = nx;
1083: tty_write(tty_cmd_deletecharacter, &ttyctx);
1.59 nicm 1084: }
1085:
1086: /* Clear nx characters. */
1087: void
1.121 nicm 1088: screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg)
1.59 nicm 1089: {
1090: struct screen *s = ctx->s;
1091: struct tty_ctx ttyctx;
1092:
1093: if (nx == 0)
1094: nx = 1;
1095:
1096: if (nx > screen_size_x(s) - s->cx)
1097: nx = screen_size_x(s) - s->cx;
1098: if (nx == 0)
1099: return;
1100:
1.107 nicm 1101: if (s->cx > screen_size_x(s) - 1)
1102: return;
1103:
1.163 nicm 1104: screen_write_initctx(ctx, &ttyctx, 0);
1.121 nicm 1105: ttyctx.bg = bg;
1.59 nicm 1106:
1.124 nicm 1107: grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg);
1.59 nicm 1108:
1.164 nicm 1109: screen_write_collect_flush(ctx, 0, __func__);
1.59 nicm 1110: ttyctx.num = nx;
1111: tty_write(tty_cmd_clearcharacter, &ttyctx);
1.1 nicm 1112: }
1113:
1114: /* Insert ny lines. */
1115: void
1.99 nicm 1116: screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1.1 nicm 1117: {
1118: struct screen *s = ctx->s;
1.107 nicm 1119: struct grid *gd = s->grid;
1.17 nicm 1120: struct tty_ctx ttyctx;
1.1 nicm 1121:
1122: if (ny == 0)
1123: ny = 1;
1124:
1.11 nicm 1125: if (s->cy < s->rupper || s->cy > s->rlower) {
1126: if (ny > screen_size_y(s) - s->cy)
1127: ny = screen_size_y(s) - s->cy;
1128: if (ny == 0)
1129: return;
1130:
1.163 nicm 1131: screen_write_initctx(ctx, &ttyctx, 1);
1.107 nicm 1132: ttyctx.bg = bg;
1.11 nicm 1133:
1.107 nicm 1134: grid_view_insert_lines(gd, s->cy, ny, bg);
1.11 nicm 1135:
1.164 nicm 1136: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1137: ttyctx.num = ny;
1138: tty_write(tty_cmd_insertline, &ttyctx);
1.11 nicm 1139: return;
1140: }
1141:
1142: if (ny > s->rlower + 1 - s->cy)
1143: ny = s->rlower + 1 - s->cy;
1.1 nicm 1144: if (ny == 0)
1145: return;
1.41 nicm 1146:
1.163 nicm 1147: screen_write_initctx(ctx, &ttyctx, 1);
1.107 nicm 1148: ttyctx.bg = bg;
1.1 nicm 1149:
1150: if (s->cy < s->rupper || s->cy > s->rlower)
1.107 nicm 1151: grid_view_insert_lines(gd, s->cy, ny, bg);
1152: else
1153: grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg);
1.1 nicm 1154:
1.164 nicm 1155: screen_write_collect_flush(ctx, 0, __func__);
1156:
1.17 nicm 1157: ttyctx.num = ny;
1158: tty_write(tty_cmd_insertline, &ttyctx);
1.1 nicm 1159: }
1160:
1161: /* Delete ny lines. */
1162: void
1.99 nicm 1163: screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg)
1.1 nicm 1164: {
1165: struct screen *s = ctx->s;
1.107 nicm 1166: struct grid *gd = s->grid;
1.17 nicm 1167: struct tty_ctx ttyctx;
1.1 nicm 1168:
1169: if (ny == 0)
1170: ny = 1;
1171:
1.11 nicm 1172: if (s->cy < s->rupper || s->cy > s->rlower) {
1173: if (ny > screen_size_y(s) - s->cy)
1174: ny = screen_size_y(s) - s->cy;
1175: if (ny == 0)
1176: return;
1177:
1.163 nicm 1178: screen_write_initctx(ctx, &ttyctx, 1);
1.107 nicm 1179: ttyctx.bg = bg;
1.11 nicm 1180:
1.107 nicm 1181: grid_view_delete_lines(gd, s->cy, ny, bg);
1.11 nicm 1182:
1.164 nicm 1183: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1184: ttyctx.num = ny;
1185: tty_write(tty_cmd_deleteline, &ttyctx);
1.11 nicm 1186: return;
1187: }
1.41 nicm 1188:
1.11 nicm 1189: if (ny > s->rlower + 1 - s->cy)
1190: ny = s->rlower + 1 - s->cy;
1.1 nicm 1191: if (ny == 0)
1192: return;
1193:
1.163 nicm 1194: screen_write_initctx(ctx, &ttyctx, 1);
1.107 nicm 1195: ttyctx.bg = bg;
1.1 nicm 1196:
1197: if (s->cy < s->rupper || s->cy > s->rlower)
1.107 nicm 1198: grid_view_delete_lines(gd, s->cy, ny, bg);
1199: else
1200: grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg);
1.1 nicm 1201:
1.164 nicm 1202: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1203: ttyctx.num = ny;
1204: tty_write(tty_cmd_deleteline, &ttyctx);
1.1 nicm 1205: }
1206:
1207: /* Clear line at cursor. */
1208: void
1.99 nicm 1209: screen_write_clearline(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1210: {
1.193 nicm 1211: struct screen *s = ctx->s;
1212: struct grid_line *gl;
1213: u_int sx = screen_size_x(s);
1214: struct screen_write_citem *ci = ctx->item;
1.1 nicm 1215:
1.137 nicm 1216: gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1.140 nicm 1217: if (gl->cellsize == 0 && COLOUR_DEFAULT(bg))
1.92 nicm 1218: return;
1.1 nicm 1219:
1.99 nicm 1220: grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1221:
1.109 nicm 1222: screen_write_collect_clear(ctx, s->cy, 1);
1.193 nicm 1223: ci->x = 0;
1224: ci->used = sx;
1225: ci->type = CLEAR;
1226: ci->bg = bg;
1227: TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1228: ctx->item = screen_write_get_citem();
1.1 nicm 1229: }
1230:
1231: /* Clear to end of line from cursor. */
1232: void
1.99 nicm 1233: screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1234: {
1.193 nicm 1235: struct screen *s = ctx->s;
1236: struct grid_line *gl;
1237: u_int sx = screen_size_x(s);
1238: struct screen_write_citem *ci = ctx->item, *before;
1.165 nicm 1239:
1240: if (s->cx == 0) {
1241: screen_write_clearline(ctx, bg);
1242: return;
1243: }
1.1 nicm 1244:
1.137 nicm 1245: gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1.140 nicm 1246: if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg)))
1.92 nicm 1247: return;
1.108 nicm 1248:
1.99 nicm 1249: grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg);
1250:
1.193 nicm 1251: before = screen_write_collect_trim(ctx, s->cy, s->cx, sx - s->cx, NULL);
1.186 nicm 1252: ci->x = s->cx;
1.193 nicm 1253: ci->used = sx - s->cx;
1254: ci->type = CLEAR;
1.186 nicm 1255: ci->bg = bg;
1.193 nicm 1256: if (before == NULL)
1257: TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1258: else
1259: TAILQ_INSERT_BEFORE(before, ci, entry);
1260: ctx->item = screen_write_get_citem();
1.1 nicm 1261: }
1262:
1263: /* Clear to start of line from cursor. */
1264: void
1.99 nicm 1265: screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1266: {
1.165 nicm 1267: struct screen *s = ctx->s;
1.193 nicm 1268: u_int sx = screen_size_x(s);
1269: struct screen_write_citem *ci = ctx->item, *before;
1.165 nicm 1270:
1271: if (s->cx >= sx - 1) {
1272: screen_write_clearline(ctx, bg);
1273: return;
1274: }
1.1 nicm 1275:
1.109 nicm 1276: if (s->cx > sx - 1)
1.99 nicm 1277: grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1.109 nicm 1278: else
1.99 nicm 1279: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1.1 nicm 1280:
1.193 nicm 1281: before = screen_write_collect_trim(ctx, s->cy, 0, s->cx + 1, NULL);
1282: ci->x = 0;
1283: ci->used = s->cx + 1;
1284: ci->type = CLEAR;
1.186 nicm 1285: ci->bg = bg;
1.193 nicm 1286: if (before == NULL)
1287: TAILQ_INSERT_TAIL(&ctx->s->write_list[s->cy].items, ci, entry);
1288: else
1289: TAILQ_INSERT_BEFORE(before, ci, entry);
1290: ctx->item = screen_write_get_citem();
1.1 nicm 1291: }
1292:
1.101 nicm 1293: /* Move cursor to px,py. */
1.1 nicm 1294: void
1.147 nicm 1295: screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py,
1296: int origin)
1.1 nicm 1297: {
1298: struct screen *s = ctx->s;
1299:
1.147 nicm 1300: if (origin && py != -1 && (s->mode & MODE_ORIGIN)) {
1.146 nicm 1301: if ((u_int)py > s->rlower - s->rupper)
1.144 nicm 1302: py = s->rlower;
1303: else
1304: py += s->rupper;
1305: }
1.145 nicm 1306:
1.146 nicm 1307: if (px != -1 && (u_int)px > screen_size_x(s) - 1)
1.145 nicm 1308: px = screen_size_x(s) - 1;
1.146 nicm 1309: if (py != -1 && (u_int)py > screen_size_y(s) - 1)
1.145 nicm 1310: py = screen_size_y(s) - 1;
1.1 nicm 1311:
1.193 nicm 1312: log_debug("%s: from %u,%u to %u,%u", __func__, s->cx, s->cy, px, py);
1.139 nicm 1313: screen_write_set_cursor(ctx, px, py);
1.1 nicm 1314: }
1315:
1.101 nicm 1316: /* Reverse index (up with scroll). */
1.1 nicm 1317: void
1.122 nicm 1318: screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1319: {
1320: struct screen *s = ctx->s;
1.17 nicm 1321: struct tty_ctx ttyctx;
1.1 nicm 1322:
1.165 nicm 1323: if (s->cy == s->rupper) {
1324: grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg);
1325: screen_write_collect_flush(ctx, 0, __func__);
1326:
1327: screen_write_initctx(ctx, &ttyctx, 1);
1328: ttyctx.bg = bg;
1.1 nicm 1329:
1.165 nicm 1330: tty_write(tty_cmd_reverseindex, &ttyctx);
1331: } else if (s->cy > 0)
1.139 nicm 1332: screen_write_set_cursor(ctx, -1, s->cy - 1);
1.1 nicm 1333:
1334: }
1335:
1336: /* Set scroll region. */
1337: void
1.83 nicm 1338: screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper,
1339: u_int rlower)
1.1 nicm 1340: {
1341: struct screen *s = ctx->s;
1342:
1343: if (rupper > screen_size_y(s) - 1)
1344: rupper = screen_size_y(s) - 1;
1345: if (rlower > screen_size_y(s) - 1)
1346: rlower = screen_size_y(s) - 1;
1.13 nicm 1347: if (rupper >= rlower) /* cannot be one line */
1.1 nicm 1348: return;
1349:
1.164 nicm 1350: screen_write_collect_flush(ctx, 0, __func__);
1.110 nicm 1351:
1.1 nicm 1352: /* Cursor moves to top-left. */
1.139 nicm 1353: screen_write_set_cursor(ctx, 0, 0);
1.1 nicm 1354:
1355: s->rupper = rupper;
1356: s->rlower = rlower;
1357: }
1358:
1.34 nicm 1359: /* Line feed. */
1.1 nicm 1360: void
1.122 nicm 1361: screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg)
1.1 nicm 1362: {
1.20 nicm 1363: struct screen *s = ctx->s;
1.109 nicm 1364: struct grid *gd = s->grid;
1.20 nicm 1365: struct grid_line *gl;
1.1 nicm 1366:
1.137 nicm 1367: gl = grid_get_line(gd, gd->hsize + s->cy);
1.20 nicm 1368: if (wrapped)
1369: gl->flags |= GRID_LINE_WRAPPED;
1370:
1.114 nicm 1371: log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1372: s->rupper, s->rlower);
1373:
1.122 nicm 1374: if (bg != ctx->bg) {
1.164 nicm 1375: screen_write_collect_flush(ctx, 1, __func__);
1.122 nicm 1376: ctx->bg = bg;
1377: }
1378:
1.92 nicm 1379: if (s->cy == s->rlower) {
1.122 nicm 1380: grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1.193 nicm 1381: screen_write_collect_scroll(ctx, bg);
1.110 nicm 1382: ctx->scrolled++;
1.109 nicm 1383: } else if (s->cy < screen_size_y(s) - 1)
1.139 nicm 1384: screen_write_set_cursor(ctx, -1, s->cy + 1);
1.1 nicm 1385: }
1386:
1.110 nicm 1387: /* Scroll up. */
1388: void
1.122 nicm 1389: screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1.110 nicm 1390: {
1391: struct screen *s = ctx->s;
1392: struct grid *gd = s->grid;
1393: u_int i;
1394:
1395: if (lines == 0)
1396: lines = 1;
1397: else if (lines > s->rlower - s->rupper + 1)
1398: lines = s->rlower - s->rupper + 1;
1399:
1.122 nicm 1400: if (bg != ctx->bg) {
1.164 nicm 1401: screen_write_collect_flush(ctx, 1, __func__);
1.122 nicm 1402: ctx->bg = bg;
1403: }
1404:
1.110 nicm 1405: for (i = 0; i < lines; i++) {
1.122 nicm 1406: grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg);
1.193 nicm 1407: screen_write_collect_scroll(ctx, bg);
1.110 nicm 1408: }
1409: ctx->scrolled += lines;
1.157 nicm 1410: }
1411:
1412: /* Scroll down. */
1413: void
1414: screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg)
1415: {
1416: struct screen *s = ctx->s;
1417: struct grid *gd = s->grid;
1418: struct tty_ctx ttyctx;
1419: u_int i;
1420:
1.163 nicm 1421: screen_write_initctx(ctx, &ttyctx, 1);
1.157 nicm 1422: ttyctx.bg = bg;
1423:
1424: if (lines == 0)
1425: lines = 1;
1426: else if (lines > s->rlower - s->rupper + 1)
1427: lines = s->rlower - s->rupper + 1;
1428:
1429: for (i = 0; i < lines; i++)
1430: grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg);
1431:
1.164 nicm 1432: screen_write_collect_flush(ctx, 0, __func__);
1.157 nicm 1433: ttyctx.num = lines;
1434: tty_write(tty_cmd_scrolldown, &ttyctx);
1.110 nicm 1435: }
1436:
1.1 nicm 1437: /* Carriage return (cursor to start of line). */
1438: void
1439: screen_write_carriagereturn(struct screen_write_ctx *ctx)
1440: {
1.139 nicm 1441: screen_write_set_cursor(ctx, 0, -1);
1.1 nicm 1442: }
1443:
1444: /* Clear to end of screen from cursor. */
1445: void
1.99 nicm 1446: screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1447: {
1448: struct screen *s = ctx->s;
1.107 nicm 1449: struct grid *gd = s->grid;
1.17 nicm 1450: struct tty_ctx ttyctx;
1.92 nicm 1451: u_int sx = screen_size_x(s), sy = screen_size_y(s);
1.1 nicm 1452:
1.163 nicm 1453: screen_write_initctx(ctx, &ttyctx, 1);
1.99 nicm 1454: ttyctx.bg = bg;
1.1 nicm 1455:
1.46 nicm 1456: /* Scroll into history if it is enabled and clearing entire screen. */
1.206 nicm 1457: if (s->cx == 0 &&
1458: s->cy == 0 &&
1459: (gd->flags & GRID_HISTORY) &&
1460: ctx->wp != NULL &&
1461: options_get_number(ctx->wp->options, "scroll-on-clear"))
1.107 nicm 1462: grid_view_clear_history(gd, bg);
1.109 nicm 1463: else {
1464: if (s->cx <= sx - 1)
1.107 nicm 1465: grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg);
1466: grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg);
1.46 nicm 1467: }
1.1 nicm 1468:
1.109 nicm 1469: screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1));
1.164 nicm 1470: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1471: tty_write(tty_cmd_clearendofscreen, &ttyctx);
1.1 nicm 1472: }
1473:
1474: /* Clear to start of screen. */
1475: void
1.105 nicm 1476: screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1477: {
1478: struct screen *s = ctx->s;
1.17 nicm 1479: struct tty_ctx ttyctx;
1.92 nicm 1480: u_int sx = screen_size_x(s);
1.1 nicm 1481:
1.163 nicm 1482: screen_write_initctx(ctx, &ttyctx, 1);
1.105 nicm 1483: ttyctx.bg = bg;
1.1 nicm 1484:
1.109 nicm 1485: if (s->cy > 0)
1.105 nicm 1486: grid_view_clear(s->grid, 0, 0, sx, s->cy, bg);
1.109 nicm 1487: if (s->cx > sx - 1)
1.120 nicm 1488: grid_view_clear(s->grid, 0, s->cy, sx, 1, bg);
1.109 nicm 1489: else
1.120 nicm 1490: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg);
1.1 nicm 1491:
1.109 nicm 1492: screen_write_collect_clear(ctx, 0, s->cy);
1.164 nicm 1493: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1494: tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1.1 nicm 1495: }
1496:
1497: /* Clear entire screen. */
1498: void
1.99 nicm 1499: screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg)
1.1 nicm 1500: {
1501: struct screen *s = ctx->s;
1.17 nicm 1502: struct tty_ctx ttyctx;
1.92 nicm 1503: u_int sx = screen_size_x(s), sy = screen_size_y(s);
1.1 nicm 1504:
1.163 nicm 1505: screen_write_initctx(ctx, &ttyctx, 1);
1.99 nicm 1506: ttyctx.bg = bg;
1.1 nicm 1507:
1.46 nicm 1508: /* Scroll into history if it is enabled. */
1.207 nicm 1509: if ((s->grid->flags & GRID_HISTORY) &&
1510: ctx->wp != NULL &&
1511: options_get_number(ctx->wp->options, "scroll-on-clear"))
1.99 nicm 1512: grid_view_clear_history(s->grid, bg);
1.83 nicm 1513: else
1.99 nicm 1514: grid_view_clear(s->grid, 0, 0, sx, sy, bg);
1.1 nicm 1515:
1.109 nicm 1516: screen_write_collect_clear(ctx, 0, sy);
1.17 nicm 1517: tty_write(tty_cmd_clearscreen, &ttyctx);
1.51 nicm 1518: }
1519:
1520: /* Clear entire history. */
1521: void
1522: screen_write_clearhistory(struct screen_write_ctx *ctx)
1523: {
1.156 nicm 1524: grid_clear_history(ctx->s->grid);
1.197 nicm 1525: }
1526:
1527: /* Force a full redraw. */
1528: void
1529: screen_write_fullredraw(struct screen_write_ctx *ctx)
1530: {
1531: struct tty_ctx ttyctx;
1532:
1533: screen_write_collect_flush(ctx, 0, __func__);
1534:
1.211 nicm 1535: screen_write_initctx(ctx, &ttyctx, 1);
1536: if (ttyctx.redraw_cb != NULL)
1.210 nicm 1537: ttyctx.redraw_cb(&ttyctx);
1.1 nicm 1538: }
1539:
1.193 nicm 1540: /* Trim collected items. */
1541: static struct screen_write_citem *
1542: screen_write_collect_trim(struct screen_write_ctx *ctx, u_int y, u_int x,
1543: u_int used, int *wrapped)
1544: {
1545: struct screen_write_cline *cl = &ctx->s->write_list[y];
1546: struct screen_write_citem *ci, *ci2, *tmp, *before = NULL;
1547: u_int sx = x, ex = x + used - 1;
1548: u_int csx, cex;
1549:
1550: if (TAILQ_EMPTY(&cl->items))
1551: return (NULL);
1552: TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1553: csx = ci->x;
1554: cex = ci->x + ci->used - 1;
1555:
1556: /* Item is entirely before. */
1557: if (cex < sx) {
1558: log_debug("%s: %p %u-%u before %u-%u", __func__, ci,
1559: csx, cex, sx, ex);
1560: continue;
1561: }
1.165 nicm 1562:
1.193 nicm 1563: /* Item is entirely after. */
1564: if (csx > ex) {
1565: log_debug("%s: %p %u-%u after %u-%u", __func__, ci,
1566: csx, cex, sx, ex);
1567: before = ci;
1.165 nicm 1568: break;
1.193 nicm 1569: }
1570:
1571: /* Item is entirely inside. */
1572: if (csx >= sx && cex <= ex) {
1573: log_debug("%s: %p %u-%u inside %u-%u", __func__, ci,
1574: csx, cex, sx, ex);
1575: TAILQ_REMOVE(&cl->items, ci, entry);
1576: screen_write_free_citem(ci);
1577: if (csx == 0 && ci->wrapped && wrapped != NULL)
1578: *wrapped = 1;
1.165 nicm 1579: continue;
1580: }
1581:
1.193 nicm 1582: /* Item under the start. */
1583: if (csx < sx && cex >= sx && cex <= ex) {
1584: log_debug("%s: %p %u-%u start %u-%u", __func__, ci,
1585: csx, cex, sx, ex);
1586: ci->used = sx - csx;
1587: log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1588: ci->x + ci->used + 1);
1.165 nicm 1589: continue;
1.193 nicm 1590: }
1591:
1592: /* Item covers the end. */
1593: if (cex > ex && csx >= sx && csx <= ex) {
1594: log_debug("%s: %p %u-%u end %u-%u", __func__, ci,
1595: csx, cex, sx, ex);
1596: ci->x = ex + 1;
1597: ci->used = cex - ex;
1598: log_debug("%s: %p now %u-%u", __func__, ci, ci->x,
1599: ci->x + ci->used + 1);
1600: before = ci;
1.165 nicm 1601: break;
1602: }
1.193 nicm 1603:
1604: /* Item must cover both sides. */
1605: log_debug("%s: %p %u-%u under %u-%u", __func__, ci,
1606: csx, cex, sx, ex);
1607: ci2 = screen_write_get_citem();
1608: ci2->type = ci->type;
1609: ci2->bg = ci->bg;
1610: memcpy(&ci2->gc, &ci->gc, sizeof ci2->gc);
1611: TAILQ_INSERT_AFTER(&cl->items, ci, ci2, entry);
1612:
1613: ci->used = sx - csx;
1614: ci2->x = ex + 1;
1615: ci2->used = cex - ex;
1616:
1617: log_debug("%s: %p now %u-%u (%p) and %u-%u (%p)", __func__, ci,
1618: ci->x, ci->x + ci->used - 1, ci, ci2->x,
1619: ci2->x + ci2->used - 1, ci2);
1620: before = ci2;
1621: break;
1622: }
1623: return (before);
1.165 nicm 1624: }
1625:
1.170 nicm 1626: /* Clear collected lines. */
1.109 nicm 1627: static void
1628: screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n)
1629: {
1.193 nicm 1630: struct screen_write_cline *cl;
1631: u_int i;
1.109 nicm 1632:
1.151 nicm 1633: for (i = y; i < y + n; i++) {
1.172 nicm 1634: cl = &ctx->s->write_list[i];
1.193 nicm 1635: TAILQ_CONCAT(&screen_write_citem_freelist, &cl->items, entry);
1.109 nicm 1636: }
1637: }
1638:
1639: /* Scroll collected lines up. */
1640: static void
1.193 nicm 1641: screen_write_collect_scroll(struct screen_write_ctx *ctx, u_int bg)
1.109 nicm 1642: {
1.193 nicm 1643: struct screen *s = ctx->s;
1644: struct screen_write_cline *cl;
1645: u_int y;
1646: char *saved;
1647: struct screen_write_citem *ci;
1.109 nicm 1648:
1.114 nicm 1649: log_debug("%s: at %u,%u (region %u-%u)", __func__, s->cx, s->cy,
1650: s->rupper, s->rlower);
1651:
1.109 nicm 1652: screen_write_collect_clear(ctx, s->rupper, 1);
1.172 nicm 1653: saved = ctx->s->write_list[s->rupper].data;
1.109 nicm 1654: for (y = s->rupper; y < s->rlower; y++) {
1.172 nicm 1655: cl = &ctx->s->write_list[y + 1];
1656: TAILQ_CONCAT(&ctx->s->write_list[y].items, &cl->items, entry);
1657: ctx->s->write_list[y].data = cl->data;
1.109 nicm 1658: }
1.172 nicm 1659: ctx->s->write_list[s->rlower].data = saved;
1.193 nicm 1660:
1661: ci = screen_write_get_citem();
1662: ci->x = 0;
1663: ci->used = screen_size_x(s);
1664: ci->type = CLEAR;
1665: ci->bg = bg;
1666: TAILQ_INSERT_TAIL(&ctx->s->write_list[s->rlower].items, ci, entry);
1.109 nicm 1667: }
1668:
1669: /* Flush collected lines. */
1670: static void
1.164 nicm 1671: screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only,
1672: const char *from)
1.109 nicm 1673: {
1.193 nicm 1674: struct screen *s = ctx->s;
1675: struct screen_write_citem *ci, *tmp;
1676: struct screen_write_cline *cl;
1677: u_int y, cx, cy, last, items = 0;
1678: struct tty_ctx ttyctx;
1.110 nicm 1679:
1680: if (ctx->scrolled != 0) {
1681: log_debug("%s: scrolled %u (region %u-%u)", __func__,
1682: ctx->scrolled, s->rupper, s->rlower);
1683: if (ctx->scrolled > s->rlower - s->rupper + 1)
1684: ctx->scrolled = s->rlower - s->rupper + 1;
1685:
1.163 nicm 1686: screen_write_initctx(ctx, &ttyctx, 1);
1.110 nicm 1687: ttyctx.num = ctx->scrolled;
1.122 nicm 1688: ttyctx.bg = ctx->bg;
1.110 nicm 1689: tty_write(tty_cmd_scrollup, &ttyctx);
1690: }
1691: ctx->scrolled = 0;
1.122 nicm 1692: ctx->bg = 8;
1693:
1.111 nicm 1694: if (scroll_only)
1695: return;
1.109 nicm 1696:
1697: cx = s->cx; cy = s->cy;
1698: for (y = 0; y < screen_size_y(s); y++) {
1.172 nicm 1699: cl = &ctx->s->write_list[y];
1.193 nicm 1700: last = UINT_MAX;
1.172 nicm 1701: TAILQ_FOREACH_SAFE(ci, &cl->items, entry, tmp) {
1.193 nicm 1702: if (last != UINT_MAX && ci->x <= last) {
1703: fatalx("collect list not in order: %u <= %u",
1704: ci->x, last);
1705: }
1.144 nicm 1706: screen_write_set_cursor(ctx, ci->x, y);
1.193 nicm 1707: if (ci->type == CLEAR) {
1.167 nicm 1708: screen_write_initctx(ctx, &ttyctx, 1);
1.165 nicm 1709: ttyctx.bg = ci->bg;
1.193 nicm 1710: ttyctx.num = ci->used;
1711: tty_write(tty_cmd_clearcharacter, &ttyctx);
1.165 nicm 1712: } else {
1.167 nicm 1713: screen_write_initctx(ctx, &ttyctx, 0);
1.165 nicm 1714: ttyctx.cell = &ci->gc;
1715: ttyctx.wrapped = ci->wrapped;
1.172 nicm 1716: ttyctx.ptr = cl->data + ci->x;
1.165 nicm 1717: ttyctx.num = ci->used;
1718: tty_write(tty_cmd_cells, &ttyctx);
1719: }
1.116 nicm 1720: items++;
1.109 nicm 1721:
1.172 nicm 1722: TAILQ_REMOVE(&cl->items, ci, entry);
1.193 nicm 1723: screen_write_free_citem(ci);
1724: last = ci->x;
1.109 nicm 1725: }
1726: }
1727: s->cx = cx; s->cy = cy;
1.116 nicm 1728:
1.193 nicm 1729: log_debug("%s: flushed %u items (%s)", __func__, items, from);
1.109 nicm 1730: }
1731:
1732: /* Finish and store collected cells. */
1733: void
1734: screen_write_collect_end(struct screen_write_ctx *ctx)
1735: {
1.193 nicm 1736: struct screen *s = ctx->s;
1737: struct screen_write_citem *ci = ctx->item, *before;
1738: struct screen_write_cline *cl = &s->write_list[s->cy];
1739: struct grid_cell gc;
1740: u_int xx;
1741: int wrapped = ci->wrapped;
1.109 nicm 1742:
1743: if (ci->used == 0)
1744: return;
1745:
1.193 nicm 1746: before = screen_write_collect_trim(ctx, s->cy, s->cx, ci->used,
1747: &wrapped);
1.109 nicm 1748: ci->x = s->cx;
1.193 nicm 1749: ci->wrapped = wrapped;
1750: if (before == NULL)
1751: TAILQ_INSERT_TAIL(&cl->items, ci, entry);
1752: else
1753: TAILQ_INSERT_BEFORE(before, ci, entry);
1754: ctx->item = screen_write_get_citem();
1.109 nicm 1755:
1.170 nicm 1756: log_debug("%s: %u %.*s (at %u,%u)", __func__, ci->used,
1.172 nicm 1757: (int)ci->used, cl->data + ci->x, s->cx, s->cy);
1.109 nicm 1758:
1.131 nicm 1759: if (s->cx != 0) {
1760: for (xx = s->cx; xx > 0; xx--) {
1761: grid_view_get_cell(s->grid, xx, s->cy, &gc);
1762: if (~gc.flags & GRID_FLAG_PADDING)
1763: break;
1.136 nicm 1764: grid_view_set_cell(s->grid, xx, s->cy,
1765: &grid_default_cell);
1.131 nicm 1766: }
1.139 nicm 1767: if (gc.data.width > 1) {
1.136 nicm 1768: grid_view_set_cell(s->grid, xx, s->cy,
1769: &grid_default_cell);
1.139 nicm 1770: }
1.131 nicm 1771: }
1772:
1.172 nicm 1773: grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, cl->data + ci->x,
1774: ci->used);
1.139 nicm 1775: screen_write_set_cursor(ctx, s->cx + ci->used, -1);
1.131 nicm 1776:
1777: for (xx = s->cx; xx < screen_size_x(s); xx++) {
1778: grid_view_get_cell(s->grid, xx, s->cy, &gc);
1779: if (~gc.flags & GRID_FLAG_PADDING)
1780: break;
1781: grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell);
1782: }
1.109 nicm 1783: }
1784:
1785: /* Write cell data, collecting if necessary. */
1786: void
1787: screen_write_collect_add(struct screen_write_ctx *ctx,
1788: const struct grid_cell *gc)
1789: {
1.193 nicm 1790: struct screen *s = ctx->s;
1791: struct screen_write_citem *ci;
1792: u_int sx = screen_size_x(s);
1793: int collect;
1.220 nicm 1794:
1.109 nicm 1795: /*
1796: * Don't need to check that the attributes and whatnot are still the
1.116 nicm 1797: * same - input_parse will end the collection when anything that isn't
1.159 nicm 1798: * a plain character is encountered.
1.109 nicm 1799: */
1800:
1801: collect = 1;
1.136 nicm 1802: if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f)
1.109 nicm 1803: collect = 0;
1804: else if (gc->attr & GRID_ATTR_CHARSET)
1805: collect = 0;
1806: else if (~s->mode & MODE_WRAP)
1807: collect = 0;
1808: else if (s->mode & MODE_INSERT)
1809: collect = 0;
1.138 nicm 1810: else if (s->sel != NULL)
1.109 nicm 1811: collect = 0;
1812: if (!collect) {
1813: screen_write_collect_end(ctx);
1.164 nicm 1814: screen_write_collect_flush(ctx, 0, __func__);
1.109 nicm 1815: screen_write_cell(ctx, gc);
1816: return;
1817: }
1818:
1819: if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx)
1820: screen_write_collect_end(ctx);
1.118 nicm 1821: ci = ctx->item; /* may have changed */
1822:
1.109 nicm 1823: if (s->cx > sx - 1) {
1.114 nicm 1824: log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1.118 nicm 1825: ci->wrapped = 1;
1.122 nicm 1826: screen_write_linefeed(ctx, 1, 8);
1.139 nicm 1827: screen_write_set_cursor(ctx, 0, -1);
1.109 nicm 1828: }
1829:
1830: if (ci->used == 0)
1831: memcpy(&ci->gc, gc, sizeof ci->gc);
1.172 nicm 1832: if (ctx->s->write_list[s->cy].data == NULL)
1833: ctx->s->write_list[s->cy].data = xmalloc(screen_size_x(ctx->s));
1834: ctx->s->write_list[s->cy].data[s->cx + ci->used++] = gc->data.data[0];
1.109 nicm 1835: }
1836:
1.1 nicm 1837: /* Write cell data. */
1838: void
1.60 nicm 1839: screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1.1 nicm 1840: {
1841: struct screen *s = ctx->s;
1842: struct grid *gd = s->grid;
1.222 nicm 1843: const struct utf8_data *ud = &gc->data;
1.109 nicm 1844: struct grid_line *gl;
1845: struct grid_cell_entry *gce;
1846: struct grid_cell tmp_gc, now_gc;
1.15 nicm 1847: struct tty_ctx ttyctx;
1.92 nicm 1848: u_int sx = screen_size_x(s), sy = screen_size_y(s);
1.222 nicm 1849: u_int width = ud->width, xx, not_wrap;
1.109 nicm 1850: int selected, skip = 1;
1.1 nicm 1851:
1.109 nicm 1852: /* Ignore padding cells. */
1.1 nicm 1853: if (gc->flags & GRID_FLAG_PADDING)
1854: return;
1.32 nicm 1855:
1.222 nicm 1856: /* Get the previous cell to check for combining. */
1857: if (screen_write_combine(ctx, gc) != 0)
1.1 nicm 1858: return;
1.112 nicm 1859:
1860: /* Flush any existing scrolling. */
1.164 nicm 1861: screen_write_collect_flush(ctx, 1, __func__);
1.1 nicm 1862:
1.109 nicm 1863: /* If this character doesn't fit, ignore it. */
1864: if ((~s->mode & MODE_WRAP) &&
1865: width > 1 &&
1866: (width > sx || (s->cx != sx && s->cx > sx - width)))
1867: return;
1868:
1.6 nicm 1869: /* If in insert mode, make space for the cells. */
1.98 nicm 1870: if (s->mode & MODE_INSERT) {
1.109 nicm 1871: grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8);
1872: skip = 0;
1873: }
1.6 nicm 1874:
1.20 nicm 1875: /* Check this will fit on the current line and wrap if not. */
1.92 nicm 1876: if ((s->mode & MODE_WRAP) && s->cx > sx - width) {
1.128 nicm 1877: log_debug("%s: wrapped at %u,%u", __func__, s->cx, s->cy);
1.122 nicm 1878: screen_write_linefeed(ctx, 1, 8);
1.139 nicm 1879: screen_write_set_cursor(ctx, 0, -1);
1.164 nicm 1880: screen_write_collect_flush(ctx, 1, __func__);
1.1 nicm 1881: }
1882:
1.64 nicm 1883: /* Sanity check cursor position. */
1.92 nicm 1884: if (s->cx > sx - width || s->cy > sy - 1)
1.1 nicm 1885: return;
1.163 nicm 1886: screen_write_initctx(ctx, &ttyctx, 0);
1.106 nicm 1887:
1.1 nicm 1888: /* Handle overwriting of UTF-8 characters. */
1.137 nicm 1889: gl = grid_get_line(s->grid, s->grid->hsize + s->cy);
1.92 nicm 1890: if (gl->flags & GRID_LINE_EXTENDED) {
1891: grid_view_get_cell(gd, s->cx, s->cy, &now_gc);
1892: if (screen_write_overwrite(ctx, &now_gc, width))
1893: skip = 0;
1894: }
1.1 nicm 1895:
1896: /*
1897: * If the new character is UTF-8 wide, fill in padding cells. Have
1898: * already ensured there is enough room.
1899: */
1.89 nicm 1900: for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1.131 nicm 1901: log_debug("%s: new padding at %u,%u", __func__, xx, s->cy);
1.184 nicm 1902: grid_view_set_padding(gd, xx, s->cy);
1.89 nicm 1903: skip = 0;
1904: }
1905:
1906: /* If no change, do not draw. */
1.92 nicm 1907: if (skip) {
1908: if (s->cx >= gl->cellsize)
1909: skip = grid_cells_equal(gc, &grid_default_cell);
1910: else {
1911: gce = &gl->celldata[s->cx];
1912: if (gce->flags & GRID_FLAG_EXTENDED)
1913: skip = 0;
1.95 nicm 1914: else if (gc->flags != gce->flags)
1.92 nicm 1915: skip = 0;
1916: else if (gc->attr != gce->data.attr)
1917: skip = 0;
1918: else if (gc->fg != gce->data.fg)
1919: skip = 0;
1920: else if (gc->bg != gce->data.bg)
1921: skip = 0;
1.95 nicm 1922: else if (gc->data.width != 1)
1923: skip = 0;
1.109 nicm 1924: else if (gc->data.size != 1)
1925: skip = 0;
1.95 nicm 1926: else if (gce->data.data != gc->data.data[0])
1.92 nicm 1927: skip = 0;
1928: }
1929: }
1.1 nicm 1930:
1.127 nicm 1931: /* Update the selected flag and set the cell. */
1.90 nicm 1932: selected = screen_check_selection(s, s->cx, s->cy);
1.109 nicm 1933: if (selected && (~gc->flags & GRID_FLAG_SELECTED)) {
1.90 nicm 1934: memcpy(&tmp_gc, gc, sizeof tmp_gc);
1935: tmp_gc.flags |= GRID_FLAG_SELECTED;
1936: grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1.109 nicm 1937: } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) {
1.90 nicm 1938: memcpy(&tmp_gc, gc, sizeof tmp_gc);
1939: tmp_gc.flags &= ~GRID_FLAG_SELECTED;
1940: grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc);
1941: } else if (!skip)
1.89 nicm 1942: grid_view_set_cell(gd, s->cx, s->cy, gc);
1.109 nicm 1943: if (selected)
1944: skip = 0;
1.1 nicm 1945:
1.64 nicm 1946: /*
1947: * Move the cursor. If not wrapping, stick at the last character and
1948: * replace it.
1949: */
1.222 nicm 1950: not_wrap = !(s->mode & MODE_WRAP);
1951: if (s->cx <= sx - not_wrap - width)
1.139 nicm 1952: screen_write_set_cursor(ctx, s->cx + width, -1);
1.64 nicm 1953: else
1.222 nicm 1954: screen_write_set_cursor(ctx, sx - not_wrap, -1);
1.1 nicm 1955:
1.89 nicm 1956: /* Create space for character in insert mode. */
1.109 nicm 1957: if (s->mode & MODE_INSERT) {
1.164 nicm 1958: screen_write_collect_flush(ctx, 0, __func__);
1.17 nicm 1959: ttyctx.num = width;
1960: tty_write(tty_cmd_insertcharacter, &ttyctx);
1961: }
1.89 nicm 1962:
1963: /* Write to the screen. */
1.109 nicm 1964: if (!skip) {
1965: if (selected) {
1966: screen_select_cell(s, &tmp_gc, gc);
1967: ttyctx.cell = &tmp_gc;
1968: } else
1969: ttyctx.cell = gc;
1.16 nicm 1970: tty_write(tty_cmd_cell, &ttyctx);
1.193 nicm 1971: }
1.35 nicm 1972: }
1973:
1.222 nicm 1974: /* Combine a UTF-8 zero-width character onto the previous if necessary. */
1975: static int
1976: screen_write_combine(struct screen_write_ctx *ctx, const struct grid_cell *gc)
1.35 nicm 1977: {
1978: struct screen *s = ctx->s;
1979: struct grid *gd = s->grid;
1.222 nicm 1980: const struct utf8_data *ud = &gc->data;
1981: u_int n, cx = s->cx, cy = s->cy;
1982: struct grid_cell last;
1983: struct tty_ctx ttyctx;
1984: int force_wide = 0, zero_width = 0;
1.35 nicm 1985:
1.222 nicm 1986: /*
1987: * Is this character which makes no sense without being combined? If
1988: * this is true then flag it here and discard the character (return 1)
1989: * if we cannot combine it.
1990: */
1991: if (utf8_is_zwj(ud))
1992: zero_width = 1;
1993: else if (utf8_is_vs(ud))
1994: zero_width = force_wide = 1;
1995: else if (ud->width == 0)
1996: zero_width = 1;
1997:
1998: /* Cannot combine empty character or at left. */
1999: if (ud->size < 2 || cx == 0)
2000: return (zero_width);
2001: log_debug("%s: character %.*s at %u,%u (width %u)", __func__,
2002: (int)ud->size, ud->data, cx, cy, ud->width);
2003:
2004: /* Find the cell to combine with. */
2005: n = 1;
2006: grid_view_get_cell(gd, cx - n, cy, &last);
2007: if (cx != 1 && (last.flags & GRID_FLAG_PADDING)) {
2008: n = 2;
2009: grid_view_get_cell(gd, cx - n, cy, &last);
1.213 nicm 2010: }
1.222 nicm 2011: if (n != last.data.width || (last.flags & GRID_FLAG_PADDING))
2012: return (zero_width);
1.35 nicm 2013:
1.222 nicm 2014: /*
2015: * Check if we need to combine characters. This could be zero width
1.223 nicm 2016: * (set above), a modifier character (with an existing Unicode
1.222 nicm 2017: * character) or a previous ZWJ.
2018: */
2019: if (!zero_width) {
2020: if (utf8_is_modifier(ud)) {
2021: if (last.data.size < 2)
2022: return (0);
2023: force_wide = 1;
2024: } else if (!utf8_has_zwj(&last.data))
2025: return (0);
1.104 nicm 2026: }
1.224 ! nicm 2027:
! 2028: /* Check if this combined character would be too long. */
! 2029: if (last.data.size + ud->size > sizeof last.data.data)
! 2030: return (0);
1.35 nicm 2031:
1.222 nicm 2032: /* Combining; flush any pending output. */
2033: screen_write_collect_flush(ctx, 0, __func__);
1.104 nicm 2034:
1.222 nicm 2035: log_debug("%s: %.*s -> %.*s at %u,%u (offset %u, width %u)", __func__,
2036: (int)ud->size, ud->data, (int)last.data.size, last.data.data,
2037: cx - n, cy, n, last.data.width);
1.35 nicm 2038:
1.77 nicm 2039: /* Append the data. */
1.222 nicm 2040: memcpy(last.data.data + last.data.size, ud->data, ud->size);
2041: last.data.size += ud->size;
2042:
2043: /* Force the width to 2 for modifiers and variation selector. */
2044: if (last.data.width == 1 && force_wide) {
2045: last.data.width = 2;
2046: n = 2;
2047: cx++;
2048: } else
2049: force_wide = 0;
1.77 nicm 2050:
2051: /* Set the new cell. */
1.222 nicm 2052: grid_view_set_cell(gd, cx - n, cy, &last);
2053: if (force_wide)
2054: grid_view_set_padding(gd, cx, cy);
1.35 nicm 2055:
1.222 nicm 2056: /*
2057: * Redraw the combined cell. If forcing the cell to width 2, reset the
2058: * cached cursor position in the tty, since we don't really know
2059: * whether the terminal thought the character was width 1 or width 2
2060: * and what it is going to do now.
2061: */
2062: screen_write_set_cursor(ctx, cx - n, cy);
2063: screen_write_initctx(ctx, &ttyctx, 0);
2064: ttyctx.cell = &last;
2065: ttyctx.num = force_wide; /* reset cached cursor position */
2066: tty_write(tty_cmd_cell, &ttyctx);
2067: screen_write_set_cursor(ctx, cx, cy);
2068:
2069: return (1);
1.1 nicm 2070: }
2071:
2072: /*
2073: * UTF-8 wide characters are a bit of an annoyance. They take up more than one
2074: * cell on the screen, so following cells must not be drawn by marking them as
2075: * padding.
2076: *
2077: * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
2078: * character, it is necessary to also overwrite any other cells which covered
2079: * by the same character.
2080: */
1.89 nicm 2081: static int
2082: screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc,
2083: u_int width)
1.1 nicm 2084: {
2085: struct screen *s = ctx->s;
2086: struct grid *gd = s->grid;
1.89 nicm 2087: struct grid_cell tmp_gc;
1.1 nicm 2088: u_int xx;
1.89 nicm 2089: int done = 0;
1.1 nicm 2090:
1.89 nicm 2091: if (gc->flags & GRID_FLAG_PADDING) {
1.1 nicm 2092: /*
2093: * A padding cell, so clear any following and leading padding
2094: * cells back to the character. Don't overwrite the current
2095: * cell as that happens later anyway.
2096: */
2097: xx = s->cx + 1;
2098: while (--xx > 0) {
1.89 nicm 2099: grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2100: if (~tmp_gc.flags & GRID_FLAG_PADDING)
1.1 nicm 2101: break;
1.131 nicm 2102: log_debug("%s: padding at %u,%u", __func__, xx, s->cy);
1.1 nicm 2103: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2104: }
2105:
2106: /* Overwrite the character at the start of this padding. */
1.131 nicm 2107: log_debug("%s: character at %u,%u", __func__, xx, s->cy);
1.1 nicm 2108: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1.89 nicm 2109: done = 1;
1.43 nicm 2110: }
1.1 nicm 2111:
1.43 nicm 2112: /*
1.95 nicm 2113: * Overwrite any padding cells that belong to any UTF-8 characters
2114: * we'll be overwriting with the current character.
1.43 nicm 2115: */
1.95 nicm 2116: if (width != 1 ||
2117: gc->data.width != 1 ||
2118: gc->flags & GRID_FLAG_PADDING) {
1.89 nicm 2119: xx = s->cx + width - 1;
2120: while (++xx < screen_size_x(s)) {
2121: grid_view_get_cell(gd, xx, s->cy, &tmp_gc);
2122: if (~tmp_gc.flags & GRID_FLAG_PADDING)
2123: break;
1.160 nicm 2124: log_debug("%s: overwrite at %u,%u", __func__, xx,
2125: s->cy);
1.89 nicm 2126: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
2127: done = 1;
2128: }
1.1 nicm 2129: }
1.89 nicm 2130:
2131: return (done);
1.50 nicm 2132: }
2133:
1.107 nicm 2134: /* Set external clipboard. */
1.50 nicm 2135: void
1.208 nicm 2136: screen_write_setselection(struct screen_write_ctx *ctx, const char *flags,
2137: u_char *str, u_int len)
1.50 nicm 2138: {
2139: struct tty_ctx ttyctx;
2140:
1.163 nicm 2141: screen_write_initctx(ctx, &ttyctx, 0);
1.50 nicm 2142: ttyctx.ptr = str;
1.208 nicm 2143: ttyctx.ptr2 = (void *)flags;
1.50 nicm 2144: ttyctx.num = len;
2145:
2146: tty_write(tty_cmd_setselection, &ttyctx);
1.47 nicm 2147: }
2148:
1.107 nicm 2149: /* Write unmodified string. */
1.47 nicm 2150: void
1.209 nicm 2151: screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len,
2152: int allow_invisible_panes)
1.47 nicm 2153: {
1.87 nicm 2154: struct tty_ctx ttyctx;
1.47 nicm 2155:
1.163 nicm 2156: screen_write_initctx(ctx, &ttyctx, 0);
1.47 nicm 2157: ttyctx.ptr = str;
2158: ttyctx.num = len;
1.209 nicm 2159: ttyctx.allow_invisible_panes = allow_invisible_panes;
1.47 nicm 2160:
2161: tty_write(tty_cmd_rawstring, &ttyctx);
1.178 nicm 2162: }
2163:
2164: /* Turn alternate screen on. */
2165: void
2166: screen_write_alternateon(struct screen_write_ctx *ctx, struct grid_cell *gc,
2167: int cursor)
2168: {
2169: struct tty_ctx ttyctx;
2170: struct window_pane *wp = ctx->wp;
2171:
2172: if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2173: return;
1.192 nicm 2174:
2175: screen_write_collect_flush(ctx, 0, __func__);
1.178 nicm 2176: screen_alternate_on(ctx->s, gc, cursor);
2177:
1.211 nicm 2178: screen_write_initctx(ctx, &ttyctx, 1);
2179: if (ttyctx.redraw_cb != NULL)
1.210 nicm 2180: ttyctx.redraw_cb(&ttyctx);
1.178 nicm 2181: }
2182:
2183: /* Turn alternate screen off. */
2184: void
2185: screen_write_alternateoff(struct screen_write_ctx *ctx, struct grid_cell *gc,
2186: int cursor)
2187: {
2188: struct tty_ctx ttyctx;
2189: struct window_pane *wp = ctx->wp;
2190:
2191: if (wp != NULL && !options_get_number(wp->options, "alternate-screen"))
2192: return;
1.192 nicm 2193:
2194: screen_write_collect_flush(ctx, 0, __func__);
1.178 nicm 2195: screen_alternate_off(ctx->s, gc, cursor);
2196:
1.211 nicm 2197: screen_write_initctx(ctx, &ttyctx, 1);
2198: if (ttyctx.redraw_cb != NULL)
1.210 nicm 2199: ttyctx.redraw_cb(&ttyctx);
1.1 nicm 2200: }