2 * SXED -- sexy text editor engine, 2022
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/>.
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)
35 # define CHECK_DIRTY(cbf_) ((void)0)
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; \
51 if (x1 >= cbwdt) x1 = cbwdt - 1; \
52 if (y1 >= cbhgt) y1 = cbhgt - 1; \
53 yterm_assert(x0 <= x1 && y0 <= y1); \
56 #define LOAD_ATTRS() \
61 flags = (attr->flags | CELL_DIRTY) & (~CELL_AUTO_WRAP); \
62 fg = attr->fg; bg = attr->bg; \
65 fg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR; \
66 bg = CELL_ATTR_MASK | CELL_DEFAULT_ATTR; \
70 #define DIFF_CELL(cc_,ch_) (\
71 ((cc_)->ch ^ (uint32_t)(ch_)) | \
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 // ////////////////////////////////////////////////////////////////////////// //
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 //==========================================================================
112 // init default palette
114 //==========================================================================
115 void cbuf_init_palette (void) {
116 const uint32_t defclr16
[16] = {
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
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) {
158 if (r
) c
+= (0x37 + 0x28 * r
) << 16;
159 if (g
) c
+= (0x37 + 0x28 * g
) << 8;
160 if (b
) c
+= (0x37 + 0x28 * b
);
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);
174 yterm_assert(cidx
== 256);
178 //==========================================================================
182 //==========================================================================
183 static __attribute__((unused
)) void check_dirty_count (const CellBuffer
*cbuf
, const int line
) {
186 const CharCell
*cc
= cbuf
->buf
;
187 int cnt
= cbuf
->width
* cbuf
->height
;
190 if (IS_DIRTY(cc
)) dc
+= 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
);
202 //==========================================================================
206 //==========================================================================
207 YTERM_STATIC_INLINE
void it_init (LineIt
*it
, CellBuffer
*cbuf
) {
209 it
->h
= cbuf
->height
;
217 //==========================================================================
221 //==========================================================================
222 YTERM_STATIC_INLINE yterm_bool
it_is_eot (const LineIt
*it
) {
223 return (it
->y
== it
->h
);
227 //==========================================================================
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;
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;
254 //==========================================================================
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?
270 if ((flags
& CELL_BOLD
) != 0) { // bold
271 // either high intensity color, or special bold color
273 // default color, transform to special
274 if ((flags
& CELL_UNDERLINE
) != 0) {
275 c
= CIDX_BOLD_UNDER_FG
+ 3 * high
;
277 c
= CIDX_BOLD_FG
+ 3 * high
;
282 if (c
>= 8 && enableUnderline
&& (flags
& CELL_UNDERLINE
) != 0) {
286 } else if ((flags
& CELL_UNDERLINE
) != 0) { // underline?
288 // default color, transform to special
289 c
= CIDX_UNDER_FG
+ 3 * high
;
291 if (enableUnderline
) *under
= 1;
299 else if (c
== CIDX_DEFAULT_FG
|| c
== CIDX_DEFAULT_BG
) c
+= 2;
302 yterm_assert(c
<= CIDX_MAX
);
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);
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
);
332 //==========================================================================
336 //==========================================================================
337 YTERM_PUBLIC
void cbuf_decode_attrs (const CharCell
*cc
, uint32_t *fg
, uint32_t *bg
,
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);
346 *bg
= decode_color(cc
->fg
, cc
->flags
, under
, 1);
347 *fg
= decode_color(cc
->bg
, cc
->flags
, under
, 0);
350 *fg
= colorSelectionFG
;
351 *bg
= colorSelectionBG
;
356 //==========================================================================
360 //==========================================================================
361 YTERM_STATIC_INLINE
void set_cell_defaults (CharCell
*cell
) {
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 //==========================================================================
383 //==========================================================================
384 YTERM_PUBLIC
void cbuf_new (CellBuffer
*cbuf
, int nwidth
, int nheight
) {
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
;
402 //==========================================================================
406 //==========================================================================
407 YTERM_PUBLIC
void cbuf_free (CellBuffer
*cbuf
) {
409 if (cbuf
->buf
) free(cbuf
->buf
);
410 memset((void *)cbuf
, 0, sizeof(*cbuf
));
415 //==========================================================================
419 //==========================================================================
420 YTERM_PUBLIC
void cbuf_resize (CellBuffer
*cbuf
, int nwidth
, int nheight
,
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
);
430 CharCell
*dest
= nbuf
;
431 cbuf
->dirtyCount
= 0;
437 // write lines to the new buffer
439 while (y
< nheight
) {
443 if (y
!= 0) dest
[-1].flags
|= CELL_AUTO_WRAP
;
445 if (y
== nheight
) break;
448 dest
->flags
&= ~CELL_AUTO_WRAP
; // just in case
449 dest
->flags
|= CELL_DIRTY
;
452 // end of line, or end of text
454 set_cell_defaults(dest
);
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
]);
472 for (int f
= 0; f
< nwidth
; f
+= 1) set_cell_defaults(&dest
[f
]);
480 cbuf
->width
= nwidth
;
481 cbuf
->height
= nheight
;
482 cbuf
->dirtyCount
= bsz
;
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_) \
516 CharCell *cc = &cbuf->buf[y0 * cbwdt + x0]; \
517 const int wdt = x1 - x0 + 1; \
518 const int ofs = cbwdt - wdt; \
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
) {
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
) {
558 cbuf
->dirtyCount
-= 1;
559 cc
->flags
&= ~CELL_DIRTY
;
565 //==========================================================================
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
)
576 const uint32_t clearch
= (attr
? attr
->ch
: 0x02);
578 if (DIFF_CELL(cc
, clearch
)) {
579 if (!IS_DIRTY(cc
)) cbuf
->dirtyCount
+= 1;
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
;
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))
632 if (x
< 0) { count
+= x
; x
= 0; }
633 if (cbuf
->width
- x
< count
) count
= cbuf
->width
- x
;
635 CharCell
*cc
= &cbuf
->buf
[y
* cbuf
->width
+ x
];
636 if (ch
> 0xffff) ch
= 0xFFFDU
; // invalid unicode
639 if (DIFF_CELL(cc
, ch
)) {
640 if (!IS_DIRTY(cc
)) cbuf
->dirtyCount
+= 1;
652 //==========================================================================
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
)
665 const int wdt
= cbuf
->width
;
667 CharCell
*cc
= &cbuf
->buf
[y
* wdt
] + (x
> 0 ? x
: 0);
669 while (*str
&& x
< wdt
) {
670 cp
= yterm_utf8d_consume(cp
, *str
); str
+= 1;
671 if (yterm_utf8_valid_cp(cp
)) {
673 if (DIFF_CELL(cc
, cp
)) {
674 if (!IS_DIRTY(cc
)) cbuf
->dirtyCount
+= 1;
689 //==========================================================================
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
];
705 if (srcofs
> destofs
) {
714 // now do it; use pasta for small speed gain
719 if (DIFF_TWO_CELLS(src
, dest
)) {
720 // dest will be marked dirty, fix the counter
721 if (!IS_DIRTY(dest
)) cbuf
->dirtyCount
+= 1;
723 dest
->flags
|= CELL_DIRTY
;
725 src
+= dir
; dest
+= dir
;
728 // overwrite dirty flags
731 if (IS_DIRTY(dest
)) {
733 if (!IS_DIRTY(src
)) cbuf
->dirtyCount
-= 1;
736 if (IS_DIRTY(src
)) cbuf
->dirtyCount
+= 1;
739 src
+= dir
; dest
+= dir
;
745 //==========================================================================
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
767 if (y1
>= hgt
) y1
= hgt
- 1;
768 const int regh
= y1
- y0
+ 1;
770 const int wdt
= cbuf
->width
;
773 const int lcp
= (regh
- lines
) * wdt
; // cells to copy
777 copy_strip(cbuf
, (y0
+ lines
) * wdt
, y0
* wdt
, lcp
, merge_dirty
);
780 cbuf_clear_region(cbuf
, 0, y1
- lines
+ 1, wdt
- 1, y1
, attr
);
784 copy_strip(cbuf
, y0
* wdt
, (y0
+ lines
) * wdt
, lcp
, merge_dirty
);
787 cbuf_clear_region(cbuf
, 0, y0
, wdt
- 1, y0
+ lines
- 1, attr
);
791 #ifdef SCROLL_DIRTY_CHECKS
792 check_dirty_count(cbuf
, __LINE__
);
795 // the whole region, just clear it
797 cbuf_clear_region(cbuf
, 0, y0
, wdt
- 1, y1
, attr
);
801 // fix autowraps: above the region, and the region itself
804 cbuf_unmark_line_autowrap(cbuf
, y0
);
810 //==========================================================================
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;
825 if (wdt
- x
> count
) {
827 copy_strip(cbuf
, y
* wdt
+ x
, y
* wdt
+ x
+ count
, wdt
- x
- count
, CBUF_DIRTY_MERGE
);
830 cbuf_clear_region(cbuf
, x
, y
, x
+ count
- 1, y
, attr
);
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__
);
847 //==========================================================================
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;
862 if (wdt
- x
> count
) {
864 copy_strip(cbuf
, y
* wdt
+ x
+ count
, y
* wdt
+ x
, wdt
- x
- count
, CBUF_DIRTY_MERGE
);
867 cbuf_clear_region(cbuf
, wdt
- count
, y
, wdt
- 1, y
, attr
);
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__
);
884 //==========================================================================
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
)
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
902 // shift destination right, to compensate missing part
904 if (destx
>= cbuf
->width
) return;
907 // shift destination down, to compensate missing part
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;
918 int cw
= x1
- x0
+ 1;
919 int ch
= y1
- y0
+ 1;
921 // clip destination region
923 // shift source right, and shorten
924 x0
-= destx
; cw
+= destx
; destx
= 0;
925 if (cw
< 1 || x0
>= srcbuf
->width
) return;
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
];
946 const CharCell
*s
= src
;
951 if (DIFF_TWO_CELLS(s
, d
)) {
952 if (!IS_DIRTY(d
)) cbuf
->dirtyCount
+= 1;
954 d
->flags
= s
->flags
| CELL_DIRTY
;
960 src
+= srcbuf
->width
;
964 #ifdef SCROLL_DIRTY_CHECKS
965 check_dirty_count(cbuf
, __LINE__
);
970 //==========================================================================
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;
983 d
->flags
= cell
->flags
| CELL_DIRTY
;