Annotation of src/usr.bin/tmux/screen-write.c, Revision 1.36
1.36 ! nicm 1: /* $OpenBSD: screen-write.c,v 1.35 2009/10/20 17:33:33 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;
! 357: u_int xx, yy, cx, cy, ax, bx, i;
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.36 ! nicm 384: if (gc->flags & GRID_FLAG_UTF8) {
! 385: gu = &gl->utf8data[xx];
! 386: memcpy(utf8data.data,
! 387: gu->data, sizeof utf8data.data);
! 388: utf8data.width = gu->width;
! 389: utf8data.size = 0;
! 390: for (i = 0; i < UTF8_SIZE; i++) {
! 391: if (gu->data[i] == 0xff)
! 392: break;
! 393: utf8data.size++;
! 394: }
1.26 nicm 395: }
1.36 ! nicm 396: screen_write_cell(ctx, gc, &utf8data);
1.1 nicm 397: }
1.26 nicm 398: if (px + nx == gd->sx && px + nx > gl->cellsize)
399: screen_write_clearendofline(ctx);
400: } else
1.36 ! nicm 401: screen_write_clearline(ctx);
1.1 nicm 402: cy++;
403: screen_write_cursormove(ctx, cx, cy);
404: }
405: }
406:
1.17 nicm 407: /* Set up context for TTY command. */
1.1 nicm 408: void
1.31 nicm 409: screen_write_initctx(
410: struct screen_write_ctx *ctx, struct tty_ctx *ttyctx, int save_last)
1.1 nicm 411: {
1.31 nicm 412: struct screen *s = ctx->s;
413: struct grid *gd = s->grid;
414: const struct grid_cell *gc;
415: const struct grid_utf8 *gu;
416: u_int xx;
1.1 nicm 417:
1.17 nicm 418: ttyctx->wp = ctx->wp;
1.1 nicm 419:
1.17 nicm 420: ttyctx->ocx = s->cx;
421: ttyctx->ocy = s->cy;
422:
423: ttyctx->orlower = s->rlower;
424: ttyctx->orupper = s->rupper;
1.31 nicm 425:
426: if (!save_last)
427: return;
428:
429: /* Save the last cell on the screen. */
430: gc = NULL;
431: for (xx = 1; xx < screen_size_x(s); xx++) {
432: gc = grid_view_peek_cell(gd, screen_size_x(s) - xx, s->cy);
433: if (!(gc->flags & GRID_FLAG_PADDING))
434: break;
435: }
436: ttyctx->last_width = xx;
437: memcpy(&ttyctx->last_cell, gc, sizeof ttyctx->last_cell);
438: if (gc->flags & GRID_FLAG_UTF8) {
439: gu = grid_view_peek_utf8(gd, screen_size_x(s) - xx, s->cy);
440: memcpy(&ttyctx->last_utf8, gu, sizeof ttyctx->last_utf8);
441: }
1.1 nicm 442: }
443:
444: /* Cursor up by ny. */
445: void
446: screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny)
447: {
448: struct screen *s = ctx->s;
449:
450: if (ny == 0)
451: ny = 1;
452:
1.12 nicm 453: if (s->cy < s->rupper) {
454: /* Above region. */
455: if (ny > s->cy)
456: ny = s->cy;
457: } else {
458: /* Below region. */
459: if (ny > s->cy - s->rupper)
460: ny = s->cy - s->rupper;
461: }
1.1 nicm 462: if (ny == 0)
463: return;
464:
465: s->cy -= ny;
466: }
467:
468: /* Cursor down by ny. */
469: void
470: screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny)
471: {
472: struct screen *s = ctx->s;
473:
474: if (ny == 0)
475: ny = 1;
476:
1.12 nicm 477: if (s->cy > s->rlower) {
478: /* Below region. */
479: if (ny > screen_size_y(s) - 1 - s->cy)
480: ny = screen_size_y(s) - 1 - s->cy;
481: } else {
482: /* Above region. */
483: if (ny > s->rlower - s->cy)
484: ny = s->rlower - s->cy;
485: }
1.1 nicm 486: if (ny == 0)
487: return;
488:
489: s->cy += ny;
490: }
491:
492: /* Cursor right by nx. */
493: void
494: screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx)
495: {
496: struct screen *s = ctx->s;
497:
498: if (nx == 0)
499: nx = 1;
500:
501: if (nx > screen_size_x(s) - 1 - s->cx)
502: nx = screen_size_x(s) - 1 - s->cx;
503: if (nx == 0)
504: return;
505:
506: s->cx += nx;
507: }
508:
509: /* Cursor left by nx. */
510: void
511: screen_write_cursorleft(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 > s->cx)
519: nx = s->cx;
520: if (nx == 0)
521: return;
522:
523: s->cx -= nx;
1.5 nicm 524: }
525:
1.29 nicm 526: /* Backspace; cursor left unless at start of wrapped line when can move up. */
527: void
528: screen_write_backspace(struct screen_write_ctx *ctx)
529: {
530: struct screen *s = ctx->s;
531: struct grid_line *gl;
532:
533: if (s->cx == 0) {
534: if (s->cy == 0)
535: return;
536: gl = &s->grid->linedata[s->grid->hsize + s->cy - 1];
537: if (gl->flags & GRID_LINE_WRAPPED) {
538: s->cy--;
539: s->cx = screen_size_x(s) - 1;
540: }
541: } else
542: s->cx--;
543: }
544:
1.5 nicm 545: /* VT100 alignment test. */
546: void
547: screen_write_alignmenttest(struct screen_write_ctx *ctx)
548: {
549: struct screen *s = ctx->s;
1.17 nicm 550: struct tty_ctx ttyctx;
1.5 nicm 551: struct grid_cell gc;
552: u_int xx, yy;
553:
1.31 nicm 554: screen_write_initctx(ctx, &ttyctx, 0);
1.17 nicm 555:
1.5 nicm 556: memcpy(&gc, &grid_default_cell, sizeof gc);
557: gc.data = 'E';
1.7 ray 558:
1.5 nicm 559: for (yy = 0; yy < screen_size_y(s); yy++) {
560: for (xx = 0; xx < screen_size_x(s); xx++)
561: grid_view_set_cell(s->grid, xx, yy, &gc);
562: }
1.7 ray 563:
1.5 nicm 564: s->cx = 0;
565: s->cy = 0;
566:
567: s->rupper = 0;
1.29 nicm 568:
1.5 nicm 569: s->rlower = screen_size_y(s) - 1;
570:
1.17 nicm 571: tty_write(tty_cmd_alignmenttest, &ttyctx);
1.1 nicm 572: }
573:
574: /* Insert nx characters. */
575: void
576: screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx)
577: {
578: struct screen *s = ctx->s;
1.17 nicm 579: struct tty_ctx ttyctx;
1.1 nicm 580:
581: if (nx == 0)
582: nx = 1;
583:
1.9 nicm 584: if (nx > screen_size_x(s) - s->cx)
585: nx = screen_size_x(s) - s->cx;
1.1 nicm 586: if (nx == 0)
587: return;
588:
1.31 nicm 589: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 590:
591: if (s->cx <= screen_size_x(s) - 1)
592: grid_view_insert_cells(s->grid, s->cx, s->cy, nx);
593:
1.17 nicm 594: ttyctx.num = nx;
595: tty_write(tty_cmd_insertcharacter, &ttyctx);
1.1 nicm 596: }
597:
598: /* Delete nx characters. */
599: void
600: screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx)
601: {
602: struct screen *s = ctx->s;
1.17 nicm 603: struct tty_ctx ttyctx;
1.1 nicm 604:
605: if (nx == 0)
606: nx = 1;
607:
1.9 nicm 608: if (nx > screen_size_x(s) - s->cx)
609: nx = screen_size_x(s) - s->cx;
1.1 nicm 610: if (nx == 0)
611: return;
612:
1.31 nicm 613: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 614:
615: if (s->cx <= screen_size_x(s) - 1)
616: grid_view_delete_cells(s->grid, s->cx, s->cy, nx);
617:
1.17 nicm 618: ttyctx.num = nx;
619: tty_write(tty_cmd_deletecharacter, &ttyctx);
1.1 nicm 620: }
621:
622: /* Insert ny lines. */
623: void
624: screen_write_insertline(struct screen_write_ctx *ctx, u_int ny)
625: {
626: struct screen *s = ctx->s;
1.17 nicm 627: struct tty_ctx ttyctx;
1.1 nicm 628:
629: if (ny == 0)
630: ny = 1;
631:
1.11 nicm 632: if (s->cy < s->rupper || s->cy > s->rlower) {
633: if (ny > screen_size_y(s) - s->cy)
634: ny = screen_size_y(s) - s->cy;
635: if (ny == 0)
636: return;
637:
1.31 nicm 638: screen_write_initctx(ctx, &ttyctx, 0);
1.11 nicm 639:
640: grid_view_insert_lines(s->grid, s->cy, ny);
641:
1.17 nicm 642: ttyctx.num = ny;
643: tty_write(tty_cmd_insertline, &ttyctx);
1.11 nicm 644: return;
645: }
646:
647: if (ny > s->rlower + 1 - s->cy)
648: ny = s->rlower + 1 - s->cy;
1.1 nicm 649: if (ny == 0)
650: return;
1.11 nicm 651:
1.31 nicm 652: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 653:
654: if (s->cy < s->rupper || s->cy > s->rlower)
655: grid_view_insert_lines(s->grid, s->cy, ny);
1.10 nicm 656: else
657: grid_view_insert_lines_region(s->grid, s->rlower, s->cy, ny);
1.1 nicm 658:
1.17 nicm 659: ttyctx.num = ny;
660: tty_write(tty_cmd_insertline, &ttyctx);
1.1 nicm 661: }
662:
663: /* Delete ny lines. */
664: void
665: screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny)
666: {
667: struct screen *s = ctx->s;
1.17 nicm 668: struct tty_ctx ttyctx;
1.1 nicm 669:
670: if (ny == 0)
671: ny = 1;
672:
1.11 nicm 673: if (s->cy < s->rupper || s->cy > s->rlower) {
674: if (ny > screen_size_y(s) - s->cy)
675: ny = screen_size_y(s) - s->cy;
676: if (ny == 0)
677: return;
678:
1.31 nicm 679: screen_write_initctx(ctx, &ttyctx, 0);
1.11 nicm 680:
681: grid_view_delete_lines(s->grid, s->cy, ny);
682:
1.17 nicm 683: ttyctx.num = ny;
684: tty_write(tty_cmd_deleteline, &ttyctx);
1.11 nicm 685: return;
686: }
687:
688: if (ny > s->rlower + 1 - s->cy)
689: ny = s->rlower + 1 - s->cy;
1.1 nicm 690: if (ny == 0)
691: return;
692:
1.31 nicm 693: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 694:
695: if (s->cy < s->rupper || s->cy > s->rlower)
696: grid_view_delete_lines(s->grid, s->cy, ny);
1.10 nicm 697: else
698: grid_view_delete_lines_region(s->grid, s->rlower, s->cy, ny);
1.1 nicm 699:
1.17 nicm 700: ttyctx.num = ny;
701: tty_write(tty_cmd_deleteline, &ttyctx);
1.1 nicm 702: }
703:
704: /* Clear line at cursor. */
705: void
706: screen_write_clearline(struct screen_write_ctx *ctx)
707: {
708: struct screen *s = ctx->s;
1.17 nicm 709: struct tty_ctx ttyctx;
1.1 nicm 710:
1.31 nicm 711: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 712:
713: grid_view_clear(s->grid, 0, s->cy, screen_size_x(s), 1);
714:
1.17 nicm 715: tty_write(tty_cmd_clearline, &ttyctx);
1.1 nicm 716: }
717:
718: /* Clear to end of line from cursor. */
719: void
720: screen_write_clearendofline(struct screen_write_ctx *ctx)
721: {
722: struct screen *s = ctx->s;
1.17 nicm 723: struct tty_ctx ttyctx;
1.1 nicm 724: u_int sx;
725:
1.31 nicm 726: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 727:
728: sx = screen_size_x(s);
729:
730: if (s->cx <= sx - 1)
731: grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
732:
1.17 nicm 733: tty_write(tty_cmd_clearendofline, &ttyctx);
1.1 nicm 734: }
735:
736: /* Clear to start of line from cursor. */
737: void
738: screen_write_clearstartofline(struct screen_write_ctx *ctx)
739: {
740: struct screen *s = ctx->s;
1.17 nicm 741: struct tty_ctx ttyctx;
1.1 nicm 742: u_int sx;
743:
1.31 nicm 744: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 745:
746: sx = screen_size_x(s);
747:
748: if (s->cx > sx - 1)
749: grid_view_clear(s->grid, 0, s->cy, sx, 1);
750: else
751: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
752:
1.17 nicm 753: tty_write(tty_cmd_clearstartofline, &ttyctx);
1.1 nicm 754: }
755:
756: /* Move cursor to px,py. */
757: void
758: screen_write_cursormove(struct screen_write_ctx *ctx, u_int px, u_int py)
759: {
760: struct screen *s = ctx->s;
761:
762: if (px > screen_size_x(s) - 1)
763: px = screen_size_x(s) - 1;
764: if (py > screen_size_y(s) - 1)
765: py = screen_size_y(s) - 1;
766:
767: s->cx = px;
768: s->cy = py;
769: }
770:
771: /* Set cursor mode. */
772: void
773: screen_write_cursormode(struct screen_write_ctx *ctx, int state)
774: {
775: struct screen *s = ctx->s;
776:
777: if (state)
778: s->mode |= MODE_CURSOR;
779: else
780: s->mode &= ~MODE_CURSOR;
781: }
782:
783: /* Reverse index (up with scroll). */
784: void
785: screen_write_reverseindex(struct screen_write_ctx *ctx)
786: {
787: struct screen *s = ctx->s;
1.17 nicm 788: struct tty_ctx ttyctx;
1.1 nicm 789:
1.31 nicm 790: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 791:
792: if (s->cy == s->rupper)
793: grid_view_scroll_region_down(s->grid, s->rupper, s->rlower);
794: else if (s->cy > 0)
795: s->cy--;
796:
1.17 nicm 797: tty_write(tty_cmd_reverseindex, &ttyctx);
1.1 nicm 798: }
799:
800: /* Set scroll region. */
801: void
802: screen_write_scrollregion(
803: struct screen_write_ctx *ctx, u_int rupper, u_int rlower)
804: {
805: struct screen *s = ctx->s;
806:
807: if (rupper > screen_size_y(s) - 1)
808: rupper = screen_size_y(s) - 1;
809: if (rlower > screen_size_y(s) - 1)
810: rlower = screen_size_y(s) - 1;
1.13 nicm 811: if (rupper >= rlower) /* cannot be one line */
1.1 nicm 812: return;
813:
814: /* Cursor moves to top-left. */
815: s->cx = 0;
816: s->cy = 0;
817:
818: s->rupper = rupper;
819: s->rlower = rlower;
820: }
821:
822: /* Set insert mode. */
823: void
824: screen_write_insertmode(struct screen_write_ctx *ctx, int state)
825: {
826: struct screen *s = ctx->s;
827:
828: if (state)
829: s->mode |= MODE_INSERT;
830: else
831: s->mode &= ~MODE_INSERT;
832: }
833:
834: /* Set mouse mode. */
835: void
836: screen_write_mousemode(struct screen_write_ctx *ctx, int state)
837: {
838: struct screen *s = ctx->s;
839:
840: if (state)
841: s->mode |= MODE_MOUSE;
842: else
843: s->mode &= ~MODE_MOUSE;
844: }
845:
1.34 nicm 846: /* Line feed. */
1.1 nicm 847: void
1.34 nicm 848: screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped)
1.1 nicm 849: {
1.20 nicm 850: struct screen *s = ctx->s;
851: struct grid_line *gl;
1.34 nicm 852: struct tty_ctx ttyctx;
853:
854: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 855:
1.20 nicm 856: gl = &s->grid->linedata[s->grid->hsize + s->cy];
857: if (wrapped)
858: gl->flags |= GRID_LINE_WRAPPED;
859: else
860: gl->flags &= ~GRID_LINE_WRAPPED;
861:
1.1 nicm 862: if (s->cy == s->rlower)
863: grid_view_scroll_region_up(s->grid, s->rupper, s->rlower);
864: else if (s->cy < screen_size_y(s) - 1)
865: s->cy++;
866:
1.34 nicm 867: ttyctx.num = wrapped;
1.17 nicm 868: tty_write(tty_cmd_linefeed, &ttyctx);
1.1 nicm 869: }
870:
871: /* Carriage return (cursor to start of line). */
872: void
873: screen_write_carriagereturn(struct screen_write_ctx *ctx)
874: {
875: struct screen *s = ctx->s;
876:
877: s->cx = 0;
878: }
879:
880: /* Set keypad cursor keys mode. */
881: void
882: screen_write_kcursormode(struct screen_write_ctx *ctx, int state)
883: {
884: struct screen *s = ctx->s;
885:
886: if (state)
887: s->mode |= MODE_KCURSOR;
888: else
889: s->mode &= ~MODE_KCURSOR;
890: }
891:
892: /* Set keypad number keys mode. */
893: void
894: screen_write_kkeypadmode(struct screen_write_ctx *ctx, int state)
895: {
896: struct screen *s = ctx->s;
897:
898: if (state)
899: s->mode |= MODE_KKEYPAD;
900: else
901: s->mode &= ~MODE_KKEYPAD;
902: }
903:
904: /* Clear to end of screen from cursor. */
905: void
906: screen_write_clearendofscreen(struct screen_write_ctx *ctx)
907: {
908: struct screen *s = ctx->s;
1.17 nicm 909: struct tty_ctx ttyctx;
1.1 nicm 910: u_int sx, sy;
911:
1.31 nicm 912: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 913:
914: sx = screen_size_x(s);
915: sy = screen_size_y(s);
916:
917: if (s->cx <= sx - 1)
918: grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1);
919: grid_view_clear(s->grid, 0, s->cy + 1, sx, sy - (s->cy + 1));
920:
1.17 nicm 921: tty_write(tty_cmd_clearendofscreen, &ttyctx);
1.1 nicm 922: }
923:
924: /* Clear to start of screen. */
925: void
926: screen_write_clearstartofscreen(struct screen_write_ctx *ctx)
927: {
928: struct screen *s = ctx->s;
1.17 nicm 929: struct tty_ctx ttyctx;
1.1 nicm 930: u_int sx;
931:
1.31 nicm 932: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 933:
934: sx = screen_size_x(s);
935:
936: if (s->cy > 0)
1.4 nicm 937: grid_view_clear(s->grid, 0, 0, sx, s->cy);
1.1 nicm 938: if (s->cx > sx - 1)
939: grid_view_clear(s->grid, 0, s->cy, sx, 1);
940: else
1.4 nicm 941: grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1);
1.1 nicm 942:
1.17 nicm 943: tty_write(tty_cmd_clearstartofscreen, &ttyctx);
1.1 nicm 944: }
945:
946: /* Clear entire screen. */
947: void
948: screen_write_clearscreen(struct screen_write_ctx *ctx)
949: {
950: struct screen *s = ctx->s;
1.17 nicm 951: struct tty_ctx ttyctx;
1.1 nicm 952:
1.31 nicm 953: screen_write_initctx(ctx, &ttyctx, 0);
1.1 nicm 954:
955: grid_view_clear(s->grid, 0, 0, screen_size_x(s), screen_size_y(s));
956:
1.17 nicm 957: tty_write(tty_cmd_clearscreen, &ttyctx);
1.1 nicm 958: }
959:
960: /* Write cell data. */
961: void
1.36 ! nicm 962: screen_write_cell(struct screen_write_ctx *ctx,
! 963: const struct grid_cell *gc, const struct utf8_data *utf8data)
1.1 nicm 964: {
965: struct screen *s = ctx->s;
966: struct grid *gd = s->grid;
1.15 nicm 967: struct tty_ctx ttyctx;
1.35 nicm 968: struct grid_utf8 gu;
969: u_int width, xx;
1.33 nicm 970: struct grid_cell tmp_gc, *tmp_gcp;
1.6 nicm 971: int insert = 0;
1.1 nicm 972:
973: /* Ignore padding. */
974: if (gc->flags & GRID_FLAG_PADDING)
975: return;
976:
977: /* Find character width. */
1.36 ! nicm 978: if (gc->flags & GRID_FLAG_UTF8)
! 979: width = utf8data->width;
! 980: else
1.1 nicm 981: width = 1;
982:
1.32 nicm 983: /*
984: * If this is a wide character and there is no room on the screen, for
985: * the entire character, don't print it.
986: */
987: if (width > 1 && (width > screen_size_x(s) ||
988: (s->cx != screen_size_x(s) && s->cx > screen_size_x(s) - width)))
989: return;
990:
1.35 nicm 991: /*
992: * If the width is zero, combine onto the previous character, if
993: * there is space.
994: */
1.1 nicm 995: if (width == 0) {
1.36 ! nicm 996: if (screen_write_combine(ctx, utf8data) == 0) {
1.35 nicm 997: screen_write_initctx(ctx, &ttyctx, 0);
998: tty_write(tty_cmd_utf8character, &ttyctx);
1.1 nicm 999: }
1000: return;
1001: }
1002:
1.31 nicm 1003: /* Initialise the redraw context, saving the last cell. */
1004: screen_write_initctx(ctx, &ttyctx, 1);
1005:
1.6 nicm 1006: /* If in insert mode, make space for the cells. */
1007: if (s->mode & MODE_INSERT && s->cx <= screen_size_x(s) - width) {
1008: xx = screen_size_x(s) - s->cx - width;
1009: grid_move_cells(s->grid, s->cx + width, s->cx, s->cy, xx);
1010: insert = 1;
1011: }
1012:
1.20 nicm 1013: /* Check this will fit on the current line and wrap if not. */
1.1 nicm 1014: if (s->cx > screen_size_x(s) - width) {
1.34 nicm 1015: screen_write_linefeed(ctx, 1);
1.30 nicm 1016: s->cx = 0; /* carriage return */
1.1 nicm 1017: }
1018:
1019: /* Sanity checks. */
1020: if (s->cx > screen_size_x(s) - 1 || s->cy > screen_size_y(s) - 1)
1021: return;
1022:
1023: /* Handle overwriting of UTF-8 characters. */
1024: screen_write_overwrite(ctx);
1025:
1026: /*
1027: * If the new character is UTF-8 wide, fill in padding cells. Have
1028: * already ensured there is enough room.
1029: */
1030: for (xx = s->cx + 1; xx < s->cx + width; xx++) {
1.18 nicm 1031: tmp_gcp = grid_view_get_cell(gd, xx, s->cy);
1032: if (tmp_gcp != NULL)
1033: tmp_gcp->flags |= GRID_FLAG_PADDING;
1.1 nicm 1034: }
1035:
1036: /* Set the cell. */
1037: grid_view_set_cell(gd, s->cx, s->cy, gc);
1.36 ! nicm 1038: if (gc->flags & GRID_FLAG_UTF8) {
! 1039: /* Construct UTF-8 and write it. */
! 1040: gu.width = utf8data->width;
! 1041: memset(gu.data, 0xff, sizeof gu.data);
! 1042: if (utf8data->size > sizeof gu.data)
! 1043: fatalx("UTF-8 data overflow");
! 1044: memcpy(gu.data, utf8data->data, utf8data->size);
1.1 nicm 1045: grid_view_set_utf8(gd, s->cx, s->cy, &gu);
1.36 ! nicm 1046: }
1.1 nicm 1047:
1048: /* Move the cursor. */
1049: s->cx += width;
1050:
1051: /* Draw to the screen if necessary. */
1.17 nicm 1052: if (insert) {
1053: ttyctx.num = width;
1054: tty_write(tty_cmd_insertcharacter, &ttyctx);
1055: }
1.15 nicm 1056: ttyctx.utf8 = &gu;
1.1 nicm 1057: if (screen_check_selection(s, s->cx - width, s->cy)) {
1.33 nicm 1058: memcpy(&tmp_gc, &s->sel.cell, sizeof tmp_gc);
1059: tmp_gc.data = gc->data;
1060: tmp_gc.flags = gc->flags &
1.28 nicm 1061: ~(GRID_FLAG_FG256|GRID_FLAG_BG256);
1.33 nicm 1062: tmp_gc.flags |= s->sel.cell.flags &
1.28 nicm 1063: (GRID_FLAG_FG256|GRID_FLAG_BG256);
1.33 nicm 1064: ttyctx.cell = &tmp_gc;
1.16 nicm 1065: tty_write(tty_cmd_cell, &ttyctx);
1.15 nicm 1066: } else {
1067: ttyctx.cell = gc;
1.16 nicm 1068: tty_write(tty_cmd_cell, &ttyctx);
1.15 nicm 1069: }
1.35 nicm 1070: }
1071:
1072: /* Combine a UTF-8 zero-width character onto the previous. */
1073: int
1.36 ! nicm 1074: screen_write_combine(
! 1075: struct screen_write_ctx *ctx, const struct utf8_data *utf8data)
1.35 nicm 1076: {
1077: struct screen *s = ctx->s;
1078: struct grid *gd = s->grid;
1079: struct grid_cell *gc;
1080: struct grid_utf8 *gu, tmp_gu;
1.36 ! nicm 1081: u_int i, old_size;
1.35 nicm 1082:
1083: /* Can't combine if at 0. */
1084: if (s->cx == 0)
1085: return (-1);
1086:
1087: /* Retrieve the previous cell and convert to UTF-8 if not already. */
1088: gc = grid_view_get_cell(gd, s->cx - 1, s->cy);
1089: if (!(gc->flags & GRID_FLAG_UTF8)) {
1090: memset(&tmp_gu.data, 0xff, sizeof tmp_gu.data);
1091: *tmp_gu.data = gc->data;
1092: tmp_gu.width = 1;
1093:
1094: grid_view_set_utf8(gd, s->cx - 1, s->cy, &tmp_gu);
1095: gc->flags |= GRID_FLAG_UTF8;
1096: }
1097:
1.36 ! nicm 1098: /* Get the previous cell's UTF-8 data and its size. */
1.35 nicm 1099: gu = grid_view_get_utf8(gd, s->cx - 1, s->cy);
1100: for (old_size = 0; old_size < UTF8_SIZE; old_size++) {
1101: if (gu->data[old_size] == 0xff)
1102: break;
1103: }
1104:
1105: /* If there isn't space, scrap this character. */
1.36 ! nicm 1106: if (old_size + utf8data->size > UTF8_SIZE) {
1.35 nicm 1107: for (i = 0; i < gu->width && i != UTF8_SIZE; i++)
1108: gu->data[i] = '_';
1109: if (i != UTF8_SIZE)
1110: gu->data[i] = 0xff;
1111: return (0);
1112: }
1113:
1114: /* Otherwise save the character. */
1.36 ! nicm 1115: memcpy(gu->data + old_size, utf8data->data, utf8data->size);
! 1116: if (old_size + utf8data->size != UTF8_SIZE)
! 1117: gu->data[old_size + utf8data->size] = 0xff;
1.35 nicm 1118: return (0);
1.1 nicm 1119: }
1120:
1121: /*
1122: * UTF-8 wide characters are a bit of an annoyance. They take up more than one
1123: * cell on the screen, so following cells must not be drawn by marking them as
1124: * padding.
1125: *
1126: * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8
1127: * character, it is necessary to also overwrite any other cells which covered
1128: * by the same character.
1129: */
1130: void
1131: screen_write_overwrite(struct screen_write_ctx *ctx)
1132: {
1133: struct screen *s = ctx->s;
1134: struct grid *gd = s->grid;
1135: const struct grid_cell *gc;
1136: const struct grid_utf8 *gu;
1137: u_int xx;
1138:
1139: gc = grid_view_peek_cell(gd, s->cx, s->cy);
1140: if (gc->flags & GRID_FLAG_PADDING) {
1141: /*
1142: * A padding cell, so clear any following and leading padding
1143: * cells back to the character. Don't overwrite the current
1144: * cell as that happens later anyway.
1145: */
1146: xx = s->cx + 1;
1147: while (--xx > 0) {
1148: gc = grid_view_peek_cell(gd, xx, s->cy);
1149: if (!(gc->flags & GRID_FLAG_PADDING))
1150: break;
1151: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1152: }
1153:
1154: /* Overwrite the character at the start of this padding. */
1155: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1156:
1157: /* Overwrite following padding cells. */
1158: xx = s->cx;
1159: while (++xx < screen_size_x(s)) {
1160: gc = grid_view_peek_cell(gd, xx, s->cy);
1161: if (!(gc->flags & GRID_FLAG_PADDING))
1162: break;
1163: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1164: }
1.22 nicm 1165: } else if (gc->flags & GRID_FLAG_UTF8) {
1166: gu = grid_view_peek_utf8(gd, s->cx, s->cy);
1167: if (gu->width > 1) {
1168: /*
1169: * An UTF-8 wide cell; overwrite following padding cells only.
1170: */
1171: xx = s->cx;
1172: while (++xx < screen_size_x(s)) {
1173: gc = grid_view_peek_cell(gd, xx, s->cy);
1174: if (!(gc->flags & GRID_FLAG_PADDING))
1175: break;
1176: grid_view_set_cell(gd, xx, s->cy, &grid_default_cell);
1177: }
1.1 nicm 1178: }
1179: }
1180: }