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