1 | /* $OpenBSD$ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2007 Nicholas Marriott <[email protected]> |
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 <stdlib.h> |
22 | #include <string.h> |
23 | |
24 | #include "tmux.h" |
25 | |
26 | static void screen_write_initctx(struct screen_write_ctx *, |
27 | struct tty_ctx *); |
28 | static void screen_write_collect_clear(struct screen_write_ctx *, u_int, |
29 | u_int); |
30 | static void screen_write_collect_scroll(struct screen_write_ctx *); |
31 | static void screen_write_collect_flush(struct screen_write_ctx *, int); |
32 | |
33 | static int screen_write_overwrite(struct screen_write_ctx *, |
34 | struct grid_cell *, u_int); |
35 | static const struct grid_cell *screen_write_combine(struct screen_write_ctx *, |
36 | const struct utf8_data *, u_int *); |
37 | |
38 | static const struct grid_cell screen_write_pad_cell = { |
39 | { { 0 }, 0, 0, 0 }, 0, GRID_FLAG_PADDING, 0, 8, 8 |
40 | }; |
41 | |
42 | struct screen_write_collect_item { |
43 | u_int x; |
44 | int wrapped; |
45 | |
46 | u_int used; |
47 | char data[256]; |
48 | |
49 | struct grid_cell gc; |
50 | |
51 | TAILQ_ENTRY(screen_write_collect_item) entry; |
52 | }; |
53 | struct screen_write_collect_line { |
54 | TAILQ_HEAD(, screen_write_collect_item) items; |
55 | }; |
56 | |
57 | static void |
58 | screen_write_offset_timer(__unused int fd, __unused short events, void *data) |
59 | { |
60 | struct window *w = data; |
61 | |
62 | tty_update_window_offset(w); |
63 | } |
64 | |
65 | /* Set cursor position. */ |
66 | static void |
67 | screen_write_set_cursor(struct screen_write_ctx *ctx, int cx, int cy) |
68 | { |
69 | struct window_pane *wp = ctx->wp; |
70 | struct window *w; |
71 | struct screen *s = ctx->s; |
72 | struct timeval tv = { .tv_usec = 10000 }; |
73 | |
74 | if (cx != -1 && (u_int)cx == s->cx && cy != -1 && (u_int)cy == s->cy) |
75 | return; |
76 | |
77 | if (cx != -1) { |
78 | if ((u_int)cx > screen_size_x(s)) /* allow last column */ |
79 | cx = screen_size_x(s) - 1; |
80 | s->cx = cx; |
81 | } |
82 | if (cy != -1) { |
83 | if ((u_int)cy > screen_size_y(s) - 1) |
84 | cy = screen_size_y(s) - 1; |
85 | s->cy = cy; |
86 | } |
87 | |
88 | if (wp == NULL) |
89 | return; |
90 | w = wp->window; |
91 | |
92 | if (!event_initialized(&w->offset_timer)) |
93 | evtimer_set(&w->offset_timer, screen_write_offset_timer, w); |
94 | if (!evtimer_pending(&w->offset_timer, NULL)) |
95 | evtimer_add(&w->offset_timer, &tv); |
96 | } |
97 | |
98 | /* Initialize writing with a window. */ |
99 | void |
100 | screen_write_start(struct screen_write_ctx *ctx, struct window_pane *wp, |
101 | struct screen *s) |
102 | { |
103 | u_int y; |
104 | |
105 | memset(ctx, 0, sizeof *ctx); |
106 | |
107 | ctx->wp = wp; |
108 | if (wp != NULL && s == NULL) |
109 | ctx->s = wp->screen; |
110 | else |
111 | ctx->s = s; |
112 | |
113 | ctx->list = xcalloc(screen_size_y(ctx->s), sizeof *ctx->list); |
114 | for (y = 0; y < screen_size_y(ctx->s); y++) |
115 | TAILQ_INIT(&ctx->list[y].items); |
116 | ctx->item = xcalloc(1, sizeof *ctx->item); |
117 | |
118 | ctx->scrolled = 0; |
119 | ctx->bg = 8; |
120 | |
121 | if (log_get_level() != 0) { |
122 | if (wp != NULL) { |
123 | log_debug("%s: size %ux%u, pane %%%u (at %u,%u)" , |
124 | __func__, screen_size_x(ctx->s), |
125 | screen_size_y(ctx->s), wp->id, wp->xoff, wp->yoff); |
126 | } else { |
127 | log_debug("%s: size %ux%u, no pane" , |
128 | __func__, screen_size_x(ctx->s), |
129 | screen_size_y(ctx->s)); |
130 | } |
131 | } |
132 | } |
133 | |
134 | /* Finish writing. */ |
135 | void |
136 | screen_write_stop(struct screen_write_ctx *ctx) |
137 | { |
138 | screen_write_collect_end(ctx); |
139 | screen_write_collect_flush(ctx, 0); |
140 | |
141 | log_debug("%s: %u cells (%u written, %u skipped)" , __func__, |
142 | ctx->cells, ctx->written, ctx->skipped); |
143 | |
144 | free(ctx->item); |
145 | free(ctx->list); /* flush will have emptied */ |
146 | } |
147 | |
148 | /* Reset screen state. */ |
149 | void |
150 | screen_write_reset(struct screen_write_ctx *ctx) |
151 | { |
152 | struct screen *s = ctx->s; |
153 | |
154 | screen_reset_tabs(s); |
155 | screen_write_scrollregion(ctx, 0, screen_size_y(s) - 1); |
156 | |
157 | s->mode = MODE_CURSOR | MODE_WRAP; |
158 | |
159 | screen_write_clearscreen(ctx, 8); |
160 | screen_write_set_cursor(ctx, 0, 0); |
161 | } |
162 | |
163 | /* Write character. */ |
164 | void |
165 | screen_write_putc(struct screen_write_ctx *ctx, const struct grid_cell *gcp, |
166 | u_char ch) |
167 | { |
168 | struct grid_cell gc; |
169 | |
170 | memcpy(&gc, gcp, sizeof gc); |
171 | |
172 | utf8_set(&gc.data, ch); |
173 | screen_write_cell(ctx, &gc); |
174 | } |
175 | |
176 | /* Calculate string length. */ |
177 | size_t |
178 | screen_write_strlen(const char *fmt, ...) |
179 | { |
180 | va_list ap; |
181 | char *msg; |
182 | struct utf8_data ud; |
183 | u_char *ptr; |
184 | size_t left, size = 0; |
185 | enum utf8_state more; |
186 | |
187 | va_start(ap, fmt); |
188 | xvasprintf(&msg, fmt, ap); |
189 | va_end(ap); |
190 | |
191 | ptr = msg; |
192 | while (*ptr != '\0') { |
193 | if (*ptr > 0x7f && utf8_open(&ud, *ptr) == UTF8_MORE) { |
194 | ptr++; |
195 | |
196 | left = strlen(ptr); |
197 | if (left < (size_t)ud.size - 1) |
198 | break; |
199 | while ((more = utf8_append(&ud, *ptr)) == UTF8_MORE) |
200 | ptr++; |
201 | ptr++; |
202 | |
203 | if (more == UTF8_DONE) |
204 | size += ud.width; |
205 | } else { |
206 | if (*ptr > 0x1f && *ptr < 0x7f) |
207 | size++; |
208 | ptr++; |
209 | } |
210 | } |
211 | |
212 | free(msg); |
213 | return (size); |
214 | } |
215 | |
216 | /* Write simple string (no UTF-8 or maximum length). */ |
217 | void |
218 | screen_write_puts(struct screen_write_ctx *ctx, const struct grid_cell *gcp, |
219 | const char *fmt, ...) |
220 | { |
221 | va_list ap; |
222 | |
223 | va_start(ap, fmt); |
224 | screen_write_vnputs(ctx, -1, gcp, fmt, ap); |
225 | va_end(ap); |
226 | } |
227 | |
228 | /* Write string with length limit (-1 for unlimited). */ |
229 | void |
230 | screen_write_nputs(struct screen_write_ctx *ctx, ssize_t maxlen, |
231 | const struct grid_cell *gcp, const char *fmt, ...) |
232 | { |
233 | va_list ap; |
234 | |
235 | va_start(ap, fmt); |
236 | screen_write_vnputs(ctx, maxlen, gcp, fmt, ap); |
237 | va_end(ap); |
238 | } |
239 | |
240 | void |
241 | screen_write_vnputs(struct screen_write_ctx *ctx, ssize_t maxlen, |
242 | const struct grid_cell *gcp, const char *fmt, va_list ap) |
243 | { |
244 | struct grid_cell gc; |
245 | struct utf8_data *ud = &gc.data; |
246 | char *msg; |
247 | u_char *ptr; |
248 | size_t left, size = 0; |
249 | enum utf8_state more; |
250 | |
251 | memcpy(&gc, gcp, sizeof gc); |
252 | xvasprintf(&msg, fmt, ap); |
253 | |
254 | ptr = msg; |
255 | while (*ptr != '\0') { |
256 | if (*ptr > 0x7f && utf8_open(ud, *ptr) == UTF8_MORE) { |
257 | ptr++; |
258 | |
259 | left = strlen(ptr); |
260 | if (left < (size_t)ud->size - 1) |
261 | break; |
262 | while ((more = utf8_append(ud, *ptr)) == UTF8_MORE) |
263 | ptr++; |
264 | ptr++; |
265 | |
266 | if (more != UTF8_DONE) |
267 | continue; |
268 | if (maxlen > 0 && size + ud->width > (size_t)maxlen) { |
269 | while (size < (size_t)maxlen) { |
270 | screen_write_putc(ctx, &gc, ' '); |
271 | size++; |
272 | } |
273 | break; |
274 | } |
275 | size += ud->width; |
276 | screen_write_cell(ctx, &gc); |
277 | } else { |
278 | if (maxlen > 0 && size + 1 > (size_t)maxlen) |
279 | break; |
280 | |
281 | if (*ptr == '\001') |
282 | gc.attr ^= GRID_ATTR_CHARSET; |
283 | else if (*ptr > 0x1f && *ptr < 0x7f) { |
284 | size++; |
285 | screen_write_putc(ctx, &gc, *ptr); |
286 | } |
287 | ptr++; |
288 | } |
289 | } |
290 | |
291 | free(msg); |
292 | } |
293 | |
294 | /* Copy from another screen. Assumes target region is big enough. */ |
295 | void |
296 | screen_write_copy(struct screen_write_ctx *ctx, struct screen *src, u_int px, |
297 | u_int py, u_int nx, u_int ny, bitstr_t *mbs, const struct grid_cell *mgc) |
298 | { |
299 | struct screen *s = ctx->s; |
300 | struct grid *gd = src->grid; |
301 | struct grid_cell gc; |
302 | u_int xx, yy, cx, cy, b; |
303 | |
304 | if (nx == 0 || ny == 0) |
305 | return; |
306 | |
307 | cx = s->cx; |
308 | cy = s->cy; |
309 | |
310 | for (yy = py; yy < py + ny; yy++) { |
311 | for (xx = px; xx < px + nx; xx++) { |
312 | grid_get_cell(gd, xx, yy, &gc); |
313 | if (mbs != NULL) { |
314 | b = (yy * screen_size_x(src)) + xx; |
315 | if (bit_test(mbs, b)) { |
316 | gc.attr = mgc->attr; |
317 | gc.fg = mgc->fg; |
318 | gc.bg = mgc->bg; |
319 | } |
320 | } |
321 | if (xx + gc.data.width <= px + nx) |
322 | screen_write_cell(ctx, &gc); |
323 | } |
324 | cy++; |
325 | screen_write_set_cursor(ctx, cx, cy); |
326 | } |
327 | } |
328 | |
329 | /* |
330 | * Copy from another screen but without the selection stuff. Also assumes the |
331 | * target region is already big enough. |
332 | */ |
333 | void |
334 | screen_write_fast_copy(struct screen_write_ctx *ctx, struct screen *src, |
335 | u_int px, u_int py, u_int nx, u_int ny) |
336 | { |
337 | struct screen *s = ctx->s; |
338 | struct grid *gd = src->grid; |
339 | struct grid_cell gc; |
340 | u_int xx, yy, cx, cy; |
341 | |
342 | if (nx == 0 || ny == 0) |
343 | return; |
344 | |
345 | cy = s->cy; |
346 | for (yy = py; yy < py + ny; yy++) { |
347 | if (yy >= gd->hsize + gd->sy) |
348 | break; |
349 | cx = s->cx; |
350 | for (xx = px; xx < px + nx; xx++) { |
351 | if (xx >= grid_get_line(gd, yy)->cellsize) |
352 | break; |
353 | grid_get_cell(gd, xx, yy, &gc); |
354 | if (xx + gc.data.width > px + nx) |
355 | break; |
356 | grid_view_set_cell(ctx->s->grid, cx, cy, &gc); |
357 | cx++; |
358 | } |
359 | cy++; |
360 | } |
361 | } |
362 | |
363 | /* Draw a horizontal line on screen. */ |
364 | void |
365 | screen_write_hline(struct screen_write_ctx *ctx, u_int nx, int left, int right) |
366 | { |
367 | struct screen *s = ctx->s; |
368 | struct grid_cell gc; |
369 | u_int cx, cy, i; |
370 | |
371 | cx = s->cx; |
372 | cy = s->cy; |
373 | |
374 | memcpy(&gc, &grid_default_cell, sizeof gc); |
375 | gc.attr |= GRID_ATTR_CHARSET; |
376 | |
377 | screen_write_putc(ctx, &gc, left ? 't' : 'q'); |
378 | for (i = 1; i < nx - 1; i++) |
379 | screen_write_putc(ctx, &gc, 'q'); |
380 | screen_write_putc(ctx, &gc, right ? 'u' : 'q'); |
381 | |
382 | screen_write_set_cursor(ctx, cx, cy); |
383 | } |
384 | |
385 | /* Draw a horizontal line on screen. */ |
386 | void |
387 | screen_write_vline(struct screen_write_ctx *ctx, u_int ny, int top, int bottom) |
388 | { |
389 | struct screen *s = ctx->s; |
390 | struct grid_cell gc; |
391 | u_int cx, cy, i; |
392 | |
393 | cx = s->cx; |
394 | cy = s->cy; |
395 | |
396 | memcpy(&gc, &grid_default_cell, sizeof gc); |
397 | gc.attr |= GRID_ATTR_CHARSET; |
398 | |
399 | screen_write_putc(ctx, &gc, top ? 'w' : 'x'); |
400 | for (i = 1; i < ny - 1; i++) { |
401 | screen_write_set_cursor(ctx, cx, cy + i); |
402 | screen_write_putc(ctx, &gc, 'x'); |
403 | } |
404 | screen_write_set_cursor(ctx, cx, cy + ny - 1); |
405 | screen_write_putc(ctx, &gc, bottom ? 'v' : 'x'); |
406 | |
407 | screen_write_set_cursor(ctx, cx, cy); |
408 | } |
409 | |
410 | /* Draw a menu on screen. */ |
411 | void |
412 | (struct screen_write_ctx *ctx, struct menu *, int choice) |
413 | { |
414 | struct screen *s = ctx->s; |
415 | struct grid_cell gc; |
416 | u_int cx, cy, i, j; |
417 | const char *name; |
418 | |
419 | cx = s->cx; |
420 | cy = s->cy; |
421 | |
422 | memcpy(&gc, &grid_default_cell, sizeof gc); |
423 | |
424 | screen_write_box(ctx, menu->width + 4, menu->count + 2); |
425 | screen_write_cursormove(ctx, cx + 2, cy, 0); |
426 | format_draw(ctx, &gc, menu->width, menu->title, NULL); |
427 | |
428 | for (i = 0; i < menu->count; i++) { |
429 | name = menu->items[i].name; |
430 | if (name == NULL) { |
431 | screen_write_cursormove(ctx, cx, cy + 1 + i, 0); |
432 | screen_write_hline(ctx, menu->width + 4, 1, 1); |
433 | } else { |
434 | if (choice >= 0 && i == (u_int)choice && *name != '-') |
435 | gc.attr |= GRID_ATTR_REVERSE; |
436 | screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); |
437 | for (j = 0; j < menu->width; j++) |
438 | screen_write_putc(ctx, &gc, ' '); |
439 | screen_write_cursormove(ctx, cx + 2, cy + 1 + i, 0); |
440 | if (*name == '-') { |
441 | name++; |
442 | gc.attr |= GRID_ATTR_DIM; |
443 | format_draw(ctx, &gc, menu->width, name, NULL); |
444 | gc.attr &= ~GRID_ATTR_DIM; |
445 | } else |
446 | format_draw(ctx, &gc, menu->width, name, NULL); |
447 | if (choice >= 0 && i == (u_int)choice) |
448 | gc.attr &= ~GRID_ATTR_REVERSE; |
449 | } |
450 | } |
451 | |
452 | screen_write_set_cursor(ctx, cx, cy); |
453 | } |
454 | |
455 | /* Draw a box on screen. */ |
456 | void |
457 | screen_write_box(struct screen_write_ctx *ctx, u_int nx, u_int ny) |
458 | { |
459 | struct screen *s = ctx->s; |
460 | struct grid_cell gc; |
461 | u_int cx, cy, i; |
462 | |
463 | cx = s->cx; |
464 | cy = s->cy; |
465 | |
466 | memcpy(&gc, &grid_default_cell, sizeof gc); |
467 | gc.attr |= GRID_ATTR_CHARSET; |
468 | |
469 | screen_write_putc(ctx, &gc, 'l'); |
470 | for (i = 1; i < nx - 1; i++) |
471 | screen_write_putc(ctx, &gc, 'q'); |
472 | screen_write_putc(ctx, &gc, 'k'); |
473 | |
474 | screen_write_set_cursor(ctx, cx, cy + ny - 1); |
475 | screen_write_putc(ctx, &gc, 'm'); |
476 | for (i = 1; i < nx - 1; i++) |
477 | screen_write_putc(ctx, &gc, 'q'); |
478 | screen_write_putc(ctx, &gc, 'j'); |
479 | |
480 | for (i = 1; i < ny - 1; i++) { |
481 | screen_write_set_cursor(ctx, cx, cy + i); |
482 | screen_write_putc(ctx, &gc, 'x'); |
483 | } |
484 | for (i = 1; i < ny - 1; i++) { |
485 | screen_write_set_cursor(ctx, cx + nx - 1, cy + i); |
486 | screen_write_putc(ctx, &gc, 'x'); |
487 | } |
488 | |
489 | screen_write_set_cursor(ctx, cx, cy); |
490 | } |
491 | |
492 | /* |
493 | * Write a preview version of a window. Assumes target area is big enough and |
494 | * already cleared. |
495 | */ |
496 | void |
497 | screen_write_preview(struct screen_write_ctx *ctx, struct screen *src, u_int nx, |
498 | u_int ny) |
499 | { |
500 | struct screen *s = ctx->s; |
501 | struct grid_cell gc; |
502 | u_int cx, cy, px, py; |
503 | |
504 | cx = s->cx; |
505 | cy = s->cy; |
506 | |
507 | /* |
508 | * If the cursor is on, pick the area around the cursor, otherwise use |
509 | * the top left. |
510 | */ |
511 | if (src->mode & MODE_CURSOR) { |
512 | px = src->cx; |
513 | if (px < nx / 3) |
514 | px = 0; |
515 | else |
516 | px = px - nx / 3; |
517 | if (px + nx > screen_size_x(src)) { |
518 | if (nx > screen_size_x(src)) |
519 | px = 0; |
520 | else |
521 | px = screen_size_x(src) - nx; |
522 | } |
523 | py = src->cy; |
524 | if (py < ny / 3) |
525 | py = 0; |
526 | else |
527 | py = py - ny / 3; |
528 | if (py + ny > screen_size_y(src)) { |
529 | if (ny > screen_size_y(src)) |
530 | py = 0; |
531 | else |
532 | py = screen_size_y(src) - ny; |
533 | } |
534 | } else { |
535 | px = 0; |
536 | py = 0; |
537 | } |
538 | |
539 | screen_write_fast_copy(ctx, src, px, src->grid->hsize + py, nx, ny); |
540 | |
541 | if (src->mode & MODE_CURSOR) { |
542 | grid_view_get_cell(src->grid, src->cx, src->cy, &gc); |
543 | gc.attr |= GRID_ATTR_REVERSE; |
544 | screen_write_set_cursor(ctx, cx + (src->cx - px), |
545 | cy + (src->cy - py)); |
546 | screen_write_cell(ctx, &gc); |
547 | } |
548 | } |
549 | |
550 | /* Set up context for TTY command. */ |
551 | static void |
552 | screen_write_initctx(struct screen_write_ctx *ctx, struct tty_ctx *ttyctx) |
553 | { |
554 | struct screen *s = ctx->s; |
555 | |
556 | memset(ttyctx, 0, sizeof *ttyctx); |
557 | |
558 | ttyctx->wp = ctx->wp; |
559 | |
560 | ttyctx->ocx = s->cx; |
561 | ttyctx->ocy = s->cy; |
562 | |
563 | ttyctx->orlower = s->rlower; |
564 | ttyctx->orupper = s->rupper; |
565 | } |
566 | |
567 | /* Set a mode. */ |
568 | void |
569 | screen_write_mode_set(struct screen_write_ctx *ctx, int mode) |
570 | { |
571 | struct screen *s = ctx->s; |
572 | |
573 | s->mode |= mode; |
574 | } |
575 | |
576 | /* Clear a mode. */ |
577 | void |
578 | screen_write_mode_clear(struct screen_write_ctx *ctx, int mode) |
579 | { |
580 | struct screen *s = ctx->s; |
581 | |
582 | s->mode &= ~mode; |
583 | } |
584 | |
585 | /* Cursor up by ny. */ |
586 | void |
587 | screen_write_cursorup(struct screen_write_ctx *ctx, u_int ny) |
588 | { |
589 | struct screen *s = ctx->s; |
590 | u_int cx = s->cx, cy = s->cy; |
591 | |
592 | if (ny == 0) |
593 | ny = 1; |
594 | |
595 | if (cy < s->rupper) { |
596 | /* Above region. */ |
597 | if (ny > cy) |
598 | ny = cy; |
599 | } else { |
600 | /* Below region. */ |
601 | if (ny > cy - s->rupper) |
602 | ny = cy - s->rupper; |
603 | } |
604 | if (cx == screen_size_x(s)) |
605 | cx--; |
606 | |
607 | cy -= ny; |
608 | |
609 | screen_write_set_cursor(ctx, cx, cy); |
610 | } |
611 | |
612 | /* Cursor down by ny. */ |
613 | void |
614 | screen_write_cursordown(struct screen_write_ctx *ctx, u_int ny) |
615 | { |
616 | struct screen *s = ctx->s; |
617 | u_int cx = s->cx, cy = s->cy; |
618 | |
619 | if (ny == 0) |
620 | ny = 1; |
621 | |
622 | if (cy > s->rlower) { |
623 | /* Below region. */ |
624 | if (ny > screen_size_y(s) - 1 - cy) |
625 | ny = screen_size_y(s) - 1 - cy; |
626 | } else { |
627 | /* Above region. */ |
628 | if (ny > s->rlower - cy) |
629 | ny = s->rlower - cy; |
630 | } |
631 | if (cx == screen_size_x(s)) |
632 | cx--; |
633 | else if (ny == 0) |
634 | return; |
635 | |
636 | cy += ny; |
637 | |
638 | screen_write_set_cursor(ctx, cx, cy); |
639 | } |
640 | |
641 | /* Cursor right by nx. */ |
642 | void |
643 | screen_write_cursorright(struct screen_write_ctx *ctx, u_int nx) |
644 | { |
645 | struct screen *s = ctx->s; |
646 | u_int cx = s->cx, cy = s->cy; |
647 | |
648 | if (nx == 0) |
649 | nx = 1; |
650 | |
651 | if (nx > screen_size_x(s) - 1 - cx) |
652 | nx = screen_size_x(s) - 1 - cx; |
653 | if (nx == 0) |
654 | return; |
655 | |
656 | cx += nx; |
657 | |
658 | screen_write_set_cursor(ctx, cx, cy); |
659 | } |
660 | |
661 | /* Cursor left by nx. */ |
662 | void |
663 | screen_write_cursorleft(struct screen_write_ctx *ctx, u_int nx) |
664 | { |
665 | struct screen *s = ctx->s; |
666 | u_int cx = s->cx, cy = s->cy; |
667 | |
668 | if (nx == 0) |
669 | nx = 1; |
670 | |
671 | if (nx > cx) |
672 | nx = cx; |
673 | if (nx == 0) |
674 | return; |
675 | |
676 | cx -= nx; |
677 | |
678 | screen_write_set_cursor(ctx, cx, cy); |
679 | } |
680 | |
681 | /* Backspace; cursor left unless at start of wrapped line when can move up. */ |
682 | void |
683 | screen_write_backspace(struct screen_write_ctx *ctx) |
684 | { |
685 | struct screen *s = ctx->s; |
686 | struct grid_line *gl; |
687 | u_int cx = s->cx, cy = s->cy; |
688 | |
689 | if (cx == 0) { |
690 | if (cy == 0) |
691 | return; |
692 | gl = grid_get_line(s->grid, s->grid->hsize + cy - 1); |
693 | if (gl->flags & GRID_LINE_WRAPPED) { |
694 | cy--; |
695 | cx = screen_size_x(s) - 1; |
696 | } |
697 | } else |
698 | cx--; |
699 | |
700 | screen_write_set_cursor(ctx, cx, cy); |
701 | } |
702 | |
703 | /* VT100 alignment test. */ |
704 | void |
705 | screen_write_alignmenttest(struct screen_write_ctx *ctx) |
706 | { |
707 | struct screen *s = ctx->s; |
708 | struct tty_ctx ttyctx; |
709 | struct grid_cell gc; |
710 | u_int xx, yy; |
711 | |
712 | memcpy(&gc, &grid_default_cell, sizeof gc); |
713 | utf8_set(&gc.data, 'E'); |
714 | |
715 | for (yy = 0; yy < screen_size_y(s); yy++) { |
716 | for (xx = 0; xx < screen_size_x(s); xx++) |
717 | grid_view_set_cell(s->grid, xx, yy, &gc); |
718 | } |
719 | |
720 | screen_write_set_cursor(ctx, 0, 0); |
721 | |
722 | s->rupper = 0; |
723 | s->rlower = screen_size_y(s) - 1; |
724 | |
725 | screen_write_initctx(ctx, &ttyctx); |
726 | |
727 | screen_write_collect_clear(ctx, 0, screen_size_y(s) - 1); |
728 | tty_write(tty_cmd_alignmenttest, &ttyctx); |
729 | } |
730 | |
731 | /* Insert nx characters. */ |
732 | void |
733 | screen_write_insertcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) |
734 | { |
735 | struct screen *s = ctx->s; |
736 | struct tty_ctx ttyctx; |
737 | |
738 | if (nx == 0) |
739 | nx = 1; |
740 | |
741 | if (nx > screen_size_x(s) - s->cx) |
742 | nx = screen_size_x(s) - s->cx; |
743 | if (nx == 0) |
744 | return; |
745 | |
746 | if (s->cx > screen_size_x(s) - 1) |
747 | return; |
748 | |
749 | screen_write_initctx(ctx, &ttyctx); |
750 | ttyctx.bg = bg; |
751 | |
752 | grid_view_insert_cells(s->grid, s->cx, s->cy, nx, bg); |
753 | |
754 | screen_write_collect_flush(ctx, 0); |
755 | ttyctx.num = nx; |
756 | tty_write(tty_cmd_insertcharacter, &ttyctx); |
757 | } |
758 | |
759 | /* Delete nx characters. */ |
760 | void |
761 | screen_write_deletecharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) |
762 | { |
763 | struct screen *s = ctx->s; |
764 | struct tty_ctx ttyctx; |
765 | |
766 | if (nx == 0) |
767 | nx = 1; |
768 | |
769 | if (nx > screen_size_x(s) - s->cx) |
770 | nx = screen_size_x(s) - s->cx; |
771 | if (nx == 0) |
772 | return; |
773 | |
774 | if (s->cx > screen_size_x(s) - 1) |
775 | return; |
776 | |
777 | screen_write_initctx(ctx, &ttyctx); |
778 | ttyctx.bg = bg; |
779 | |
780 | grid_view_delete_cells(s->grid, s->cx, s->cy, nx, bg); |
781 | |
782 | screen_write_collect_flush(ctx, 0); |
783 | ttyctx.num = nx; |
784 | tty_write(tty_cmd_deletecharacter, &ttyctx); |
785 | } |
786 | |
787 | /* Clear nx characters. */ |
788 | void |
789 | screen_write_clearcharacter(struct screen_write_ctx *ctx, u_int nx, u_int bg) |
790 | { |
791 | struct screen *s = ctx->s; |
792 | struct tty_ctx ttyctx; |
793 | |
794 | if (nx == 0) |
795 | nx = 1; |
796 | |
797 | if (nx > screen_size_x(s) - s->cx) |
798 | nx = screen_size_x(s) - s->cx; |
799 | if (nx == 0) |
800 | return; |
801 | |
802 | if (s->cx > screen_size_x(s) - 1) |
803 | return; |
804 | |
805 | screen_write_initctx(ctx, &ttyctx); |
806 | ttyctx.bg = bg; |
807 | |
808 | grid_view_clear(s->grid, s->cx, s->cy, nx, 1, bg); |
809 | |
810 | screen_write_collect_flush(ctx, 0); |
811 | ttyctx.num = nx; |
812 | tty_write(tty_cmd_clearcharacter, &ttyctx); |
813 | } |
814 | |
815 | /* Insert ny lines. */ |
816 | void |
817 | screen_write_insertline(struct screen_write_ctx *ctx, u_int ny, u_int bg) |
818 | { |
819 | struct screen *s = ctx->s; |
820 | struct grid *gd = s->grid; |
821 | struct tty_ctx ttyctx; |
822 | |
823 | if (ny == 0) |
824 | ny = 1; |
825 | |
826 | if (s->cy < s->rupper || s->cy > s->rlower) { |
827 | if (ny > screen_size_y(s) - s->cy) |
828 | ny = screen_size_y(s) - s->cy; |
829 | if (ny == 0) |
830 | return; |
831 | |
832 | screen_write_initctx(ctx, &ttyctx); |
833 | ttyctx.bg = bg; |
834 | |
835 | grid_view_insert_lines(gd, s->cy, ny, bg); |
836 | |
837 | screen_write_collect_flush(ctx, 0); |
838 | ttyctx.num = ny; |
839 | tty_write(tty_cmd_insertline, &ttyctx); |
840 | return; |
841 | } |
842 | |
843 | if (ny > s->rlower + 1 - s->cy) |
844 | ny = s->rlower + 1 - s->cy; |
845 | if (ny == 0) |
846 | return; |
847 | |
848 | screen_write_initctx(ctx, &ttyctx); |
849 | ttyctx.bg = bg; |
850 | |
851 | if (s->cy < s->rupper || s->cy > s->rlower) |
852 | grid_view_insert_lines(gd, s->cy, ny, bg); |
853 | else |
854 | grid_view_insert_lines_region(gd, s->rlower, s->cy, ny, bg); |
855 | |
856 | screen_write_collect_flush(ctx, 0); |
857 | ttyctx.num = ny; |
858 | tty_write(tty_cmd_insertline, &ttyctx); |
859 | } |
860 | |
861 | /* Delete ny lines. */ |
862 | void |
863 | screen_write_deleteline(struct screen_write_ctx *ctx, u_int ny, u_int bg) |
864 | { |
865 | struct screen *s = ctx->s; |
866 | struct grid *gd = s->grid; |
867 | struct tty_ctx ttyctx; |
868 | |
869 | if (ny == 0) |
870 | ny = 1; |
871 | |
872 | if (s->cy < s->rupper || s->cy > s->rlower) { |
873 | if (ny > screen_size_y(s) - s->cy) |
874 | ny = screen_size_y(s) - s->cy; |
875 | if (ny == 0) |
876 | return; |
877 | |
878 | screen_write_initctx(ctx, &ttyctx); |
879 | ttyctx.bg = bg; |
880 | |
881 | grid_view_delete_lines(gd, s->cy, ny, bg); |
882 | |
883 | screen_write_collect_flush(ctx, 0); |
884 | ttyctx.num = ny; |
885 | tty_write(tty_cmd_deleteline, &ttyctx); |
886 | return; |
887 | } |
888 | |
889 | if (ny > s->rlower + 1 - s->cy) |
890 | ny = s->rlower + 1 - s->cy; |
891 | if (ny == 0) |
892 | return; |
893 | |
894 | screen_write_initctx(ctx, &ttyctx); |
895 | ttyctx.bg = bg; |
896 | |
897 | if (s->cy < s->rupper || s->cy > s->rlower) |
898 | grid_view_delete_lines(gd, s->cy, ny, bg); |
899 | else |
900 | grid_view_delete_lines_region(gd, s->rlower, s->cy, ny, bg); |
901 | |
902 | screen_write_collect_flush(ctx, 0); |
903 | ttyctx.num = ny; |
904 | tty_write(tty_cmd_deleteline, &ttyctx); |
905 | } |
906 | |
907 | /* Clear line at cursor. */ |
908 | void |
909 | screen_write_clearline(struct screen_write_ctx *ctx, u_int bg) |
910 | { |
911 | struct screen *s = ctx->s; |
912 | struct grid_line *gl; |
913 | struct tty_ctx ttyctx; |
914 | u_int sx = screen_size_x(s); |
915 | |
916 | gl = grid_get_line(s->grid, s->grid->hsize + s->cy); |
917 | if (gl->cellsize == 0 && COLOUR_DEFAULT(bg)) |
918 | return; |
919 | |
920 | screen_write_initctx(ctx, &ttyctx); |
921 | ttyctx.bg = bg; |
922 | |
923 | grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); |
924 | |
925 | screen_write_collect_clear(ctx, s->cy, 1); |
926 | screen_write_collect_flush(ctx, 0); |
927 | tty_write(tty_cmd_clearline, &ttyctx); |
928 | } |
929 | |
930 | /* Clear to end of line from cursor. */ |
931 | void |
932 | screen_write_clearendofline(struct screen_write_ctx *ctx, u_int bg) |
933 | { |
934 | struct screen *s = ctx->s; |
935 | struct grid_line *gl; |
936 | struct tty_ctx ttyctx; |
937 | u_int sx = screen_size_x(s); |
938 | |
939 | gl = grid_get_line(s->grid, s->grid->hsize + s->cy); |
940 | if (s->cx > sx - 1 || (s->cx >= gl->cellsize && COLOUR_DEFAULT(bg))) |
941 | return; |
942 | |
943 | screen_write_initctx(ctx, &ttyctx); |
944 | ttyctx.bg = bg; |
945 | |
946 | grid_view_clear(s->grid, s->cx, s->cy, sx - s->cx, 1, bg); |
947 | |
948 | if (s->cx == 0) |
949 | screen_write_collect_clear(ctx, s->cy, 1); |
950 | screen_write_collect_flush(ctx, 0); |
951 | tty_write(tty_cmd_clearendofline, &ttyctx); |
952 | } |
953 | |
954 | /* Clear to start of line from cursor. */ |
955 | void |
956 | screen_write_clearstartofline(struct screen_write_ctx *ctx, u_int bg) |
957 | { |
958 | struct screen *s = ctx->s; |
959 | struct tty_ctx ttyctx; |
960 | u_int sx = screen_size_x(s); |
961 | |
962 | screen_write_initctx(ctx, &ttyctx); |
963 | ttyctx.bg = bg; |
964 | |
965 | if (s->cx > sx - 1) |
966 | grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); |
967 | else |
968 | grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); |
969 | |
970 | if (s->cx > sx - 1) |
971 | screen_write_collect_clear(ctx, s->cy, 1); |
972 | screen_write_collect_flush(ctx, 0); |
973 | tty_write(tty_cmd_clearstartofline, &ttyctx); |
974 | } |
975 | |
976 | /* Move cursor to px,py. */ |
977 | void |
978 | screen_write_cursormove(struct screen_write_ctx *ctx, int px, int py, |
979 | int origin) |
980 | { |
981 | struct screen *s = ctx->s; |
982 | |
983 | if (origin && py != -1 && (s->mode & MODE_ORIGIN)) { |
984 | if ((u_int)py > s->rlower - s->rupper) |
985 | py = s->rlower; |
986 | else |
987 | py += s->rupper; |
988 | } |
989 | |
990 | if (px != -1 && (u_int)px > screen_size_x(s) - 1) |
991 | px = screen_size_x(s) - 1; |
992 | if (py != -1 && (u_int)py > screen_size_y(s) - 1) |
993 | py = screen_size_y(s) - 1; |
994 | |
995 | screen_write_set_cursor(ctx, px, py); |
996 | } |
997 | |
998 | /* Reverse index (up with scroll). */ |
999 | void |
1000 | screen_write_reverseindex(struct screen_write_ctx *ctx, u_int bg) |
1001 | { |
1002 | struct screen *s = ctx->s; |
1003 | struct tty_ctx ttyctx; |
1004 | |
1005 | screen_write_initctx(ctx, &ttyctx); |
1006 | ttyctx.bg = bg; |
1007 | |
1008 | if (s->cy == s->rupper) |
1009 | grid_view_scroll_region_down(s->grid, s->rupper, s->rlower, bg); |
1010 | else if (s->cy > 0) |
1011 | screen_write_set_cursor(ctx, -1, s->cy - 1); |
1012 | |
1013 | screen_write_collect_flush(ctx, 0); |
1014 | tty_write(tty_cmd_reverseindex, &ttyctx); |
1015 | } |
1016 | |
1017 | /* Set scroll region. */ |
1018 | void |
1019 | screen_write_scrollregion(struct screen_write_ctx *ctx, u_int rupper, |
1020 | u_int rlower) |
1021 | { |
1022 | struct screen *s = ctx->s; |
1023 | |
1024 | if (rupper > screen_size_y(s) - 1) |
1025 | rupper = screen_size_y(s) - 1; |
1026 | if (rlower > screen_size_y(s) - 1) |
1027 | rlower = screen_size_y(s) - 1; |
1028 | if (rupper >= rlower) /* cannot be one line */ |
1029 | return; |
1030 | |
1031 | screen_write_collect_flush(ctx, 0); |
1032 | |
1033 | /* Cursor moves to top-left. */ |
1034 | screen_write_set_cursor(ctx, 0, 0); |
1035 | |
1036 | s->rupper = rupper; |
1037 | s->rlower = rlower; |
1038 | } |
1039 | |
1040 | /* Line feed. */ |
1041 | void |
1042 | screen_write_linefeed(struct screen_write_ctx *ctx, int wrapped, u_int bg) |
1043 | { |
1044 | struct screen *s = ctx->s; |
1045 | struct grid *gd = s->grid; |
1046 | struct grid_line *gl; |
1047 | |
1048 | gl = grid_get_line(gd, gd->hsize + s->cy); |
1049 | if (wrapped) |
1050 | gl->flags |= GRID_LINE_WRAPPED; |
1051 | else |
1052 | gl->flags &= ~GRID_LINE_WRAPPED; |
1053 | |
1054 | log_debug("%s: at %u,%u (region %u-%u)" , __func__, s->cx, s->cy, |
1055 | s->rupper, s->rlower); |
1056 | |
1057 | if (bg != ctx->bg) { |
1058 | screen_write_collect_flush(ctx, 1); |
1059 | ctx->bg = bg; |
1060 | } |
1061 | |
1062 | if (s->cy == s->rlower) { |
1063 | grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); |
1064 | screen_write_collect_scroll(ctx); |
1065 | ctx->scrolled++; |
1066 | } else if (s->cy < screen_size_y(s) - 1) |
1067 | screen_write_set_cursor(ctx, -1, s->cy + 1); |
1068 | } |
1069 | |
1070 | /* Scroll up. */ |
1071 | void |
1072 | screen_write_scrollup(struct screen_write_ctx *ctx, u_int lines, u_int bg) |
1073 | { |
1074 | struct screen *s = ctx->s; |
1075 | struct grid *gd = s->grid; |
1076 | u_int i; |
1077 | |
1078 | if (lines == 0) |
1079 | lines = 1; |
1080 | else if (lines > s->rlower - s->rupper + 1) |
1081 | lines = s->rlower - s->rupper + 1; |
1082 | |
1083 | if (bg != ctx->bg) { |
1084 | screen_write_collect_flush(ctx, 1); |
1085 | ctx->bg = bg; |
1086 | } |
1087 | |
1088 | for (i = 0; i < lines; i++) { |
1089 | grid_view_scroll_region_up(gd, s->rupper, s->rlower, bg); |
1090 | screen_write_collect_scroll(ctx); |
1091 | } |
1092 | ctx->scrolled += lines; |
1093 | } |
1094 | |
1095 | /* Scroll down. */ |
1096 | void |
1097 | screen_write_scrolldown(struct screen_write_ctx *ctx, u_int lines, u_int bg) |
1098 | { |
1099 | struct screen *s = ctx->s; |
1100 | struct grid *gd = s->grid; |
1101 | struct tty_ctx ttyctx; |
1102 | u_int i; |
1103 | |
1104 | screen_write_initctx(ctx, &ttyctx); |
1105 | ttyctx.bg = bg; |
1106 | |
1107 | if (lines == 0) |
1108 | lines = 1; |
1109 | else if (lines > s->rlower - s->rupper + 1) |
1110 | lines = s->rlower - s->rupper + 1; |
1111 | |
1112 | for (i = 0; i < lines; i++) |
1113 | grid_view_scroll_region_down(gd, s->rupper, s->rlower, bg); |
1114 | |
1115 | screen_write_collect_flush(ctx, 0); |
1116 | ttyctx.num = lines; |
1117 | tty_write(tty_cmd_scrolldown, &ttyctx); |
1118 | } |
1119 | |
1120 | /* Carriage return (cursor to start of line). */ |
1121 | void |
1122 | screen_write_carriagereturn(struct screen_write_ctx *ctx) |
1123 | { |
1124 | screen_write_set_cursor(ctx, 0, -1); |
1125 | } |
1126 | |
1127 | /* Clear to end of screen from cursor. */ |
1128 | void |
1129 | screen_write_clearendofscreen(struct screen_write_ctx *ctx, u_int bg) |
1130 | { |
1131 | struct screen *s = ctx->s; |
1132 | struct grid *gd = s->grid; |
1133 | struct tty_ctx ttyctx; |
1134 | u_int sx = screen_size_x(s), sy = screen_size_y(s); |
1135 | |
1136 | screen_write_initctx(ctx, &ttyctx); |
1137 | ttyctx.bg = bg; |
1138 | |
1139 | /* Scroll into history if it is enabled and clearing entire screen. */ |
1140 | if (s->cx == 0 && s->cy == 0 && (gd->flags & GRID_HISTORY)) |
1141 | grid_view_clear_history(gd, bg); |
1142 | else { |
1143 | if (s->cx <= sx - 1) |
1144 | grid_view_clear(gd, s->cx, s->cy, sx - s->cx, 1, bg); |
1145 | grid_view_clear(gd, 0, s->cy + 1, sx, sy - (s->cy + 1), bg); |
1146 | } |
1147 | |
1148 | screen_write_collect_clear(ctx, s->cy + 1, sy - (s->cy + 1)); |
1149 | screen_write_collect_flush(ctx, 0); |
1150 | tty_write(tty_cmd_clearendofscreen, &ttyctx); |
1151 | } |
1152 | |
1153 | /* Clear to start of screen. */ |
1154 | void |
1155 | screen_write_clearstartofscreen(struct screen_write_ctx *ctx, u_int bg) |
1156 | { |
1157 | struct screen *s = ctx->s; |
1158 | struct tty_ctx ttyctx; |
1159 | u_int sx = screen_size_x(s); |
1160 | |
1161 | screen_write_initctx(ctx, &ttyctx); |
1162 | ttyctx.bg = bg; |
1163 | |
1164 | if (s->cy > 0) |
1165 | grid_view_clear(s->grid, 0, 0, sx, s->cy, bg); |
1166 | if (s->cx > sx - 1) |
1167 | grid_view_clear(s->grid, 0, s->cy, sx, 1, bg); |
1168 | else |
1169 | grid_view_clear(s->grid, 0, s->cy, s->cx + 1, 1, bg); |
1170 | |
1171 | screen_write_collect_clear(ctx, 0, s->cy); |
1172 | screen_write_collect_flush(ctx, 0); |
1173 | tty_write(tty_cmd_clearstartofscreen, &ttyctx); |
1174 | } |
1175 | |
1176 | /* Clear entire screen. */ |
1177 | void |
1178 | screen_write_clearscreen(struct screen_write_ctx *ctx, u_int bg) |
1179 | { |
1180 | struct screen *s = ctx->s; |
1181 | struct tty_ctx ttyctx; |
1182 | u_int sx = screen_size_x(s), sy = screen_size_y(s); |
1183 | |
1184 | screen_write_initctx(ctx, &ttyctx); |
1185 | ttyctx.bg = bg; |
1186 | |
1187 | /* Scroll into history if it is enabled. */ |
1188 | if (s->grid->flags & GRID_HISTORY) |
1189 | grid_view_clear_history(s->grid, bg); |
1190 | else |
1191 | grid_view_clear(s->grid, 0, 0, sx, sy, bg); |
1192 | |
1193 | screen_write_collect_clear(ctx, 0, sy); |
1194 | tty_write(tty_cmd_clearscreen, &ttyctx); |
1195 | } |
1196 | |
1197 | /* Clear entire history. */ |
1198 | void |
1199 | screen_write_clearhistory(struct screen_write_ctx *ctx) |
1200 | { |
1201 | grid_clear_history(ctx->s->grid); |
1202 | } |
1203 | |
1204 | /* Clear a collected line. */ |
1205 | static void |
1206 | screen_write_collect_clear(struct screen_write_ctx *ctx, u_int y, u_int n) |
1207 | { |
1208 | struct screen_write_collect_item *ci, *tmp; |
1209 | u_int i; |
1210 | size_t size; |
1211 | |
1212 | for (i = y; i < y + n; i++) { |
1213 | if (TAILQ_EMPTY(&ctx->list[i].items)) |
1214 | continue; |
1215 | size = 0; |
1216 | TAILQ_FOREACH_SAFE(ci, &ctx->list[i].items, entry, tmp) { |
1217 | size += ci->used; |
1218 | TAILQ_REMOVE(&ctx->list[i].items, ci, entry); |
1219 | free(ci); |
1220 | } |
1221 | ctx->skipped += size; |
1222 | log_debug("%s: dropped %zu bytes (line %u)" , __func__, size, i); |
1223 | } |
1224 | } |
1225 | |
1226 | /* Scroll collected lines up. */ |
1227 | static void |
1228 | screen_write_collect_scroll(struct screen_write_ctx *ctx) |
1229 | { |
1230 | struct screen *s = ctx->s; |
1231 | struct screen_write_collect_line *cl; |
1232 | u_int y; |
1233 | |
1234 | log_debug("%s: at %u,%u (region %u-%u)" , __func__, s->cx, s->cy, |
1235 | s->rupper, s->rlower); |
1236 | |
1237 | screen_write_collect_clear(ctx, s->rupper, 1); |
1238 | for (y = s->rupper; y < s->rlower; y++) { |
1239 | cl = &ctx->list[y + 1]; |
1240 | TAILQ_CONCAT(&ctx->list[y].items, &cl->items, entry); |
1241 | } |
1242 | } |
1243 | |
1244 | /* Flush collected lines. */ |
1245 | static void |
1246 | screen_write_collect_flush(struct screen_write_ctx *ctx, int scroll_only) |
1247 | { |
1248 | struct screen *s = ctx->s; |
1249 | struct screen_write_collect_item *ci, *tmp; |
1250 | u_int y, cx, cy, items = 0; |
1251 | struct tty_ctx ttyctx; |
1252 | size_t written = 0; |
1253 | |
1254 | if (ctx->scrolled != 0) { |
1255 | log_debug("%s: scrolled %u (region %u-%u)" , __func__, |
1256 | ctx->scrolled, s->rupper, s->rlower); |
1257 | if (ctx->scrolled > s->rlower - s->rupper + 1) |
1258 | ctx->scrolled = s->rlower - s->rupper + 1; |
1259 | |
1260 | screen_write_initctx(ctx, &ttyctx); |
1261 | ttyctx.num = ctx->scrolled; |
1262 | ttyctx.bg = ctx->bg; |
1263 | tty_write(tty_cmd_scrollup, &ttyctx); |
1264 | } |
1265 | ctx->scrolled = 0; |
1266 | ctx->bg = 8; |
1267 | |
1268 | if (scroll_only) |
1269 | return; |
1270 | |
1271 | cx = s->cx; cy = s->cy; |
1272 | for (y = 0; y < screen_size_y(s); y++) { |
1273 | TAILQ_FOREACH_SAFE(ci, &ctx->list[y].items, entry, tmp) { |
1274 | screen_write_set_cursor(ctx, ci->x, y); |
1275 | screen_write_initctx(ctx, &ttyctx); |
1276 | ttyctx.cell = &ci->gc; |
1277 | ttyctx.wrapped = ci->wrapped; |
1278 | ttyctx.ptr = ci->data; |
1279 | ttyctx.num = ci->used; |
1280 | tty_write(tty_cmd_cells, &ttyctx); |
1281 | |
1282 | items++; |
1283 | written += ci->used; |
1284 | |
1285 | TAILQ_REMOVE(&ctx->list[y].items, ci, entry); |
1286 | free(ci); |
1287 | } |
1288 | } |
1289 | s->cx = cx; s->cy = cy; |
1290 | |
1291 | log_debug("%s: flushed %u items (%zu bytes)" , __func__, items, written); |
1292 | ctx->written += written; |
1293 | } |
1294 | |
1295 | /* Finish and store collected cells. */ |
1296 | void |
1297 | screen_write_collect_end(struct screen_write_ctx *ctx) |
1298 | { |
1299 | struct screen *s = ctx->s; |
1300 | struct screen_write_collect_item *ci = ctx->item; |
1301 | struct grid_cell gc; |
1302 | u_int xx; |
1303 | |
1304 | if (ci->used == 0) |
1305 | return; |
1306 | ci->data[ci->used] = '\0'; |
1307 | |
1308 | ci->x = s->cx; |
1309 | TAILQ_INSERT_TAIL(&ctx->list[s->cy].items, ci, entry); |
1310 | ctx->item = xcalloc(1, sizeof *ctx->item); |
1311 | |
1312 | log_debug("%s: %u %s (at %u,%u)" , __func__, ci->used, ci->data, s->cx, |
1313 | s->cy); |
1314 | |
1315 | if (s->cx != 0) { |
1316 | for (xx = s->cx; xx > 0; xx--) { |
1317 | grid_view_get_cell(s->grid, xx, s->cy, &gc); |
1318 | if (~gc.flags & GRID_FLAG_PADDING) |
1319 | break; |
1320 | grid_view_set_cell(s->grid, xx, s->cy, |
1321 | &grid_default_cell); |
1322 | } |
1323 | if (gc.data.width > 1) { |
1324 | grid_view_set_cell(s->grid, xx, s->cy, |
1325 | &grid_default_cell); |
1326 | } |
1327 | } |
1328 | |
1329 | grid_view_set_cells(s->grid, s->cx, s->cy, &ci->gc, ci->data, ci->used); |
1330 | screen_write_set_cursor(ctx, s->cx + ci->used, -1); |
1331 | |
1332 | for (xx = s->cx; xx < screen_size_x(s); xx++) { |
1333 | grid_view_get_cell(s->grid, xx, s->cy, &gc); |
1334 | if (~gc.flags & GRID_FLAG_PADDING) |
1335 | break; |
1336 | grid_view_set_cell(s->grid, xx, s->cy, &grid_default_cell); |
1337 | } |
1338 | } |
1339 | |
1340 | /* Write cell data, collecting if necessary. */ |
1341 | void |
1342 | screen_write_collect_add(struct screen_write_ctx *ctx, |
1343 | const struct grid_cell *gc) |
1344 | { |
1345 | struct screen *s = ctx->s; |
1346 | struct screen_write_collect_item *ci; |
1347 | u_int sx = screen_size_x(s); |
1348 | int collect; |
1349 | |
1350 | /* |
1351 | * Don't need to check that the attributes and whatnot are still the |
1352 | * same - input_parse will end the collection when anything that isn't |
1353 | * a plain character is encountered. |
1354 | */ |
1355 | |
1356 | collect = 1; |
1357 | if (gc->data.width != 1 || gc->data.size != 1 || *gc->data.data >= 0x7f) |
1358 | collect = 0; |
1359 | else if (gc->attr & GRID_ATTR_CHARSET) |
1360 | collect = 0; |
1361 | else if (~s->mode & MODE_WRAP) |
1362 | collect = 0; |
1363 | else if (s->mode & MODE_INSERT) |
1364 | collect = 0; |
1365 | else if (s->sel != NULL) |
1366 | collect = 0; |
1367 | if (!collect) { |
1368 | screen_write_collect_end(ctx); |
1369 | screen_write_collect_flush(ctx, 0); |
1370 | screen_write_cell(ctx, gc); |
1371 | return; |
1372 | } |
1373 | ctx->cells++; |
1374 | |
1375 | if (s->cx > sx - 1 || ctx->item->used > sx - 1 - s->cx) |
1376 | screen_write_collect_end(ctx); |
1377 | ci = ctx->item; /* may have changed */ |
1378 | |
1379 | if (s->cx > sx - 1) { |
1380 | log_debug("%s: wrapped at %u,%u" , __func__, s->cx, s->cy); |
1381 | ci->wrapped = 1; |
1382 | screen_write_linefeed(ctx, 1, 8); |
1383 | screen_write_set_cursor(ctx, 0, -1); |
1384 | } |
1385 | |
1386 | if (ci->used == 0) |
1387 | memcpy(&ci->gc, gc, sizeof ci->gc); |
1388 | ci->data[ci->used++] = gc->data.data[0]; |
1389 | if (ci->used == (sizeof ci->data) - 1) |
1390 | screen_write_collect_end(ctx); |
1391 | } |
1392 | |
1393 | /* Write cell data. */ |
1394 | void |
1395 | screen_write_cell(struct screen_write_ctx *ctx, const struct grid_cell *gc) |
1396 | { |
1397 | struct screen *s = ctx->s; |
1398 | struct grid *gd = s->grid; |
1399 | struct grid_line *gl; |
1400 | struct grid_cell_entry *gce; |
1401 | struct grid_cell tmp_gc, now_gc; |
1402 | struct tty_ctx ttyctx; |
1403 | u_int sx = screen_size_x(s), sy = screen_size_y(s); |
1404 | u_int width = gc->data.width, xx, last, cx, cy; |
1405 | int selected, skip = 1; |
1406 | |
1407 | /* Ignore padding cells. */ |
1408 | if (gc->flags & GRID_FLAG_PADDING) |
1409 | return; |
1410 | ctx->cells++; |
1411 | |
1412 | /* If the width is zero, combine onto the previous character. */ |
1413 | if (width == 0) { |
1414 | screen_write_collect_flush(ctx, 0); |
1415 | if ((gc = screen_write_combine(ctx, &gc->data, &xx)) != 0) { |
1416 | cx = s->cx; cy = s->cy; |
1417 | screen_write_set_cursor(ctx, xx, s->cy); |
1418 | screen_write_initctx(ctx, &ttyctx); |
1419 | ttyctx.cell = gc; |
1420 | tty_write(tty_cmd_cell, &ttyctx); |
1421 | s->cx = cx; s->cy = cy; |
1422 | } |
1423 | return; |
1424 | } |
1425 | |
1426 | /* Flush any existing scrolling. */ |
1427 | screen_write_collect_flush(ctx, 1); |
1428 | |
1429 | /* If this character doesn't fit, ignore it. */ |
1430 | if ((~s->mode & MODE_WRAP) && |
1431 | width > 1 && |
1432 | (width > sx || (s->cx != sx && s->cx > sx - width))) |
1433 | return; |
1434 | |
1435 | /* If in insert mode, make space for the cells. */ |
1436 | if (s->mode & MODE_INSERT) { |
1437 | grid_view_insert_cells(s->grid, s->cx, s->cy, width, 8); |
1438 | skip = 0; |
1439 | } |
1440 | |
1441 | /* Check this will fit on the current line and wrap if not. */ |
1442 | if ((s->mode & MODE_WRAP) && s->cx > sx - width) { |
1443 | log_debug("%s: wrapped at %u,%u" , __func__, s->cx, s->cy); |
1444 | screen_write_linefeed(ctx, 1, 8); |
1445 | screen_write_set_cursor(ctx, 0, -1); |
1446 | screen_write_collect_flush(ctx, 1); |
1447 | } |
1448 | |
1449 | /* Sanity check cursor position. */ |
1450 | if (s->cx > sx - width || s->cy > sy - 1) |
1451 | return; |
1452 | screen_write_initctx(ctx, &ttyctx); |
1453 | |
1454 | /* Handle overwriting of UTF-8 characters. */ |
1455 | gl = grid_get_line(s->grid, s->grid->hsize + s->cy); |
1456 | if (gl->flags & GRID_LINE_EXTENDED) { |
1457 | grid_view_get_cell(gd, s->cx, s->cy, &now_gc); |
1458 | if (screen_write_overwrite(ctx, &now_gc, width)) |
1459 | skip = 0; |
1460 | } |
1461 | |
1462 | /* |
1463 | * If the new character is UTF-8 wide, fill in padding cells. Have |
1464 | * already ensured there is enough room. |
1465 | */ |
1466 | for (xx = s->cx + 1; xx < s->cx + width; xx++) { |
1467 | log_debug("%s: new padding at %u,%u" , __func__, xx, s->cy); |
1468 | grid_view_set_cell(gd, xx, s->cy, &screen_write_pad_cell); |
1469 | skip = 0; |
1470 | } |
1471 | |
1472 | /* If no change, do not draw. */ |
1473 | if (skip) { |
1474 | if (s->cx >= gl->cellsize) |
1475 | skip = grid_cells_equal(gc, &grid_default_cell); |
1476 | else { |
1477 | gce = &gl->celldata[s->cx]; |
1478 | if (gce->flags & GRID_FLAG_EXTENDED) |
1479 | skip = 0; |
1480 | else if (gc->flags != gce->flags) |
1481 | skip = 0; |
1482 | else if (gc->attr != gce->data.attr) |
1483 | skip = 0; |
1484 | else if (gc->fg != gce->data.fg) |
1485 | skip = 0; |
1486 | else if (gc->bg != gce->data.bg) |
1487 | skip = 0; |
1488 | else if (gc->data.width != 1) |
1489 | skip = 0; |
1490 | else if (gc->data.size != 1) |
1491 | skip = 0; |
1492 | else if (gce->data.data != gc->data.data[0]) |
1493 | skip = 0; |
1494 | } |
1495 | } |
1496 | |
1497 | /* Update the selected flag and set the cell. */ |
1498 | selected = screen_check_selection(s, s->cx, s->cy); |
1499 | if (selected && (~gc->flags & GRID_FLAG_SELECTED)) { |
1500 | memcpy(&tmp_gc, gc, sizeof tmp_gc); |
1501 | tmp_gc.flags |= GRID_FLAG_SELECTED; |
1502 | grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); |
1503 | } else if (!selected && (gc->flags & GRID_FLAG_SELECTED)) { |
1504 | memcpy(&tmp_gc, gc, sizeof tmp_gc); |
1505 | tmp_gc.flags &= ~GRID_FLAG_SELECTED; |
1506 | grid_view_set_cell(gd, s->cx, s->cy, &tmp_gc); |
1507 | } else if (!skip) |
1508 | grid_view_set_cell(gd, s->cx, s->cy, gc); |
1509 | if (selected) |
1510 | skip = 0; |
1511 | |
1512 | /* |
1513 | * Move the cursor. If not wrapping, stick at the last character and |
1514 | * replace it. |
1515 | */ |
1516 | last = !(s->mode & MODE_WRAP); |
1517 | if (s->cx <= sx - last - width) |
1518 | screen_write_set_cursor(ctx, s->cx + width, -1); |
1519 | else |
1520 | screen_write_set_cursor(ctx, sx - last, -1); |
1521 | |
1522 | /* Create space for character in insert mode. */ |
1523 | if (s->mode & MODE_INSERT) { |
1524 | screen_write_collect_flush(ctx, 0); |
1525 | ttyctx.num = width; |
1526 | tty_write(tty_cmd_insertcharacter, &ttyctx); |
1527 | } |
1528 | |
1529 | /* Write to the screen. */ |
1530 | if (!skip) { |
1531 | if (selected) { |
1532 | screen_select_cell(s, &tmp_gc, gc); |
1533 | ttyctx.cell = &tmp_gc; |
1534 | } else |
1535 | ttyctx.cell = gc; |
1536 | tty_write(tty_cmd_cell, &ttyctx); |
1537 | ctx->written++; |
1538 | } else |
1539 | ctx->skipped++; |
1540 | } |
1541 | |
1542 | /* Combine a UTF-8 zero-width character onto the previous. */ |
1543 | static const struct grid_cell * |
1544 | screen_write_combine(struct screen_write_ctx *ctx, const struct utf8_data *ud, |
1545 | u_int *xx) |
1546 | { |
1547 | struct screen *s = ctx->s; |
1548 | struct grid *gd = s->grid; |
1549 | static struct grid_cell gc; |
1550 | u_int n; |
1551 | |
1552 | /* Can't combine if at 0. */ |
1553 | if (s->cx == 0) |
1554 | return (NULL); |
1555 | |
1556 | /* Empty data is out. */ |
1557 | if (ud->size == 0) |
1558 | fatalx("UTF-8 data empty" ); |
1559 | |
1560 | /* Retrieve the previous cell. */ |
1561 | for (n = 1; n <= s->cx; n++) { |
1562 | grid_view_get_cell(gd, s->cx - n, s->cy, &gc); |
1563 | if (~gc.flags & GRID_FLAG_PADDING) |
1564 | break; |
1565 | } |
1566 | if (n > s->cx) |
1567 | return (NULL); |
1568 | *xx = s->cx - n; |
1569 | |
1570 | /* Check there is enough space. */ |
1571 | if (gc.data.size + ud->size > sizeof gc.data.data) |
1572 | return (NULL); |
1573 | |
1574 | log_debug("%s: %.*s onto %.*s at %u,%u" , __func__, (int)ud->size, |
1575 | ud->data, (int)gc.data.size, gc.data.data, *xx, s->cy); |
1576 | |
1577 | /* Append the data. */ |
1578 | memcpy(gc.data.data + gc.data.size, ud->data, ud->size); |
1579 | gc.data.size += ud->size; |
1580 | |
1581 | /* Set the new cell. */ |
1582 | grid_view_set_cell(gd, *xx, s->cy, &gc); |
1583 | |
1584 | return (&gc); |
1585 | } |
1586 | |
1587 | /* |
1588 | * UTF-8 wide characters are a bit of an annoyance. They take up more than one |
1589 | * cell on the screen, so following cells must not be drawn by marking them as |
1590 | * padding. |
1591 | * |
1592 | * So far, so good. The problem is, when overwriting a padding cell, or a UTF-8 |
1593 | * character, it is necessary to also overwrite any other cells which covered |
1594 | * by the same character. |
1595 | */ |
1596 | static int |
1597 | screen_write_overwrite(struct screen_write_ctx *ctx, struct grid_cell *gc, |
1598 | u_int width) |
1599 | { |
1600 | struct screen *s = ctx->s; |
1601 | struct grid *gd = s->grid; |
1602 | struct grid_cell tmp_gc; |
1603 | u_int xx; |
1604 | int done = 0; |
1605 | |
1606 | if (gc->flags & GRID_FLAG_PADDING) { |
1607 | /* |
1608 | * A padding cell, so clear any following and leading padding |
1609 | * cells back to the character. Don't overwrite the current |
1610 | * cell as that happens later anyway. |
1611 | */ |
1612 | xx = s->cx + 1; |
1613 | while (--xx > 0) { |
1614 | grid_view_get_cell(gd, xx, s->cy, &tmp_gc); |
1615 | if (~tmp_gc.flags & GRID_FLAG_PADDING) |
1616 | break; |
1617 | log_debug("%s: padding at %u,%u" , __func__, xx, s->cy); |
1618 | grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); |
1619 | } |
1620 | |
1621 | /* Overwrite the character at the start of this padding. */ |
1622 | log_debug("%s: character at %u,%u" , __func__, xx, s->cy); |
1623 | grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); |
1624 | done = 1; |
1625 | } |
1626 | |
1627 | /* |
1628 | * Overwrite any padding cells that belong to any UTF-8 characters |
1629 | * we'll be overwriting with the current character. |
1630 | */ |
1631 | if (width != 1 || |
1632 | gc->data.width != 1 || |
1633 | gc->flags & GRID_FLAG_PADDING) { |
1634 | xx = s->cx + width - 1; |
1635 | while (++xx < screen_size_x(s)) { |
1636 | grid_view_get_cell(gd, xx, s->cy, &tmp_gc); |
1637 | if (~tmp_gc.flags & GRID_FLAG_PADDING) |
1638 | break; |
1639 | log_debug("%s: overwrite at %u,%u" , __func__, xx, |
1640 | s->cy); |
1641 | grid_view_set_cell(gd, xx, s->cy, &grid_default_cell); |
1642 | done = 1; |
1643 | } |
1644 | } |
1645 | |
1646 | return (done); |
1647 | } |
1648 | |
1649 | /* Set external clipboard. */ |
1650 | void |
1651 | screen_write_setselection(struct screen_write_ctx *ctx, u_char *str, u_int len) |
1652 | { |
1653 | struct tty_ctx ttyctx; |
1654 | |
1655 | screen_write_initctx(ctx, &ttyctx); |
1656 | ttyctx.ptr = str; |
1657 | ttyctx.num = len; |
1658 | |
1659 | tty_write(tty_cmd_setselection, &ttyctx); |
1660 | } |
1661 | |
1662 | /* Write unmodified string. */ |
1663 | void |
1664 | screen_write_rawstring(struct screen_write_ctx *ctx, u_char *str, u_int len) |
1665 | { |
1666 | struct tty_ctx ttyctx; |
1667 | |
1668 | screen_write_initctx(ctx, &ttyctx); |
1669 | ttyctx.ptr = str; |
1670 | ttyctx.num = len; |
1671 | |
1672 | tty_write(tty_cmd_rawstring, &ttyctx); |
1673 | } |
1674 | |