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