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