Annotation of src/usr.bin/tmux/screen-write.c, Revision 1.43
1.43 ! nicm 1: /* $OpenBSD: screen-write.c,v 1.42 2010/04/06 21:35:44 nicm Exp $ */
1.1 nicm 2:
3: /*
4: * Copyright (c) 2007 Nicholas Marriott <nicm@users.sourceforge.net>
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:
21: #include <string.h>
22:
23: #include "tmux.h"
24:
1.31 nicm 25: void screen_write_initctx(struct screen_write_ctx *, struct tty_ctx *, int);
1.43 ! nicm 26: void screen_write_overwrite(struct screen_write_ctx *, u_int);
1.36 nicm 27: int screen_write_combine(
28: struct screen_write_ctx *, const struct utf8_data *);
1.1 nicm 29:
30: /* Initialise writing with a window. */
31: void
32: screen_write_start(
33: struct screen_write_ctx *ctx, struct window_pane *wp, struct screen *s)
34: {
35: ctx->wp = wp;
36: if (wp != NULL && s == NULL)
37: ctx->s = wp->screen;
38: else
39: ctx->s = s;
40: }
41:
42: /* Finish writing. */
1.40 nicm 43: /* ARGSUSED */
1.1 nicm 44: void
45: screen_write_stop(unused struct screen_write_ctx *ctx)
46: {
47: }
48:
49: /* Write character. */
50: void
51: screen_write_putc(
52: struct screen_write_ctx *ctx, struct grid_cell *gc, u_char ch)
53: {
54: gc->data = ch;
55: screen_write_cell(ctx, gc, NULL);
56: }
57:
1.24 nicm 58: /* Calculate string length, with embedded formatting. */
59: size_t printflike2
60: screen_write_cstrlen(int utf8flag, const char *fmt, ...)
61: {
62: va_list ap;
63: char *msg, *msg2, *ptr, *ptr2;
64: size_t size;
65:
66: va_start(ap, fmt);
67: xvasprintf(&msg, fmt, ap);
68: va_end(ap);
69: msg2 = xmalloc(strlen(msg) + 1);
70:
71: ptr = msg;
72: ptr2 = msg2;
73: while (*ptr != '\0') {
74: if (ptr[0] == '#' && ptr[1] == '[') {
75: while (*ptr != ']' && *ptr != '\0')
76: ptr++;
77: if (*ptr == ']')
78: ptr++;
79: continue;
80: }
81: *ptr2++ = *ptr++;
82: }
83: *ptr2 = '\0';
84:
85: size = screen_write_strlen(utf8flag, "%s", msg2);
86:
87: xfree(msg);
88: xfree(msg2);
89:
90: return (size);
91: }
92:
1.2 nicm 93: /* Calculate string length. */
1.3 nicm 94: size_t printflike2
95: screen_write_strlen(int utf8flag, const char *fmt, ...)
1.2 nicm 96: {
1.36 nicm 97: va_list ap;
98: char *msg;
99: struct utf8_data utf8data;
100: u_char *ptr;
101: size_t left, size = 0;
1.2 nicm 102:
103: va_start(ap, fmt);
104: xvasprintf(&msg, fmt, ap);
105: va_end(ap);
106:
107: ptr = msg;
108: while (*ptr != '\0') {
1.36 nicm 109: if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
110: ptr++;
1.2 nicm 111:
112: left = strlen(ptr);
1.36 nicm 113: if (left < utf8data.size - 1)
114: break;
115: while (utf8_append(&utf8data, *ptr))
1.2 nicm 116: ptr++;
1.36 nicm 117: ptr++;
118:
119: size += utf8data.width;
1.2 nicm 120: } else {
121: size++;
122: ptr++;
123: }
1.7 ray 124: }
1.2 nicm 125:
1.19 nicm 126: xfree(msg);
1.2 nicm 127: return (size);
128: }
129:
1.3 nicm 130: /* Write simple string (no UTF-8 or maximum length). */
1.1 nicm 131: void printflike3
132: screen_write_puts(
133: struct screen_write_ctx *ctx, struct grid_cell *gc, const char *fmt, ...)
134: {
135: va_list ap;
136:
137: va_start(ap, fmt);
1.3 nicm 138: screen_write_vnputs(ctx, -1, gc, 0, fmt, ap);
1.2 nicm 139: va_end(ap);
140: }
141:
142: /* Write string with length limit (-1 for unlimited). */
1.3 nicm 143: void printflike5
1.2 nicm 144: screen_write_nputs(struct screen_write_ctx *ctx,
1.3 nicm 145: ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
1.2 nicm 146: {
147: va_list ap;
148:
149: va_start(ap, fmt);
1.3 nicm 150: screen_write_vnputs(ctx, maxlen, gc, utf8flag, fmt, ap);
1.2 nicm 151: va_end(ap);
152: }
153:
154: void
1.3 nicm 155: screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen,
156: struct grid_cell *gc, int utf8flag, const char *fmt, va_list ap)
1.2 nicm 157: {
1.36 nicm 158: char *msg;
159: struct utf8_data utf8data;
160: u_char *ptr;
161: size_t left, size = 0;
1.2 nicm 162:
1.1 nicm 163: xvasprintf(&msg, fmt, ap);
164:
1.2 nicm 165: ptr = msg;
166: while (*ptr != '\0') {
1.36 nicm 167: if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
168: ptr++;
1.2 nicm 169:
170: left = strlen(ptr);
1.36 nicm 171: if (left < utf8data.size - 1)
172: break;
173: while (utf8_append(&utf8data, *ptr))
1.2 nicm 174: ptr++;
1.36 nicm 175: ptr++;
1.7 ray 176:
1.36 nicm 177: if (maxlen > 0 &&
178: size + utf8data.width > (size_t) maxlen) {
1.2 nicm 179: while (size < (size_t) maxlen) {
180: screen_write_putc(ctx, gc, ' ');
181: size++;
182: }
183: break;
184: }
1.36 nicm 185: size += utf8data.width;
1.41 nicm 186:
1.2 nicm 187: gc->flags |= GRID_FLAG_UTF8;
1.36 nicm 188: screen_write_cell(ctx, gc, &utf8data);
1.2 nicm 189: gc->flags &= ~GRID_FLAG_UTF8;
190: } else {
1.8 nicm 191: if (maxlen > 0 && size + 1 > (size_t) maxlen)
1.2 nicm 192: break;
193:
194: size++;
195: screen_write_putc(ctx, gc, *ptr);
196: ptr++;
197: }
198: }
1.1 nicm 199:
200: xfree(msg);
1.24 nicm 201: }
202:
203: /* Write string, similar to nputs, but with embedded formatting (#[]). */
204: void printflike5
205: screen_write_cnputs(struct screen_write_ctx *ctx,
206: ssize_t maxlen, struct grid_cell *gc, int utf8flag, const char *fmt, ...)
207: {
208: struct grid_cell lgc;
1.36 nicm 209: struct utf8_data utf8data;
1.24 nicm 210: va_list ap;
211: char *msg;
1.36 nicm 212: u_char *ptr, *last;
1.24 nicm 213: size_t left, size = 0;
214:
215: va_start(ap, fmt);
216: xvasprintf(&msg, fmt, ap);
217: va_end(ap);
218:
219: memcpy(&lgc, gc, sizeof lgc);
220:
221: ptr = msg;
222: while (*ptr != '\0') {
223: if (ptr[0] == '#' && ptr[1] == '[') {
224: ptr += 2;
225: last = ptr + strcspn(ptr, "]");
226: if (*last == '\0') {
227: /* No ]. Not much point in doing anything. */
228: break;
229: }
230: *last = '\0';
231:
232: screen_write_parsestyle(gc, &lgc, ptr);
233: ptr = last + 1;
234: continue;
235: }
236:
1.36 nicm 237: if (utf8flag && *ptr > 0x7f && utf8_open(&utf8data, *ptr)) {
238: ptr++;
1.24 nicm 239:
240: left = strlen(ptr);
1.36 nicm 241: if (left < utf8data.size - 1)
242: break;
243: while (utf8_append(&utf8data, *ptr))
1.24 nicm 244: ptr++;
1.36 nicm 245: ptr++;
1.24 nicm 246:
1.36 nicm 247: if (maxlen > 0 &&
248: size + utf8data.width > (size_t) maxlen) {
1.24 nicm 249: while (size < (size_t) maxlen) {
250: screen_write_putc(ctx, gc, ' ');
251: size++;
252: }
253: break;
254: }
1.36 nicm 255: size += utf8data.width;
1.24 nicm 256:
257: lgc.flags |= GRID_FLAG_UTF8;
1.36 nicm 258: screen_write_cell(ctx, &lgc, &utf8data);
1.24 nicm 259: lgc.flags &= ~GRID_FLAG_UTF8;
260: } else {
261: if (maxlen > 0 && size + 1 > (size_t) maxlen)
262: break;
263:
264: size++;
265: screen_write_putc(ctx, &lgc, *ptr);
266: ptr++;
267: }
268: }
269:
270: xfree(msg);
271: }
272:
273: /* Parse an embedded style of the form "fg=colour,bg=colour,bright,...". */
274: void
275: screen_write_parsestyle(
276: struct grid_cell *defgc, struct grid_cell *gc, const char *in)
277: {
278: const char delimiters[] = " ,";
279: char tmp[32];
280: int val;
281: size_t end;
1.25 nicm 282: u_char fg, bg, attr, flags;
1.24 nicm 283:
284: if (*in == '\0')
285: return;
286: if (strchr(delimiters, in[strlen(in) - 1]) != NULL)
287: return;
288:
289: fg = gc->fg;
290: bg = gc->bg;
1.25 nicm 291: attr = gc->attr;
292: flags = gc->flags;
1.24 nicm 293: do {
294: end = strcspn(in, delimiters);
295: if (end > (sizeof tmp) - 1)
296: return;
297: memcpy(tmp, in, end);
298: tmp[end] = '\0';
299:
300: if (strcasecmp(tmp, "default") == 0) {
301: fg = defgc->fg;
302: bg = defgc->bg;
303: attr = defgc->attr;
304: } else if (end > 3 && strncasecmp(tmp + 1, "g=", 2) == 0) {
305: if ((val = colour_fromstring(tmp + 3)) == -1)
306: return;
307: if (*in == 'f' || *in == 'F') {
1.25 nicm 308: if (val != 8) {
309: if (val & 0x100) {
310: flags |= GRID_FLAG_FG256;
311: val &= ~0x100;
312: } else
313: flags &= ~GRID_FLAG_FG256;
1.24 nicm 314: fg = val;
1.25 nicm 315: } else
1.24 nicm 316: fg = defgc->fg;
317: } else if (*in == 'b' || *in == 'B') {
1.25 nicm 318: if (val != 8) {
319: if (val & 0x100) {
320: flags |= GRID_FLAG_BG256;
321: val &= ~0x100;
322: } else
323: flags &= ~GRID_FLAG_BG256;
1.24 nicm 324: bg = val;
1.25 nicm 325: } else
1.24 nicm 326: bg = defgc->bg;
327: } else
328: return;
1.27 nicm 329: } else if (end > 2 && strncasecmp(tmp, "no", 2) == 0) {
330: if ((val = attributes_fromstring(tmp + 2)) == -1)
331: return;
332: attr &= ~val;
1.24 nicm 333: } else {
334: if ((val = attributes_fromstring(tmp)) == -1)
335: return;
336: attr |= val;
337: }
338:
339: in += end + strspn(in + end, delimiters);
340: } while (*in != '\0');
341: gc->fg = fg;
342: gc->bg = bg;
343: gc->attr = attr;
1.25 nicm 344: gc->flags = flags;
1.1 nicm 345: }
346:
347: /* Copy from another screen. */
348: void
349: screen_write_copy(struct screen_write_ctx *ctx,
350: struct screen *src, u_int px, u_int py, u_int nx, u_int ny)
351: {
352: struct screen *s = ctx->s;
353: struct grid *gd = src->grid;
1.21 nicm 354: struct grid_line *gl;
1.1 nicm 355: const struct grid_cell *gc;
1.36 nicm 356: const struct grid_utf8 *gu;
357: struct utf8_data utf8data;
1.39 nicm 358: u_int xx, yy, cx, cy, ax, bx;
1.1 nicm 359:
360: cx = s->cx;
361: cy = s->cy;
362: for (yy = py; yy < py + ny; yy++) {
1.21 nicm 363: gl = &gd->linedata[yy];
1.26 nicm 364: if (yy < gd->hsize + gd->sy) {
365: /*
366: * Find start and end position and copy between
367: * them. Limit to the real end of the line then use a
368: * clear EOL only if copying to the end, otherwise
369: * could overwrite whatever is there already.
370: */
371: if (px > gl->cellsize)
372: ax = gl->cellsize;
373: else
374: ax = px;
375: if (px + nx == gd->sx && px + nx > gl->cellsize)
376: bx = gl->cellsize;
377: else
378: bx = px + nx;
1.41 nicm 379:
1.26 nicm 380: for (xx = ax; xx < bx; xx++) {
381: if (xx >= gl->cellsize)
382: gc = &grid_default_cell;
1.36 nicm 383: else
1.26 nicm 384: gc = &gl->celldata[xx];
1.39 nicm 385: if (!(gc->flags & GRID_FLAG_UTF8)) {
386: screen_write_cell(ctx, gc, NULL);
387: continue;
1.26 nicm 388: }
1.39 nicm 389: /* Reinject the UTF-8 sequence. */
390: gu = &gl->utf8data[xx];
391: utf8data.size = grid_utf8_copy(
392: gu, utf8data.data, sizeof utf8data.data);
393: utf8data.width = gu->width;
1.36 nicm 394: screen_write_cell(ctx, gc, &utf8data);
1.1 nicm 395: }
1.26 nicm 396: if (px + nx == gd->sx && px + nx > gl->cellsize)
397: screen_write_clearendofline(ctx);
398: } else
1.36 nicm 399: screen_write_clearline(ctx);
1.1 nicm 400: cy++;
401: screen_write_cursormove(ctx, cx, cy);
402: }
403: }
404:
1.17 nicm 405: /* Set up context for TTY command. */
1.1 nicm 406: void
1.31 nicm 407: screen_write_initctx(
408: struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last)
1.1 nicm 409: {
1.31 nicm 410: struct screen *s = ctx->s;
411: struct grid *gd = s->grid;
412: const struct grid_cell *gc;
413: const struct grid_utf8 *gu;
414: u_int xx;
1.1 nicm 415:
1.17 nicm 416: ttyctx->wp = ctx->wp;
1.1 nicm 417:
1.17 nicm 418: ttyctx->ocx = s->cx;
419: ttyctx->ocy = s->cy;
420:
421: ttyctx->orlower = s->rlower;
422: ttyctx->orupper = s->rupper;
1.31 nicm 423:
424: if (!save_last)
425: return;
426:
427: /* Save the last cell on the screen. */
1.38 nicm 428: gc = &grid_default_cell;
429: for (xx = 1; xx <= screen_size_x(s); xx++) {
1.31 nicm 430: gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy);
431: if (!(gc->flags & GRID_FLAG_PADDING))
432: break;
433: }
434: ttyctx->last_width = xx;
435: memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell);
436: if (gc->flags & GRID_FLAG_UTF8) {
437: gu = grid_view_peek_utf8(gd, screen_size_x(s) - xx, s->cy);
438: memcpy(&ttyctx->last_utf8, gu, sizeof ttyctx->last_utf8);
439: }
1.1 nicm 440: }
441:
442: /* Cursor up by ny. */
443: void
444: screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
445: {
446: struct screen *s = ctx->s;
447:
448: if (ny == 0)
449: ny = 1;
450:
1.12 nicm 451: if (s->cy < s->rupper) {
452: /* Above region. */
453: if (ny > s->cy)
454: ny = s->cy;
455: } else {
456: /* Below region. */
457: if (ny > s->cy - s->rupper)
458: ny = s->cy - s->rupper;
459: }
1.1 nicm 460: if (ny == 0)
461: return;
462:
463: s->cy -= ny;
464: }
465:
466: /* Cursor down by ny. */
467: void
468: screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
469: {
470: struct screen *s = ctx->s;
471:
472: if (ny == 0)
473: ny = 1;
474:
1.12 nicm 475: if (s->cy > s->rlower) {
476: /* Below region. */
477: if (ny > screen_size_y(s) - 1 - s->cy)
478: ny = screen_size_y(s) - 1 - s->cy;
479: } else {
480: /* Above region. */
481: if (ny > s->rlower - s->cy)
482: ny = s->rlower - s->cy;
483: }
1.1 nicm 484: if (ny == 0)
485: return;
486:
487: s->cy += ny;
488: }
489:
490: /* Cursor right by nx. */
491: void
492: screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
493: {
494: struct screen *s = ctx->s;
495:
496: if (nx == 0)
497: nx = 1;
498:
499: if (nx > screen_size_x(s) - 1 - s->cx)
500: nx = screen_size_x(s) - 1 - s->cx;
501: if (nx == 0)
502: return;
503:
504: s->cx += nx;
505: }
506:
507: /* Cursor left by nx. */
508: void
509: screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx)
510: {
511: struct screen *s = ctx->s;
512:
513: if (nx == 0)
514: nx = 1;
515:
516: if (nx > s->cx)
517: nx = s->cx;
518: if (nx == 0)
519: return;
520:
521: s->cx -= nx;
1.5 nicm 522: }
523:
1.29 nicm 524: /* Backspace; cursor left unless at start of wrapped line when can move up. */
525: void
526: screen_write_backspace(struct screen_write_ctx *ctx)
527: {
528: struct screen *s = ctx->s;
529: struct grid_line *gl;
530:
531: if (s->cx == 0) {
532: if (s->cy == 0)
533: return;
534: gl = &s->grid->linedata[s->grid->hsize + s->cy - 1];
535: if (gl->flags & GRID_LINE_WRAPPED) {
536: s->cy--;
537: s->cx = screen_size_x(s) - 1;
538: }
539: } else
540: s->cx--;
541: }
542:
1.5 nicm 543: /* VT100 alignment test. */
544: void
545: screen_write_alignmenttest(struct screen_write_ctx *ctx)
546: {
547: struct screen *s = ctx->s;
1.17 nicm 548: struct tty_ctx ttyctx;
1.5 nicm 549: struct grid_cell gc;
550: u_int xx, yy;
551:
1.31 nicm 552: screen_write_initctx(ctx, &ttyctx, 0);
1.17 nicm 553:
1.5 nicm 554: memcpy(&gc, &grid_default_cell, sizeof gc);
555: gc.data = 'E';
1.7 ray 556:
1.5 nicm 557: for (yy = 0; yy < screen_size_y(s); yy++) {
558: for (xx = 0; xx < screen_size_x(s); xx++)
559: grid_view_set_cell(s->grid, xx, yy, &gc);
560: }
1.7 ray 561:
1.5 nicm 562: s->cx = 0;
563: s->cy = 0;
564:
565: s->rupper = 0;
1.29 nicm 566:
1.5 nicm 567: s->rlower = screen_size_y(s) - 1;
568:
1.17 nicm 569: tty_write(tty_cmd_alignmenttest, &ttyctx);
1.1 nicm 570: }
571:
572: /* Insert nx characters. */
573: void
574: screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx)
575: {
576: struct screen *s = ctx->s;
1.17 nicm 577: struct tty_ctx ttyctx;
1.1 nicm 578:
579: if (nx == 0)
580: nx = 1;
581:
1.9 nicm 582: if (nx > screen_size_x(s) - s->cx)
583: nx = screen_size_x(s) - s->cx;
1.1 nicm 584: if (nx == 0)
585: return;
586:
1.31 nicm 587: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 588:
589: if (s->cx <= screen_size_x(s) - 1)
590: grid_view_insert_cells(s->grid, s->cx, s->cy, nx);
591:
1.17 nicm 592: ttyctx.num = nx;
593: tty_write(tty_cmd_insertcharacter, &ttyctx);
1.1 nicm 594: }
595:
596: /* Delete nx characters. */
597: void
598: screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx)
599: {
600: struct screen *s = ctx->s;
1.17 nicm 601: struct tty_ctx ttyctx;
1.1 nicm 602:
603: if (nx == 0)
604: nx = 1;
605:
1.9 nicm 606: if (nx > screen_size_x(s) - s->cx)
607: nx = screen_size_x(s) - s->cx;
1.1 nicm 608: if (nx == 0)
609: return;
610:
1.31 nicm 611: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 612:
613: if (s->cx <= screen_size_x(s) - 1)
614: grid_view_delete_cells(s->grid, s->cx, s->cy, nx);
615:
1.17 nicm 616: ttyctx.num = nx;
617: tty_write(tty_cmd_deletecharacter, &ttyctx);
1.1 nicm 618: }
619:
620: /* Insert ny lines. */
621: void
622: screen_write_insertline(struct screen_write_ctx *ctx, u_int ny)
623: {
624: struct screen *s = ctx->s;
1.17 nicm 625: struct tty_ctx ttyctx;
1.1 nicm 626:
627: if (ny == 0)
628: ny = 1;
629:
1.11 nicm 630: if (s->cy < s->rupper || s->cy > s->rlower) {
631: if (ny > screen_size_y(s) - s->cy)
632: ny = screen_size_y(s) - s->cy;
633: if (ny == 0)
634: return;
635:
1.31 nicm 636: screen_write_initctx(ctx, &ttyctx, 0);
1.11 nicm 637:
638: grid_view_insert_lines(s->grid, s->cy, ny);
639:
1.17 nicm 640: ttyctx.num = ny;
641: tty_write(tty_cmd_insertline, &ttyctx);
1.11 nicm 642: return;
643: }
644:
645: if (ny > s->rlower + 1 - s->cy)
646: ny = s->rlower + 1 - s->cy;
1.1 nicm 647: if (ny == 0)
648: return;
1.41 nicm 649:
1.31 nicm 650: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 651:
652: if (s->cy < s->rupper || s->cy > s->rlower)
653: grid_view_insert_lines(s->grid, s->cy, ny);
1.10 nicm 654: else
655: grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny);
1.1 nicm 656:
1.17 nicm 657: ttyctx.num = ny;
658: tty_write(tty_cmd_insertline, &ttyctx);
1.1 nicm 659: }
660:
661: /* Delete ny lines. */
662: void
663: screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny)
664: {
665: struct screen *s = ctx->s;
1.17 nicm 666: struct tty_ctx ttyctx;
1.1 nicm 667:
668: if (ny == 0)
669: ny = 1;
670:
1.11 nicm 671: if (s->cy < s->rupper || s->cy > s->rlower) {
672: if (ny > screen_size_y(s) - s->cy)
673: ny = screen_size_y(s) - s->cy;
674: if (ny == 0)
675: return;
676:
1.31 nicm 677: screen_write_initctx(ctx, &ttyctx, 0);
1.11 nicm 678:
679: grid_view_delete_lines(s->grid, s->cy, ny);
680:
1.17 nicm 681: ttyctx.num = ny;
682: tty_write(tty_cmd_deleteline, &ttyctx);
1.11 nicm 683: return;
684: }
1.41 nicm 685:
1.11 nicm 686: if (ny > s->rlower + 1 - s->cy)
687: ny = s->rlower + 1 - s->cy;
1.1 nicm 688: if (ny == 0)
689: return;
690:
1.31 nicm 691: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 692:
693: if (s->cy < s->rupper || s->cy > s->rlower)
694: grid_view_delete_lines(s->grid, s->cy, ny);
1.10 nicm 695: else
696: grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny);
1.1 nicm 697:
1.17 nicm 698: ttyctx.num = ny;
699: tty_write(tty_cmd_deleteline, &ttyctx);
1.1 nicm 700: }
701:
702: /* Clear line at cursor. */
703: void
704: screen_write_clearline(struct screen_write_ctx *ctx)
705: {
706: struct screen *s = ctx->s;
1.17 nicm 707: struct tty_ctx ttyctx;
1.1 nicm 708:
1.31 nicm 709: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 710:
711: grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1);
712:
1.17 nicm 713: tty_write(tty_cmd_clearline, &ttyctx);
1.1 nicm 714: }
715:
716: /* Clear to end of line from cursor. */
717: void
718: screen_write_clearendofline(struct screen_write_ctx *ctx)
719: {
720: struct screen *s = ctx->s;
1.17 nicm 721: struct tty_ctx ttyctx;
1.1 nicm 722: u_int sx;
723:
1.31 nicm 724: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 725:
726: sx = screen_size_x(s);
727:
728: if (s->cx <= sx - 1)
729: grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
730:
1.41 nicm 731: tty_write(tty_cmd_clearendofline, &ttyctx);
1.1 nicm 732: }
733:
734: /* Clear to start of line from cursor. */
735: void
736: screen_write_clearstartofline(struct screen_write_ctx *ctx)
737: {
738: struct screen *s = ctx->s;
1.17 nicm 739: struct tty_ctx ttyctx;
1.1 nicm 740: u_int sx;
741:
1.31 nicm 742: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 743:
744: sx = screen_size_x(s);
745:
746: if (s->cx > sx - 1)
747: grid_view_clear(s->grid, 0, s->cy, sx, 1);
748: else
749: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
750:
1.17 nicm 751: tty_write(tty_cmd_clearstartofline, &ttyctx);
1.1 nicm 752: }
753:
754: /* Move cursor to px,py. */
755: void
756: screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
757: {
758: struct screen *s = ctx->s;
759:
760: if (px > screen_size_x(s) - 1)
761: px = screen_size_x(s) - 1;
762: if (py > screen_size_y(s) - 1)
763: py = screen_size_y(s) - 1;
764:
765: s->cx = px;
766: s->cy = py;
767: }
768:
769: /* Set cursor mode. */
770: void
771: screen_write_cursormode(struct screen_write_ctx *ctx, int state)
772: {
773: struct screen *s = ctx->s;
774:
775: if (state)
776: s->mode |= MODE_CURSOR;
777: else
778: s->mode &= ~MODE_CURSOR;
779: }
780:
781: /* Reverse index (up with scroll). */
782: void
783: screen_write_reverseindex(struct screen_write_ctx *ctx)
784: {
785: struct screen *s = ctx->s;
1.17 nicm 786: struct tty_ctx ttyctx;
1.1 nicm 787:
1.31 nicm 788: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 789:
790: if (s->cy == s->rupper)
791: grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
792: else if (s->cy > 0)
793: s->cy--;
794:
1.17 nicm 795: tty_write(tty_cmd_reverseindex, &ttyctx);
1.1 nicm 796: }
797:
798: /* Set scroll region. */
799: void
800: screen_write_scrollregion(
801: struct screen_write_ctx *ctx, u_int rupper, u_int rlower)
802: {
803: struct screen *s = ctx->s;
804:
805: if (rupper > screen_size_y(s) - 1)
806: rupper = screen_size_y(s) - 1;
807: if (rlower > screen_size_y(s) - 1)
808: rlower = screen_size_y(s) - 1;
1.13 nicm 809: if (rupper >= rlower) /* cannot be one line */
1.1 nicm 810: return;
811:
812: /* Cursor moves to top-left. */
813: s->cx = 0;
814: s->cy = 0;
815:
816: s->rupper = rupper;
817: s->rlower = rlower;
818: }
819:
820: /* Set insert mode. */
821: void
822: screen_write_insertmode(struct screen_write_ctx *ctx, int state)
823: {
824: struct screen *s = ctx->s;
825:
826: if (state)
827: s->mode |= MODE_INSERT;
828: else
829: s->mode &= ~MODE_INSERT;
830: }
831:
832: /* Set mouse mode. */
833: void
834: screen_write_mousemode(struct screen_write_ctx *ctx, int state)
835: {
836: struct screen *s = ctx->s;
837:
838: if (state)
839: s->mode |= MODE_MOUSE;
840: else
841: s->mode &= ~MODE_MOUSE;
842: }
843:
1.34 nicm 844: /* Line feed. */
1.1 nicm 845: void
1.34 nicm 846: screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
1.1 nicm 847: {
1.20 nicm 848: struct screen *s = ctx->s;
849: struct grid_line *gl;
1.34 nicm 850: struct tty_ctx ttyctx;
851:
852: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 853:
1.20 nicm 854: gl = &s->grid->linedata[s->grid->hsize + s->cy];
855: if (wrapped)
856: gl->flags |= GRID_LINE_WRAPPED;
857: else
858: gl->flags &= ~GRID_LINE_WRAPPED;
859:
1.1 nicm 860: if (s->cy == s->rlower)
861: grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
862: else if (s->cy < screen_size_y(s) - 1)
863: s->cy++;
864:
1.34 nicm 865: ttyctx.num = wrapped;
1.41 nicm 866: tty_write(tty_cmd_linefeed, &ttyctx);
1.1 nicm 867: }
868:
869: /* Carriage return (cursor to start of line). */
870: void
871: screen_write_carriagereturn(struct screen_write_ctx *ctx)
872: {
873: struct screen *s = ctx->s;
874:
875: s->cx = 0;
876: }
877:
878: /* Set keypad cursor keys mode. */
879: void
880: screen_write_kcursormode(struct screen_write_ctx *ctx, int state)
881: {
882: struct screen *s = ctx->s;
883:
884: if (state)
885: s->mode |= MODE_KCURSOR;
886: else
887: s->mode &= ~MODE_KCURSOR;
888: }
889:
890: /* Set keypad number keys mode. */
891: void
892: screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state)
893: {
894: struct screen *s = ctx->s;
895:
896: if (state)
897: s->mode |= MODE_KKEYPAD;
898: else
899: s->mode &= ~MODE_KKEYPAD;
900: }
901:
902: /* Clear to end of screen from cursor. */
903: void
904: screen_write_clearendofscreen(struct screen_write_ctx *ctx)
905: {
906: struct screen *s = ctx->s;
1.17 nicm 907: struct tty_ctx ttyctx;
1.1 nicm 908: u_int sx, sy;
909:
1.31 nicm 910: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 911:
912: sx = screen_size_x(s);
913: sy = screen_size_y(s);
914:
915: if (s->cx <= sx - 1)
916: grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
917: grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1));
918:
1.17 nicm 919: tty_write(tty_cmd_clearendofscreen, &ttyctx);
1.1 nicm 920: }
921:
922: /* Clear to start of screen. */
923: void
924: screen_write_clearstartofscreen(struct screen_write_ctx *ctx)
925: {
926: struct screen *s = ctx->s;
1.17 nicm 927: struct tty_ctx ttyctx;
1.1 nicm 928: u_int sx;
929:
1.31 nicm 930: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 931:
932: sx = screen_size_x(s);
933:
934: if (s->cy > 0)
1.4 nicm 935: grid_view_clear(s->grid, 0, 0, sx, s->cy);
1.1 nicm 936: if (s->cx > sx - 1)
937: grid_view_clear(s->grid, 0, s->cy, sx, 1);
938: else
1.4 nicm 939: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
1.1 nicm 940:
1.17 nicm 941: tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1.1 nicm 942: }
943:
944: /* Clear entire screen. */
945: void
946: screen_write_clearscreen(struct screen_write_ctx *ctx)
947: {
948: struct screen *s = ctx->s;
1.17 nicm 949: struct tty_ctx ttyctx;
1.1 nicm 950:
1.31 nicm 951: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 952:
953: grid_view_clear(s->grid, 0, 0, screen_size_x(s), screen_size_y(s));
954:
1.17 nicm 955: tty_write(tty_cmd_clearscreen, &ttyctx);
1.1 nicm 956: }
957:
958: /* Write cell data. */
959: void
1.36 nicm 960: screen_write_cell(struct screen_write_ctx *ctx,
961: const struct grid_cell *gc, const struct utf8_data *utf8data)
1.1 nicm 962: {
963: struct screen *s = ctx->s;
964: struct grid *gd = s->grid;
1.15 nicm 965: struct tty_ctx ttyctx;
1.35 nicm 966: struct grid_utf8 gu;
967: u_int width, xx;
1.33 nicm 968: struct grid_cell tmp_gc, *tmp_gcp;
1.6 nicm 969: int insert = 0;
1.1 nicm 970:
971: /* Ignore padding. */
972: if (gc->flags & GRID_FLAG_PADDING)
973: return;
974:
975: /* Find character width. */
1.36 nicm 976: if (gc->flags & GRID_FLAG_UTF8)
977: width = utf8data->width;
978: else
1.1 nicm 979: width = 1;
980:
1.32 nicm 981: /*
982: * If this is a wide character and there is no room on the screen, for
983: * the entire character, don't print it.
984: */
985: if (width > 1 && (width > screen_size_x(s) ||
986: (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))
987: return;
988:
1.35 nicm 989: /*
990: * If the width is zero, combine onto the previous character, if
1.41 nicm 991: * there is space.
1.35 nicm 992: */
1.1 nicm 993: if (width == 0) {
1.36 nicm 994: if (screen_write_combine(ctx, utf8data) == 0) {
1.35 nicm 995: screen_write_initctx(ctx, &ttyctx, 0);
996: tty_write(tty_cmd_utf8character, &ttyctx);
1.1 nicm 997: }
998: return;
999: }
1000:
1.31 nicm 1001: /* Initialise the redraw context, saving the last cell. */
1002: screen_write_initctx(ctx, &ttyctx, 1);
1003:
1.6 nicm 1004: /* If in insert mode, make space for the cells. */
1005: if (s->mode & MODE_INSERT && s->cx <= screen_size_x(s) - width) {
1006: xx = screen_size_x(s) - s->cx - width;
1007: grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx);
1008: insert = 1;
1009: }
1010:
1.20 nicm 1011: /* Check this will fit on the current line and wrap if not. */
1.42 nicm 1012: if ((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - width) {
1.34 nicm 1013: screen_write_linefeed(ctx, 1);
1.30 nicm 1014: s->cx = 0; /* carriage return */
1.1 nicm 1015: }
1016:
1017: /* Sanity checks. */
1.42 nicm 1018: if (((s->mode & MODE_WRAP) && s->cx > screen_size_x(s) - 1)
1019: || s->cy > screen_size_y(s) - 1)
1.1 nicm 1020: return;
1021:
1022: /* Handle overwriting of UTF-8 characters. */
1.43 ! nicm 1023: screen_write_overwrite(ctx, width);
1.1 nicm 1024:
1025: /*
1026: * If the new character is UTF-8 wide, fill in padding cells. Have
1027: * already ensured there is enough room.
1028: */
1029: for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1.18 nicm 1030: tmp_gcp = grid_view_get_cell(gd, xx, s->cy);
1031: if (tmp_gcp != NULL)
1032: tmp_gcp->flags |= GRID_FLAG_PADDING;
1.1 nicm 1033: }
1034:
1035: /* Set the cell. */
1036: grid_view_set_cell(gd, s->cx, s->cy, gc);
1.36 nicm 1037: if (gc->flags & GRID_FLAG_UTF8) {
1038: /* Construct UTF-8 and write it. */
1.39 nicm 1039: grid_utf8_set(&gu, utf8data);
1.1 nicm 1040: grid_view_set_utf8(gd, s->cx, s->cy, &gu);
1.36 nicm 1041: }
1.1 nicm 1042:
1043: /* Move the cursor. */
1044: s->cx += width;
1045:
1046: /* Draw to the screen if necessary. */
1.17 nicm 1047: if (insert) {
1048: ttyctx.num = width;
1049: tty_write(tty_cmd_insertcharacter, &ttyctx);
1050: }
1.15 nicm 1051: ttyctx.utf8 = &gu;
1.1 nicm 1052: if (screen_check_selection(s, s->cx - width, s->cy)) {
1.33 nicm 1053: memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc);
1054: tmp_gc.data = gc->data;
1055: tmp_gc.flags = gc->flags &
1.28 nicm 1056: ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
1.33 nicm 1057: tmp_gc.flags |= s->sel.cell.flags &
1.28 nicm 1058: (GRID_FLAG_FG256|GRID_FLAG_BG256);
1.33 nicm 1059: ttyctx.cell = &tmp_gc;
1.16 nicm 1060: tty_write(tty_cmd_cell, &ttyctx);
1.15 nicm 1061: } else {
1062: ttyctx.cell = gc;
1.16 nicm 1063: tty_write(tty_cmd_cell, &ttyctx);
1.15 nicm 1064: }
1.35 nicm 1065: }
1066:
1067: /* Combine a UTF-8 zero-width character onto the previous. */
1068: int
1.36 nicm 1069: screen_write_combine(
1070: struct screen_write_ctx *ctx, const struct utf8_data *utf8data)
1.35 nicm 1071: {
1072: struct screen *s = ctx->s;
1073: struct grid *gd = s->grid;
1074: struct grid_cell *gc;
1075: struct grid_utf8 *gu, tmp_gu;
1.39 nicm 1076: u_int i;
1.35 nicm 1077:
1078: /* Can't combine if at 0. */
1079: if (s->cx == 0)
1080: return (-1);
1081:
1.37 nicm 1082: /* Empty utf8data is out. */
1083: if (utf8data->size == 0)
1084: fatalx("UTF-8 data empty");
1085:
1.35 nicm 1086: /* Retrieve the previous cell and convert to UTF-8 if not already. */
1087: gc = grid_view_get_cell(gd, s->cx - 1, s->cy);
1088: if (!(gc->flags & GRID_FLAG_UTF8)) {
1.39 nicm 1089: tmp_gu.data[0] = gc->data;
1090: tmp_gu.data[1] = 0xff;
1.35 nicm 1091: tmp_gu.width = 1;
1092:
1093: grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu);
1094: gc->flags |= GRID_FLAG_UTF8;
1095: }
1096:
1.39 nicm 1097: /* Append the current cell. */
1.35 nicm 1098: gu = grid_view_get_utf8(gd, s->cx - 1, s->cy);
1.39 nicm 1099: if (grid_utf8_append(gu, utf8data) != 0) {
1100: /* Failed: scrap this character and replace with underscores. */
1101: if (gu->width == 1) {
1102: gc->data = '_';
1103: gc->flags &= ~GRID_FLAG_UTF8;
1104: } else {
1105: for (i = 0; i < gu->width && i != sizeof gu->data; i++)
1106: gu->data[i] = '_';
1107: if (i != sizeof gu->data)
1108: gu->data[i] = 0xff;
1109: gu->width = i;
1110: }
1.35 nicm 1111: }
1112:
1113: return (0);
1.1 nicm 1114: }
1115:
1116: /*
1117: * UTF-8 wide characters are a bit of an annoyance. They take up more than one
1118: * cell on the screen, so following cells must not be drawn by marking them as
1119: * padding.
1120: *
1121: * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
1122: * character, it is necessary to also overwrite any other cells which covered
1123: * by the same character.
1124: */
1125: void
1.43 ! nicm 1126: screen_write_overwrite(struct screen_write_ctx *ctx, u_int width)
1.1 nicm 1127: {
1128: struct screen *s = ctx->s;
1129: struct grid *gd = s->grid;
1130: const struct grid_cell *gc;
1131: u_int xx;
1132:
1133: gc = grid_view_peek_cell(gd, s->cx, s->cy);
1134: if (gc->flags & GRID_FLAG_PADDING) {
1135: /*
1136: * A padding cell, so clear any following and leading padding
1137: * cells back to the character. Don't overwrite the current
1138: * cell as that happens later anyway.
1139: */
1140: xx = s->cx + 1;
1141: while (--xx > 0) {
1142: gc = grid_view_peek_cell(gd, xx, s->cy);
1143: if (!(gc->flags & GRID_FLAG_PADDING))
1144: break;
1145: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1146: }
1147:
1148: /* Overwrite the character at the start of this padding. */
1149: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1.43 ! nicm 1150: }
1.1 nicm 1151:
1.43 ! nicm 1152: /*
! 1153: * Overwrite any padding cells that belong to a UTF-8 character
! 1154: * we'll be overwriting with the current character.
! 1155: */
! 1156: xx = s->cx + width - 1;
! 1157: while (++xx < screen_size_x(s)) {
! 1158: gc = grid_view_peek_cell(gd, xx, s->cy);
! 1159: if (!(gc->flags & GRID_FLAG_PADDING))
! 1160: break;
! 1161: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1.1 nicm 1162: }
1163: }