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