1 static void tsetdirt (Term
*term
, int top
, int bot
) {
2 LIMIT(top
, 0, term
->row
-1);
3 LIMIT(bot
, 0, term
->row
-1);
4 for (int y
= top
; y
<= bot
; ++y
) markDirty(term
, y
, 2);
8 static void tfulldirt (Term
*term
) {
9 tsetdirt(term
, 0, term
->row
-1);
13 static void tmoveto (Term
*term
, int x
, int y
) {
14 LIMIT(x
, 0, term
->col
-1);
15 LIMIT(y
, 0, term
->row
-1);
16 term
->c
.state
&= ~CURSOR_WRAPNEXT
;
17 if (term
->c
.x
!= x
|| term
->c
.y
!= y
) {
20 setWantRedraw(term
, 0);
25 static void tclearregion (Term
*term
, int x1
, int y1
, int x2
, int y2
) {
28 //fprintf(stderr, "tclearregion: (%d,%d)-(%d,%d)\n", x1, y1, x2, y2);
29 if (x1
> x2
) { temp
= x1
; x1
= x2
; x2
= temp
; }
30 if (y1
> y2
) { temp
= y1
; y1
= y2
; y2
= temp
; }
31 LIMIT(x1
, 0, term
->col
-1);
32 LIMIT(x2
, 0, term
->col
-1);
33 LIMIT(y1
, 0, term
->row
-1);
34 LIMIT(y2
, 0, term
->row
-1);
35 //fprintf(stderr, " tclearregion: (%d,%d)-(%d,%d)\n", x1, y1, x2, y2);
36 for (int y
= y1
; y
<= y2
; ++y
) {
37 Line l
= term
->line
[y
];
39 markDirty(term
, y
, (x1
<= 0 && x2
>= term
->col
-1) ? 2 : 1);
40 for (int x
= x1
; x
<= x2
; ++x
) {
41 l
[x
].fg
= term
->c
.attr
.fg
;
42 l
[x
].bg
= term
->c
.attr
.bg
;
43 l
[x
].state
= GLYPH_DIRTY
;
44 l
[x
].attr
= ATTR_NULL
|(term
->c
.attr
.attr
&(ATTR_DEFFG
|ATTR_DEFBG
));
46 if (term
->sel
.bx
!= -1 && isselected(term
, x
, y
)) selhide(term
);
48 l
[term
->col
-1].state
&= ~GLYPH_WRAP
;
53 static void tcursor (Term
*term
, int mode
) {
54 if (mode
== CURSOR_SAVE
) {
55 term
->csaved
= term
->c
;
56 } else if (mode
== CURSOR_LOAD
) {
57 term
->c
= term
->csaved
;
58 tmoveto(term
, term
->c
.x
, term
->c
.y
);
59 setWantRedraw(term
, 0);
64 static void tadjustmaxhistory (Term
*term
, int maxh
) {
66 LIMIT(maxh
, 0, 65535);
67 if (term
->maxhistory
< maxh
) {
69 int newlc
= term
->linecount
+(maxh
-term
->maxhistory
);
72 if ((nl
= realloc(term
->line
, sizeof(Line
)*newlc
)) != NULL
) {
77 g
.state
= GLYPH_DIRTY
;
78 g
.attr
= ATTR_NULL
|ATTR_DEFFG
|ATTR_DEFBG
;
83 for (int y
= term
->linecount
; y
< newlc
; ++y
) {
84 term
->line
[y
] = calloc(term
->col
, sizeof(Glyph
));
85 for (int x
= 0; x
< term
->col
; ++x
) term
->line
[y
][x
] = g
;
87 term
->maxhistory
= maxh
;
88 term
->linecount
= newlc
;
90 } else if (term
->maxhistory
> maxh
) {
94 // remove history lines
95 while (term
->linecount
> term
->row
+maxh
) free(term
->line
[--term
->linecount
]);
96 if ((nl
= realloc(term
->line
, sizeof(Line
)*term
->linecount
)) != NULL
) term
->line
= nl
;
97 term
->maxhistory
= maxh
;
103 static void tswapscreen (Term
*term
) {
106 for (int f
= 0; f
< term
->row
; ++f
) {
107 Line t
= term
->line
[f
];
109 term
->line
[f
] = term
->alt
[f
];
112 term
->mode
^= MODE_ALTSCREEN
;
114 term
->justSwapped
= 1;
118 //FIXME: works bad with history
120 static void selscroll (Term
*term
, int orig
, int n
, int tohistory
) {
123 if (term
->sel
.bx
== -1) return;
125 tfulldirt(term
); // just in case
127 if (BETWEEN(term
->sel
.by
, orig
, term
->bot
) || BETWEEN(term
->sel
.ey
, orig
, term
->bot
)) {
128 if ((term
->sel
.by
+= n
) > term
->bot
|| (term
->sel
.ey
+= n
) < term
->top
) {
132 if (term
->sel
.by
< term
->top
) {
133 term
->sel
.by
= term
->top
;
137 if (term
->sel
.ey
> term
->bot
) {
138 term
->sel
.ey
= term
->bot
;
139 term
->sel
.ex
= term
->col
;
142 term
->sel
.b
.x
= term
->sel
.bx
;
143 term
->sel
.b
.y
= term
->sel
.by
;
144 term
->sel
.e
.x
= term
->sel
.ex
;
145 term
->sel
.e
.y
= term
->sel
.ey
;
148 // tohistory!=0; always scrolls full screen up (n == -1)
149 //fprintf(stderr, "selscroll to history\n");
152 //fprintf(stderr, " by=%d; ey=%d; maxhistory=%d\n", term->sel.by, term->sel.ey, term->maxhistory);
153 if (term
->sel
.ey
< 0 && -(term
->sel
.ey
) > term
->maxhistory
) {
154 // out of screen completely
158 if (term
->sel
.by
< 0 && -(term
->sel
.by
) > term
->maxhistory
) {
159 term
->sel
.by
= -term
->maxhistory
;
163 term
->sel
.b
.x
= term
->sel
.bx
;
164 term
->sel
.b
.y
= term
->sel
.by
;
165 term
->sel
.e
.x
= term
->sel
.ex
;
166 term
->sel
.e
.y
= term
->sel
.ey
;
169 if (docopy
) selcopy(term
);
173 static void tscrolldown (Term
*term
, int orig
, int n
) {
176 LIMIT(n
, 0, term
->bot
-orig
+1);
178 selscroll(term
, orig
, n
, 0);
179 //fprintf(stderr, "tscrolldown(%d, %d)\n", orig, n);
180 tclearregion(term
, 0, term
->bot
-n
+1, term
->col
-1, term
->bot
);
181 for (int f
= term
->bot
; f
>= orig
+n
; --f
) {
182 temp
= term
->line
[f
];
183 term
->line
[f
] = term
->line
[f
-n
];
184 term
->line
[f
-n
] = temp
;
185 markDirty(term
, f
, 2);
186 markDirty(term
, f
-n
, 2);
191 static void tscrollup (Term
*term
, int orig
, int n
, int tohistory
) {
194 if (term
== NULL
) return;
195 LIMIT(n
, 0, term
->bot
-orig
+1);
197 //fprintf(stderr, "tscrollup(%d, %d)\n", orig, n);
198 if (tohistory
&& !IS_SET(term
, MODE_ALTSCREEN
) && term
->maxhistory
> 0) {
199 Line l
= term
->line
[term
->linecount
-1];
201 for (int f
= term
->linecount
-1; f
> term
->row
; --f
) term
->line
[f
] = term
->line
[f
-1];
202 term
->line
[term
->row
] = l
;
203 for (int x
= 0; x
< term
->col
; ++x
) l
[x
] = term
->line
[0][x
];
208 selscroll(term
, orig
, -n
, tohistory
);
209 //tclearregion(0, orig, term->col-1, orig+n-1);
210 for (int f
= orig
; f
<= term
->bot
-n
; ++f
) {
211 temp
= term
->line
[f
];
212 term
->line
[f
] = term
->line
[f
+n
];
213 term
->line
[f
+n
] = temp
;
214 markDirty(term
, f
, 2);
215 markDirty(term
, f
+n
, 2);
217 tclearregion(term
, 0, term
->bot
-n
+1, term
->col
-1, term
->bot
);
221 static inline void tsetcharwrap (Term
*term
, int y
, int wrap
) {
222 if (y
>= 0 && y
< term
->row
) {
223 if (wrap
) term
->line
[y
][term
->col
-1].state
|= GLYPH_WRAP
;
224 else term
->line
[y
][term
->col
-1].state
&= ~GLYPH_WRAP
;
229 static void tnewline (Term
*term
, int first_col
) {
232 tsetcharwrap(term
, y
, (first_col
== 2)); // 2: wrapping
233 if (y
== term
->bot
) tscrollup(term
, term
->top
, 1, 1); else ++y
;
234 tmoveto(term
, (first_col
? 0 : term
->c
.x
), y
);
238 static void csiparse (Term
*term
) {
239 const char *p
= term
->escseq
.buf
;
241 term
->escseq
.narg
= 0;
242 if (*p
== '?') { term
->escseq
.priv
= 1; ++p
; }
243 while (p
< term
->escseq
.buf
+term
->escseq
.len
) {
244 int n
= term
->escseq
.arg
[term
->escseq
.narg
];
246 for (; *p
&& isdigit(*p
); ++p
) n
= n
*10+(p
[0]-'0');
247 term
->escseq
.arg
[term
->escseq
.narg
] = n
;
249 if (*p
== ';' && term
->escseq
.narg
+1 < ESC_ARG_SIZ
) {
253 term
->escseq
.mode
= *p
;
261 static void tsetchar (Term
*term
, const char *c
) {
263 int rev
= 0, gfx
= 0;
264 int x
= term
->c
.x
, y
= term
->c
.y
;
266 if (x
< 0 || x
>= term
->col
|| y
< 0 || y
>= term
->row
) return;
268 if (!term
->needConv
&& unimap
!= NULL
&& (unsigned char)c
[0] >= 0x80) {
273 ushort uc
= unimap
[cc
];
275 //fprintf(stderr, "unimap: %d [%02X] 0x%04x -> 0x%04x\n", (unsigned char)c[0], cc, uc);
293 markDirty(term
, y
, 1);
295 term
->line
[y
][x
] = term
->c
.attr
;
296 if (rev
) term
->line
[y
][x
].attr
^= ATTR_REVERSE
;
297 if (gfx
|| (term
->mode
&term
->charset
)) {
298 term
->line
[y
][x
].attr
|= ATTR_GFX
;
300 term
->line
[y
][x
].attr
&= ~ATTR_GFX
;
303 term
->line
[y
][x
].state
= (GLYPH_SET
|GLYPH_DIRTY
);
304 memcpy(term
->line
[y
][x
].c
, c
, UTF_SIZ
);
306 if (IS_GFX(term
->line
[y
][x
].attr
)) {
307 unsigned char c
= (unsigned char)(term
->line
[y
][x
].c
[0]);
309 if (c
> 95 && c
< 128) term
->line
[y
][x
].c
[0] -= 95;
310 else if (c
> 127) term
->line
[y
][x
].c
[0] = ' ';
312 if (term
->sel
.bx
!= -1 && isselected(term
, x
, y
)) selhide(term
);
313 //dlogf("tsetchar(%d,%d): [%c] (%d); dirty=%d\n", x, y, c[0], term->wantRedraw, term->dirty[y]);
317 static void tdeletechar (Term
*term
, int n
) {
318 int src
= term
->c
.x
+n
;
320 int size
= term
->col
-src
;
322 markDirty(term
, term
->c
.y
, 2);
323 if (src
>= term
->col
) {
324 tclearregion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
326 memmove(&term
->line
[term
->c
.y
][dst
], &term
->line
[term
->c
.y
][src
], size
*sizeof(Glyph
));
327 tclearregion(term
, term
->col
-n
, term
->c
.y
, term
->col
-1, term
->c
.y
);
332 static void tinsertblank (Term
*term
, int n
) {
335 int size
= term
->col
-dst
;
337 markDirty(term
, term
->c
.y
, 2);
338 if (dst
>= term
->col
) {
339 tclearregion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
341 memmove(&term
->line
[term
->c
.y
][dst
], &term
->line
[term
->c
.y
][src
], size
*sizeof(Glyph
));
342 tclearregion(term
, src
, term
->c
.y
, dst
-1, term
->c
.y
);
347 static void tinsertblankline (Term
*term
, int n
) {
348 if (term
->c
.y
< term
->top
|| term
->c
.y
> term
->bot
) return;
349 tscrolldown(term
, term
->c
.y
, n
);
353 static void tdeleteline (Term
*term
, int n
) {
354 if (term
->c
.y
< term
->top
|| term
->c
.y
> term
->bot
) return;
355 tscrollup(term
, term
->c
.y
, n
, 0);
359 static void tsetscroll (Term
*term
, int t
, int b
) {
362 LIMIT(t
, 0, term
->row
-1);
363 LIMIT(b
, 0, term
->row
-1);
374 static void tunshowhistory (Term
*term
) {
375 if (term
!= NULL
&& term
->topline
!= 0) {
377 setWantRedraw(term
, 1);
382 static void tsendfocusevent (Term
*term
, int focused
) {
383 if (term
!= NULL
&& IS_SET(term
, MODE_FOCUSEVT
)) {
384 ttywritestr(term
, "\x1b[");
385 ttywrite(term
, focused
?"I":"O", 1);
390 ////////////////////////////////////////////////////////////////////////////////
392 static void tresetmode (Term
*term
) {
395 term
->bot
= term
->row
-1;
396 term
->mode
= MODE_WRAP
/*|MODE_MOUSEBTN*/|MODE_GFX1
;
397 term
->mousemode
= 1000;
398 term
->charset
= MODE_GFX0
;
400 term
->c
.state
= CURSOR_DEFAULT
;
403 tcursor(term
, CURSOR_SAVE
);
407 static void treset (Term
*term
) {
410 term
->c
= (TCursor
){{
411 .attr
= ATTR_NULL
|ATTR_DEFFG
|ATTR_DEFBG
,
414 }, .x
= 0, .y
= 0, .state
= CURSOR_DEFAULT
};
416 g
.state
= GLYPH_DIRTY
;
417 g
.attr
= term
->c
.attr
.attr
;
418 g
.fg
= term
->c
.attr
.fg
;
419 g
.bg
= term
->c
.attr
.bg
;
424 //tclearregion(0, 0, term->col-1, term->row-1);
425 for (int y
= 0; y
< term
->row
; ++y
) {
426 markDirty(term
, y
, 2);
427 for (int x
= 0; x
< term
->col
; ++x
) term
->alt
[y
][x
] = term
->line
[y
][x
] = g
;
429 for (int y
= term
->row
; y
< term
->linecount
; ++y
) {
430 for (int x
= 0; x
< term
->col
; ++x
) term
->line
[y
][x
] = g
;
438 static int tinitialize (Term
*term
, int col
, int row
) {
439 //memset(term, 0, sizeof(Term));
440 //term->needConv = needConversion ? 1 : 0;
441 term
->wrbufsize
= WBUFSIZ
;
442 term
->deffg
= term
->deffg
;
443 term
->defbg
= term
->defbg
;
446 term
->dirty
= calloc(term
->row
, sizeof(*term
->dirty
));
447 term
->maxhistory
= opt_maxhistory
;
448 term
->linecount
= term
->maxhistory
+term
->row
;
449 term
->line
= calloc(term
->linecount
, sizeof(Line
));
450 term
->alt
= calloc(term
->row
, sizeof(Line
));
451 for (int y
= 0; y
< term
->linecount
; ++y
) term
->line
[y
] = calloc(term
->col
, sizeof(Glyph
));
452 for (int y
= 0; y
< term
->row
; ++y
) term
->alt
[y
] = calloc(term
->col
, sizeof(Glyph
));