added "amber tint" mode for selection (default)
[yterm.git] / src / cellwin.c
blobb0150f09b0adc93f48e72c9646a252e9794cf5e8
1 /*
2 * SXED -- sexy text editor engine, 2022
4 * Config directories
6 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
7 * Understanding is not required. Only obedience.
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation, version 3 of the License ONLY.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "cellwin.h"
22 #include "utf.h"
24 #include <wchar.h>
25 #include <unistd.h>
28 // ////////////////////////////////////////////////////////////////////////// //
29 //#define SLOW_DIRTY_CHECKS
30 //#define SCROLL_DIRTY_CHECKS
32 #ifdef SLOW_DIRTY_CHECKS
33 # define CHECK_DIRTY(cbf_) do { check_dirty_count(cbf_, __LINE__); } while (0)
34 #else
35 # define CHECK_DIRTY(cbf_) ((void)0)
36 #endif
39 // ////////////////////////////////////////////////////////////////////////// //
40 #define IS_DIRTY(cell_) ((cell_)->flags & CELL_DIRTY)
42 #define CLIP_REGION() \
43 if (cbuf == NULL) return; \
44 if (x1 < 0 || y1 < 0 || x0 > x1 || y0 > y1) return; \
45 const int cbwdt = cbuf->width; \
46 if (x0 >= cbwdt) return; \
47 const int cbhgt = cbuf->height; \
48 if (y0 >= cbhgt) return; \
49 if (x0 < 0) x0 = 0; \
50 if (y0 < 0) y0 = 0; \
51 if (x1 >= cbwdt) x1 = cbwdt - 1; \
52 if (y1 >= cbhgt) y1 = cbhgt - 1; \
53 yterm_assert(x0 <= x1 && y0 <= y1); \
54 do {} while (0);
56 #define LOAD_ATTRS() \
57 uint16_t flags; \
58 uint32_t fg, bg; \
59 do { \
60 if (attr != NULL) { \
61 flags = (attr->flags | CELL_DIRTY) & (~CELL_AUTO_WRAP); \
62 fg = attr->fg; bg = attr->bg; \
63 } else { \
64 flags = CELL_DIRTY; \
65 fg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR; \
66 bg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR; \
67 } \
68 } while (0)
70 #define DIFF_CELL(cc_,ch_) (\
71 ((cc_)->ch ^ (uint32_t)(ch_)) | \
72 ((cc_)->fg ^ fg) | \
73 ((cc_)->bg ^ bg) | \
74 (((cc_)->flags ^ flags) & ~CELL_AUTO_WRAP) \
77 #define DIFF_TWO_CELLS(cc0_,cc1_) (\
78 ((cc0_)->ch ^ (cc1_)->ch) | \
79 ((cc0_)->fg ^ (cc1_)->fg) | \
80 ((cc0_)->bg ^ (cc1_)->bg) | \
81 (((cc0_)->flags ^ (cc1_)->flags) & ~(CELL_AUTO_WRAP|CELL_DIRTY)) \
85 // ////////////////////////////////////////////////////////////////////////// //
86 typedef struct {
87 int w, h;
88 int x, y;
89 int addr;
90 CharCell *buf;
91 } LineIt;
94 // ////////////////////////////////////////////////////////////////////////// //
95 int colorMode = CMODE_NORMAL;
96 yterm_bool enableUnderline = 1;
97 yterm_bool cbufInverse = 0;
100 // ////////////////////////////////////////////////////////////////////////// //
101 uint32_t colorsTTY[CIDX_MAX + 1];
102 uint32_t colorSelectionFG = 0xffffff;
103 uint32_t colorSelectionBG = 0x0055cc;
104 uint32_t colorAmberFG = 0xffaa00;
105 uint32_t colorAmberBG = 0x333333;
108 //==========================================================================
110 // cbuf_init_palette
112 // init default palette
114 //==========================================================================
115 void cbuf_init_palette (void) {
116 const uint32_t defclr16 [16] = {
117 // 8 normal colors
118 0x000000,
119 0xb21818,
120 0x18b218,
121 0xb26818,
122 0x1818b2,
123 0xb218b2,
124 0x18b2b2,
125 0xb2b2b2,
126 // 8 bright colors
127 0x686868,
128 0xff5454,
129 0x54ff54,
130 0xffff54,
131 0x5454ff,
132 0xff54ff,
133 0x54ffff,
134 0xffffff,
137 memcpy(colorsTTY, defclr16, 16 * sizeof(colorsTTY[0]));
139 colorsTTY[CIDX_DEFAULT_FG] = 0xb2b2b2;
140 colorsTTY[CIDX_DEFAULT_BG] = 0x000000;
141 colorsTTY[CIDX_DEFAULT_HIGH_FG] = 0xffffff;
142 colorsTTY[CIDX_DEFAULT_HIGH_BG] = 0x000000;
144 colorsTTY[CIDX_BOLD_FG] = 0x00afaf;
145 colorsTTY[CIDX_UNDER_FG] = 0x00af00;
146 colorsTTY[CIDX_BOLD_UNDER_FG] = 0xafaf00;
148 colorsTTY[CIDX_BOLD_HIGH_FG] = 0x00ffff;
149 colorsTTY[CIDX_UNDER_HIGH_FG] = 0x44ff00;
150 colorsTTY[CIDX_BOLD_UNDER_HIGH_FG] = 0xffff44;
152 // xterm 256 color palette
153 int cidx = 16;
154 for (uint32_t r = 0; r < 6; r += 1) {
155 for (uint32_t g = 0; g < 6; g += 1) {
156 for (uint32_t b = 0; b < 6; b += 1) {
157 uint32_t c = 0;
158 if (r) c += (0x37 + 0x28 * r) << 16;
159 if (g) c += (0x37 + 0x28 * g) << 8;
160 if (b) c += (0x37 + 0x28 * b);
161 colorsTTY[cidx] = c;
162 cidx += 1;
166 yterm_assert(cidx == 232);
167 // 24 grayscale colors
168 for (uint32_t gs = 0; gs < 24; gs += 1) {
169 uint32_t c = 0x08 + 0x0a * gs;
170 c = c | (c << 8) | (c << 16);
171 colorsTTY[cidx] = c;
172 cidx += 1;
174 yterm_assert(cidx == 256);
178 //==========================================================================
180 // check_dirty_count
182 //==========================================================================
183 static __attribute__((unused)) void check_dirty_count (const CellBuffer *cbuf, const int line) {
184 if (cbuf) {
185 int dc = 0;
186 const CharCell *cc = cbuf->buf;
187 int cnt = cbuf->width * cbuf->height;
188 while (cnt != 0) {
189 cnt -= 1;
190 if (IS_DIRTY(cc)) dc += 1;
191 cc += 1;
193 if (dc != cbuf->dirtyCount) {
194 fprintf(stderr, "DIRTY COUNT ERROR at line %d: calc=%d, stored=%d\n",
195 line, dc, cbuf->dirtyCount);
196 yterm_assert(0);
202 //==========================================================================
204 // it_init
206 //==========================================================================
207 YTERM_STATIC_INLINE void it_init (LineIt *it, CellBuffer *cbuf) {
208 it->w = cbuf->width;
209 it->h = cbuf->height;
210 it->x = 0;
211 it->y = 0;
212 it->addr = 0;
213 it->buf = cbuf->buf;
217 //==========================================================================
219 // it_is_eot
221 //==========================================================================
222 YTERM_STATIC_INLINE yterm_bool it_is_eot (const LineIt *it) {
223 return (it->y == it->h);
227 //==========================================================================
229 // it_get
231 // get next char
232 // returns `NULL` on end of buffer, or on EOL
234 //==========================================================================
235 YTERM_STATIC_INLINE const CharCell *it_get (LineIt *it) {
236 const CharCell *res = NULL;
237 if (it->y != it->h) {
238 if (it->x == it->w) {
239 it->x = 0; it->y += 1;
240 } else {
241 res = &it->buf[it->addr];
242 it->x += 1; it->addr += 1;
243 // if last in line, check for autowraping
244 if (it->x == it->w && (res->flags & CELL_AUTO_WRAP) != 0) {
245 // autowrap, move to the next line
246 it->x = 0; it->y += 1;
250 return res;
254 //==========================================================================
256 // decode_color
258 //==========================================================================
259 YTERM_STATIC_INLINE uint32_t decode_color (uint32_t c, uint16_t flags,
260 yterm_bool *under, yterm_bool isfg)
262 if (c & CELL_ATTR_MASK) {
263 // predefined terminal colors
264 c &= ~CELL_ATTR_MASK;
265 if (c > 255) c = CIDX_DEFAULT_BG - isfg; // default attribute
267 yterm_bool high = ((flags & CELL_BLINK) != 0); // high intensity?
269 if (isfg) {
270 if ((flags & CELL_BOLD) != 0) { // bold
271 // either high intensity color, or special bold color
272 if (c > 255) {
273 // default color, transform to special
274 if ((flags & CELL_UNDERLINE) != 0) {
275 c = CIDX_BOLD_UNDER_FG + 3 * high;
276 } else {
277 c = CIDX_BOLD_FG + 3 * high;
279 } else {
280 // non-default color
281 high = 1;
282 if (c >= 8 && enableUnderline && (flags & CELL_UNDERLINE) != 0) {
283 *under = 1;
286 } else if ((flags & CELL_UNDERLINE) != 0) { // underline?
287 if (c > 255) {
288 // default color, transform to special
289 c = CIDX_UNDER_FG + 3 * high;
290 } else {
291 if (enableUnderline) *under = 1;
296 if (high) {
297 // high intensity
298 if (c < 8) c += 8;
299 else if (c == CIDX_DEFAULT_FG || c == CIDX_DEFAULT_BG) c += 2;
302 yterm_assert(c <= CIDX_MAX);
303 c = colorsTTY[c];
306 if (colorMode == CMODE_AMBER) {
307 c = (isfg ? colorAmberFG : colorAmberBG);
308 } else if (colorMode == CMODE_BW || colorMode == CMODE_GREEN || colorMode == CMODE_AMBER_TINT) {
309 //0.3*r + 0.59*g + 0.11*b
310 int lumi = (19660 * ((c>>16)&0xff) +
311 38666 * ((c>>8)&0xff) +
312 7208 * (c&0xff)) >> 16;
313 if (lumi > 255) lumi = 255;
314 if (colorMode == CMODE_BW) {
315 c = ((uint32_t)lumi << 16) | ((uint32_t)lumi << 8) | (uint32_t)lumi;
316 } else if (colorMode == CMODE_GREEN) {
317 c = ((uint32_t)lumi << 8);
318 } else {
319 // amber tint
320 lumi = 1 - ((lumi >> 7) & 0x01);
321 uint32_t xclr = (isfg ? colorAmberFG : colorAmberBG);
322 c = ((((xclr >> 16) & 0xff) >> lumi) << 16) |
323 ((((xclr >> 8) & 0xff) >> lumi) << 8) |
324 ((xclr & 0xff) >> lumi);
328 return c;
332 //==========================================================================
334 // cbuf_decode_attrs
336 //==========================================================================
337 YTERM_PUBLIC void cbuf_decode_attrs (const CharCell *cc, uint32_t *fg, uint32_t *bg,
338 yterm_bool *under)
340 *under = 0;
341 if ((cc->flags & CELL_SELECTION) == 0) {
342 if (!(cc->flags & CELL_INVERSE) == !cbufInverse) {
343 *fg = decode_color(cc->fg, cc->flags, under, 1);
344 *bg = decode_color(cc->bg, cc->flags, under, 0);
345 } else {
346 *bg = decode_color(cc->fg, cc->flags, under, 1);
347 *fg = decode_color(cc->bg, cc->flags, under, 0);
349 } else {
350 *fg = colorSelectionFG;
351 *bg = colorSelectionBG;
356 //==========================================================================
358 // set_cell_defaults
360 //==========================================================================
361 YTERM_STATIC_INLINE void set_cell_defaults (CharCell *cell) {
362 cell->ch = 0x20;
363 cell->flags = CELL_DIRTY;
364 cell->fg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR;
365 cell->bg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR;
369 //==========================================================================
371 // cbuf_set_cell_defaults
373 //==========================================================================
374 YTERM_PUBLIC void cbuf_set_cell_defaults (CharCell *cell) {
375 if (cell != NULL) set_cell_defaults(cell);
379 //==========================================================================
381 // cbuf_new
383 //==========================================================================
384 YTERM_PUBLIC void cbuf_new (CellBuffer *cbuf, int nwidth, int nheight) {
385 yterm_assert(cbuf);
386 yterm_assert(nwidth >= MinBufferWidth && nwidth <= MaxBufferWidth);
387 yterm_assert(nheight >= MinBufferHeight && nheight <= MaxBufferHeight);
388 memset((void *)cbuf, 0, sizeof(*cbuf));
389 const int bsz = nwidth * nheight;
390 cbuf->buf = malloc(sizeof(cbuf->buf[0]) * (size_t)bsz);
391 yterm_assert(cbuf->buf);
392 for (int f = 0; f < bsz; f += 1) {
393 set_cell_defaults(&cbuf->buf[f]);
395 cbuf->width = nwidth;
396 cbuf->height = nheight;
397 cbuf->dirtyCount = bsz;
398 CHECK_DIRTY(cbuf);
402 //==========================================================================
404 // cbuf_free
406 //==========================================================================
407 YTERM_PUBLIC void cbuf_free (CellBuffer *cbuf) {
408 if (cbuf != NULL) {
409 if (cbuf->buf) free(cbuf->buf);
410 memset((void *)cbuf, 0, sizeof(*cbuf));
415 //==========================================================================
417 // cbuf_resize
419 //==========================================================================
420 YTERM_PUBLIC void cbuf_resize (CellBuffer *cbuf, int nwidth, int nheight,
421 yterm_bool relayout)
423 yterm_assert(nwidth >= MinBufferWidth && nwidth <= MaxBufferWidth);
424 yterm_assert(nheight >= MinBufferHeight && nheight <= MaxBufferHeight);
426 if (cbuf != NULL && (cbuf->width != nwidth || cbuf->height != nheight)) {
427 const int bsz = nwidth * nheight;
428 CharCell *nbuf = malloc(sizeof(cbuf->buf[0]) * (size_t)bsz);
429 yterm_assert(nbuf);
430 CharCell *dest = nbuf;
431 cbuf->dirtyCount = 0;
432 const CharCell *src;
433 if (relayout) {
434 // relayout lines
435 LineIt it;
436 int x = 0, y = 0;
437 // write lines to the new buffer
438 it_init(&it, cbuf);
439 while (y < nheight) {
440 src = it_get(&it);
441 if (src != NULL) {
442 if (x == nwidth) {
443 if (y != 0) dest[-1].flags |= CELL_AUTO_WRAP;
444 x = 0; y += 1;
445 if (y == nheight) break;
447 *dest = *src;
448 dest->flags &= ~CELL_AUTO_WRAP; // just in case
449 dest->flags |= CELL_DIRTY;
450 dest += 1; x += 1;
451 } else {
452 // end of line, or end of text
453 while (x < nwidth) {
454 set_cell_defaults(dest);
455 dest += 1; x += 1;
457 x = 0; y += 1;
460 } else {
461 src = cbuf->buf;
462 const int xmin = min2(cbuf->width, nwidth);
463 for (int y = 0; y < nheight; y += 1) {
464 if (y < cbuf->height) {
465 memcpy(dest, src, (unsigned)xmin * sizeof(CharCell));
466 for (int f = 0; f < xmin; f += 1) {
467 dest[f].flags &= ~CELL_AUTO_WRAP; // just in case
468 dest[f].flags |= CELL_DIRTY;
470 for (int f = xmin; f < nwidth; f += 1) set_cell_defaults(&dest[f]);
471 } else {
472 for (int f = 0; f < nwidth; f += 1) set_cell_defaults(&dest[f]);
474 src += cbuf->width;
475 dest += nwidth;
478 free(cbuf->buf);
479 cbuf->buf = nbuf;
480 cbuf->width = nwidth;
481 cbuf->height = nheight;
482 cbuf->dirtyCount = bsz;
483 CHECK_DIRTY(cbuf);
488 //==========================================================================
490 // cbuf_mark_line_autowrap
492 //==========================================================================
493 YTERM_PUBLIC void cbuf_mark_line_autowrap (CellBuffer *cbuf, int y) {
494 if (cbuf != NULL && y >= 0 && y < cbuf->height) {
495 CharCell *cc = &cbuf->buf[(y + 1) * cbuf->width - 1];
496 cc->flags |= CELL_AUTO_WRAP;
501 //==========================================================================
503 // cbuf_unmark_line_autowrap
505 //==========================================================================
506 YTERM_PUBLIC void cbuf_unmark_line_autowrap (CellBuffer *cbuf, int y) {
507 if (cbuf != NULL && y >= 0 && y < cbuf->height) {
508 CharCell *cc = &cbuf->buf[(y + 1) * cbuf->width - 1];
509 cc->flags &= ~CELL_AUTO_WRAP;
514 #define PROCESS_REGION(code_) \
515 CLIP_REGION(); \
516 CharCell *cc = &cbuf->buf[y0 * cbwdt + x0]; \
517 const int wdt = x1 - x0 + 1; \
518 const int ofs = cbwdt - wdt; \
519 while (y0 <= y1) { \
520 int cnt = wdt; \
521 while (cnt != 0) { \
522 cnt -= 1; \
523 code_ \
524 cc += 1; \
526 cc += ofs; \
527 y0 += 1; \
529 CHECK_DIRTY(cbuf); \
530 do {} while (0)
533 //==========================================================================
535 // cbuf_mark_region_dirty
537 // region is inclusive
539 //==========================================================================
540 YTERM_PUBLIC void cbuf_mark_region_dirty (CellBuffer *cbuf, int x0, int y0, int x1, int y1) {
541 PROCESS_REGION(
542 if (!IS_DIRTY(cc)) {
543 cbuf->dirtyCount += 1;
544 cc->flags |= CELL_DIRTY;
550 //==========================================================================
552 // cbuf_mark_region_clean
554 //==========================================================================
555 YTERM_PUBLIC void cbuf_mark_region_clean (CellBuffer *cbuf, int x0, int y0, int x1, int y1) {
556 PROCESS_REGION(
557 if (IS_DIRTY(cc)) {
558 cbuf->dirtyCount -= 1;
559 cc->flags &= ~CELL_DIRTY;
565 //==========================================================================
567 // cbuf_clear_region
569 // region is inclusive
571 //==========================================================================
572 YTERM_PUBLIC void cbuf_clear_region (CellBuffer *cbuf, int x0, int y0, int x1, int y1,
573 const CharCell *attr)
575 LOAD_ATTRS();
576 const uint32_t clearch = (attr ? attr->ch : 0x02);
577 PROCESS_REGION(
578 if (DIFF_CELL(cc, clearch)) {
579 if (!IS_DIRTY(cc)) cbuf->dirtyCount += 1;
580 cc->ch = clearch;
581 cc->flags = flags;
582 cc->fg = fg;
583 cc->bg = bg;
589 //==========================================================================
591 // cbuf_merge_all_dirty
593 // this is called on screen switching.
594 // only non-dirty and totally unchanged cells will remain non-dirty.
596 //==========================================================================
597 YTERM_PUBLIC void cbuf_merge_all_dirty (CellBuffer *cbuf, const CellBuffer *sbuf) {
598 if (cbuf == NULL || sbuf == NULL || cbuf == sbuf) return;
599 const int wdt = min2(cbuf->width, sbuf->width);
600 const int hgt = min2(cbuf->height, sbuf->height);
601 const int slineadd = sbuf->width - wdt;
602 const int dlineadd = cbuf->width - wdt;
603 const CharCell *src = sbuf->buf;
604 CharCell *dest = cbuf->buf;
605 for (int y = 0; y < hgt; y += 1) {
606 for (int x = 0; x < wdt; x += 1) {
607 if (!IS_DIRTY(dest) && (IS_DIRTY(src) || DIFF_TWO_CELLS(src, dest))) {
608 cbuf->dirtyCount += 1;
609 dest->flags |= CELL_DIRTY;
611 src += 1; dest += 1;
613 src += slineadd;
614 dest += dlineadd;
619 //==========================================================================
621 // cbuf_write_wchar_count
623 //==========================================================================
624 YTERM_PUBLIC void cbuf_write_wchar_count (CellBuffer *cbuf, int x, int y, uint32_t ch,
625 int count, const CharCell *attr)
627 if (cbuf == NULL || count < 1 || y < 0 || y >= cbuf->height ||
628 x >= cbuf->width || (x < 0 && x + count <= 0))
630 return;
632 if (x < 0) { count += x; x = 0; }
633 if (cbuf->width - x < count) count = cbuf->width - x;
634 LOAD_ATTRS();
635 CharCell *cc = &cbuf->buf[y * cbuf->width + x];
636 if (ch > 0xffff) ch = 0xFFFDU; // invalid unicode
637 while (count != 0) {
638 count -= 1;
639 if (DIFF_CELL(cc, ch)) {
640 if (!IS_DIRTY(cc)) cbuf->dirtyCount += 1;
641 cc->ch = ch;
642 cc->flags = flags;
643 cc->fg = fg;
644 cc->bg = bg;
646 cc += 1;
648 CHECK_DIRTY(cbuf);
652 //==========================================================================
654 // cbuf_write_utf
656 //==========================================================================
657 YTERM_PUBLIC void cbuf_write_utf (CellBuffer *cbuf, int x, int y, const char *str,
658 const CharCell *attr)
660 if (cbuf == NULL || str == NULL || str[0] == 0 ||
661 y < 0 || x >= cbuf->width || y >= cbuf->height)
663 return;
665 const int wdt = cbuf->width;
666 uint32_t cp = 0;
667 CharCell *cc = &cbuf->buf[y * wdt] + (x > 0 ? x : 0);
668 LOAD_ATTRS();
669 while (*str && x < wdt) {
670 cp = yterm_utf8d_consume(cp, *str); str += 1;
671 if (yterm_utf8_valid_cp(cp)) {
672 if (x >= 0) {
673 if (DIFF_CELL(cc, cp)) {
674 if (!IS_DIRTY(cc)) cbuf->dirtyCount += 1;
675 cc->ch = cp;
676 cc->flags = flags;
677 cc->fg = fg;
678 cc->bg = bg;
680 cc += 1;
682 x += 1;
685 CHECK_DIRTY(cbuf);
689 //==========================================================================
691 // copy_strip
693 // copy continuous strip, with possible overlap
694 // it can either merge dirty state, or overwrite
695 // no input checking is done, it is caller's responsibility
697 //==========================================================================
698 static void copy_strip (CellBuffer *cbuf, int srcofs, int destofs, int count,
699 yterm_bool merge_dirty)
701 yterm_assert(count > 0);
702 const CharCell *src = &cbuf->buf[srcofs];
703 CharCell *dest = &cbuf->buf[destofs];
704 ssize_t dir;
705 if (srcofs > destofs) {
706 // forward copy
707 dir = 1;
708 } else {
709 // backward copy
710 src += count - 1;
711 dest += count - 1;
712 dir = -1;
714 // now do it; use pasta for small speed gain
715 if (merge_dirty) {
716 // merge dirty flags
717 while (count > 0) {
718 count -= 1;
719 if (DIFF_TWO_CELLS(src, dest)) {
720 // dest will be marked dirty, fix the counter
721 if (!IS_DIRTY(dest)) cbuf->dirtyCount += 1;
722 *dest = *src;
723 dest->flags |= CELL_DIRTY;
725 src += dir; dest += dir;
727 } else {
728 // overwrite dirty flags
729 while (count > 0) {
730 count -= 1;
731 if (IS_DIRTY(dest)) {
732 // dest is dirty
733 if (!IS_DIRTY(src)) cbuf->dirtyCount -= 1;
734 } else {
735 // dest is not dirty
736 if (IS_DIRTY(src)) cbuf->dirtyCount += 1;
738 *dest = *src;
739 src += dir; dest += dir;
745 //==========================================================================
747 // cbuf_scroll_area
749 // vertical area scroll.
750 // will scroll the region from `y0` to `y1` (inclusive).
751 // if `lines` is negative, scroll up, else scroll down.
752 // dirty flags will be copied.
753 // it is caller's responsibility to clear new lines.
755 //==========================================================================
756 YTERM_PUBLIC void cbuf_scroll_area (CellBuffer *cbuf, int y0, int y1, int lines,
757 int dir, yterm_bool merge_dirty, const CharCell *attr)
759 if (cbuf == NULL || dir == 0 || lines <= 0) return;
761 const int hgt = cbuf->height;
763 if (y0 > y1 || y1 < 0 || y0 >= hgt || cbuf->width == 0) return;
765 // clip scroll region
766 if (y0 < 0) y0 = 0;
767 if (y1 >= hgt) y1 = hgt - 1;
768 const int regh = y1 - y0 + 1;
770 const int wdt = cbuf->width;
772 if (lines < regh) {
773 const int lcp = (regh - lines) * wdt; // cells to copy
775 if (dir < 0) {
776 // scroll up
777 copy_strip(cbuf, (y0 + lines) * wdt, y0 * wdt, lcp, merge_dirty);
778 // clear new lines
779 if (attr != NULL) {
780 cbuf_clear_region(cbuf, 0, y1 - lines + 1, wdt - 1, y1, attr);
782 } else {
783 // scroll down
784 copy_strip(cbuf, y0 * wdt, (y0 + lines) * wdt, lcp, merge_dirty);
785 // clear new lines
786 if (attr != NULL) {
787 cbuf_clear_region(cbuf, 0, y0, wdt - 1, y0 + lines - 1, attr);
791 #ifdef SCROLL_DIRTY_CHECKS
792 check_dirty_count(cbuf, __LINE__);
793 #endif
794 } else {
795 // the whole region, just clear it
796 if (attr != NULL) {
797 cbuf_clear_region(cbuf, 0, y0, wdt - 1, y1, attr);
801 // fix autowraps: above the region, and the region itself
802 y0 -= 1;
803 while (y0 != y1) {
804 cbuf_unmark_line_autowrap(cbuf, y0);
805 y0 += 1;
810 //==========================================================================
812 // cbuf_insert_chars
814 //==========================================================================
815 YTERM_PUBLIC void cbuf_insert_chars (CellBuffer *cbuf, int x, int y, int count,
816 const CharCell *attr)
818 if (cbuf == NULL || count <= 0 || y < 0 || y >= cbuf->height) return;
820 const int wdt = cbuf->width;
822 if (x >= wdt) return;
823 if (x < 0) x = 0;
825 if (wdt - x > count) {
826 // copy
827 copy_strip(cbuf, y * wdt + x, y * wdt + x + count, wdt - x - count, CBUF_DIRTY_MERGE);
828 // clear
829 if (attr != NULL) {
830 cbuf_clear_region(cbuf, x, y, x + count - 1, y, attr);
832 } else {
833 // just clear
834 if (attr != NULL) {
835 cbuf_clear_region(cbuf, x, y, wdt - 1, y, attr);
839 cbuf_unmark_line_autowrap(cbuf, y);
841 #ifdef SCROLL_DIRTY_CHECKS
842 check_dirty_count(cbuf, __LINE__);
843 #endif
847 //==========================================================================
849 // cbuf_delete_chars
851 //==========================================================================
852 YTERM_PUBLIC void cbuf_delete_chars (CellBuffer *cbuf, int x, int y, int count,
853 const CharCell *attr)
855 if (cbuf == NULL || count <= 0 || y < 0 || y >= cbuf->height) return;
857 const int wdt = cbuf->width;
859 if (x >= wdt) return;
860 if (x < 0) x = 0;
862 if (wdt - x > count) {
863 // copy
864 copy_strip(cbuf, y * wdt + x + count, y * wdt + x, wdt - x - count, CBUF_DIRTY_MERGE);
865 // clear
866 if (attr != NULL) {
867 cbuf_clear_region(cbuf, wdt - count, y, wdt - 1, y, attr);
869 } else {
870 // just clear
871 if (attr != NULL) {
872 cbuf_clear_region(cbuf, x, y, wdt - 1, y, attr);
876 cbuf_unmark_line_autowrap(cbuf, y);
878 #ifdef SCROLL_DIRTY_CHECKS
879 check_dirty_count(cbuf, __LINE__);
880 #endif
884 //==========================================================================
886 // cbuf_copy_region
888 //==========================================================================
889 YTERM_PUBLIC void cbuf_copy_region (CellBuffer *cbuf, int destx, int desty,
890 const CellBuffer *srcbuf,
891 int x0, int y0, int x1, int y1)
893 // fast exit checks
894 if (cbuf == NULL || srcbuf == NULL) return;
895 if (x1 < 0 || y1 < 0 || x0 > x1 || y0 > y1) return;
897 if (destx >= cbuf->width || desty >= cbuf->height) return;
898 if (x0 >= srcbuf->width || y0 >= srcbuf->height) return;
900 // clip source region
901 if (x0 < 0) {
902 // shift destination right, to compensate missing part
903 destx -= x0; x0 = 0;
904 if (destx >= cbuf->width) return;
906 if (y0 < 0) {
907 // shift destination down, to compensate missing part
908 desty -= y0; y0 = 0;
909 if (desty >= cbuf->height) return;
911 if (x1 >= srcbuf->width) x1 = cbuf->width - 1;
912 if (y1 >= srcbuf->height) y1 = cbuf->height - 1;
914 // check it again, just in case
915 if (x0 > x1 || y0 > y1) return;
917 // region size
918 int cw = x1 - x0 + 1;
919 int ch = y1 - y0 + 1;
921 // clip destination region
922 if (destx < 0) {
923 // shift source right, and shorten
924 x0 -= destx; cw += destx; destx = 0;
925 if (cw < 1 || x0 >= srcbuf->width) return;
927 if (desty < 0) {
928 // shift source down, and shorten
929 y0 -= desty; ch += desty; desty = 0;
930 if (ch < 1 || y0 >= srcbuf->height) return;
933 // clip destination even more
934 yterm_assert(destx < cbuf->width);
935 yterm_assert(desty < cbuf->height);
936 cw = min2(cw, cbuf->width - destx);
937 ch = min2(ch, cbuf->height - desty);
938 yterm_assert(cw > 0);
939 yterm_assert(ch > 0);
941 // ok, everything is properly clipped now, we can copy cells
942 const CharCell *src = &srcbuf->buf[y0 * srcbuf->width + x0];
943 CharCell *dest = &cbuf->buf[desty * cbuf->width + destx];
945 while (ch != 0) {
946 const CharCell *s = src;
947 CharCell *d = d;
948 int cnt = cw;
949 while (cnt != 0) {
950 cnt -= 1;
951 if (DIFF_TWO_CELLS(s, d)) {
952 if (!IS_DIRTY(d)) cbuf->dirtyCount += 1;
953 d->ch = s->ch;
954 d->flags = s->flags | CELL_DIRTY;
955 d->fg = s->fg;
956 d->bg = s->bg;
958 s += 1; d += 1;
960 src += srcbuf->width;
961 dest += cbuf->width;
964 #ifdef SCROLL_DIRTY_CHECKS
965 check_dirty_count(cbuf, __LINE__);
966 #endif
970 //==========================================================================
972 // cbuf_merge_cell
974 //==========================================================================
975 YTERM_PUBLIC void cbuf_merge_cell (CellBuffer *cbuf, int x, int y, const CharCell *cell) {
976 if (cbuf != NULL && cell != NULL && x >= 0 && y >= 0 &&
977 x < cbuf->width && y < cbuf->height)
979 CharCell *d = &cbuf->buf[y * cbuf->width + x];
980 if (DIFF_TWO_CELLS(cell, d)) {
981 if (!IS_DIRTY(d)) cbuf->dirtyCount += 1;
982 d->ch = cell->ch;
983 d->flags = cell->flags | CELL_DIRTY;
984 d->fg = cell->fg;
985 d->bg = cell->bg;