1 static void k8t_tmDirty (K8Term
*term
, int top
, int bot
) {
2 K8T_LIMIT(top
, 0, term
->row
-1);
3 K8T_LIMIT(bot
, 0, term
->row
-1);
4 for (int y
= top
; y
<= bot
; ++y
) k8t_tmDirtyMark(term
, y
, 2);
8 static void k8t_tmFullDirty (K8Term
*term
) {
9 k8t_tmDirty(term
, 0, term
->row
-1);
13 static void k8t_tmMoveTo (K8Term
*term
, int x
, int y
) {
14 K8T_LIMIT(x
, 0, term
->col
-1);
15 K8T_LIMIT(y
, 0, term
->row
-1);
16 term
->c
.state
&= ~K8T_CURSOR_WRAPNEXT
;
17 if (term
->c
.x
!= x
|| term
->c
.y
!= y
) {
20 k8t_tmWantRedraw(term
, 0);
25 static void k8t_tmClearRegion (K8Term
*term
, int x1
, int y1
, int x2
, int y2
) {
28 //fprintf(stderr, "k8t_tmClearRegion: (%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 K8T_LIMIT(x1
, 0, term
->col
-1);
32 K8T_LIMIT(x2
, 0, term
->col
-1);
33 K8T_LIMIT(y1
, 0, term
->row
-1);
34 K8T_LIMIT(y2
, 0, term
->row
-1);
35 //fprintf(stderr, " k8t_tmClearRegion: (%d,%d)-(%d,%d)\n", x1, y1, x2, y2);
36 for (int y
= y1
; y
<= y2
; ++y
) {
37 K8TLine l
= term
->line
[y
];
39 k8t_tmDirtyMark(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
= K8T_GLYPH_DIRTY
;
44 l
[x
].attr
= K8T_ATTR_NULL
|(term
->c
.attr
.attr
&(K8T_ATTR_DEFFG
|K8T_ATTR_DEFBG
));
46 if (term
->sel
.bx
!= -1 && k8t_isSelected(term
, x
, y
)) k8t_selHide(term
);
48 l
[term
->col
-1].state
&= ~K8T_GLYPH_WRAP
;
53 static void k8t_tmCursor (K8Term
*term
, int mode
) {
54 if (mode
== K8T_CURSOR_SAVE
) {
55 term
->csaved
= term
->c
;
56 } else if (mode
== K8T_CURSOR_LOAD
) {
57 term
->c
= term
->csaved
;
58 k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
);
59 k8t_tmWantRedraw(term
, 0);
64 static void k8t_tmAdjMaxHistory (K8Term
*term
, int maxh
) {
66 K8T_LIMIT(maxh
, 0, 65535);
67 if (term
->maxhistory
< maxh
) {
69 int newlc
= term
->linecount
+(maxh
-term
->maxhistory
);
72 if ((nl
= realloc(term
->line
, sizeof(K8TLine
)*newlc
)) != NULL
) {
77 g
.state
= K8T_GLYPH_DIRTY
;
78 g
.attr
= K8T_ATTR_NULL
|K8T_ATTR_DEFFG
|K8T_ATTR_DEFBG
;
83 for (int y
= term
->linecount
; y
< newlc
; ++y
) {
84 term
->line
[y
] = calloc(term
->col
, sizeof(K8TGlyph
));
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(K8TLine
)*term
->linecount
)) != NULL
) term
->line
= nl
;
97 term
->maxhistory
= maxh
;
103 static void k8t_tmSwapScreen (K8Term
*term
) {
106 for (int f
= 0; f
< term
->row
; ++f
) {
107 K8TLine t
= term
->line
[f
];
109 term
->line
[f
] = term
->alt
[f
];
112 term
->mode
^= K8T_MODE_ALTSCREEN
;
113 k8t_tmFullDirty(term
);
114 term
->justSwapped
= 1;
118 //FIXME: works bad with history
120 static void k8t_tmScrollSelection (K8Term
*term
, int orig
, int n
, int tohistory
) {
123 if (term
->sel
.bx
== -1) return;
125 k8t_tmFullDirty(term
); // just in case
127 if (K8T_BETWEEN(term
->sel
.by
, orig
, term
->bot
) || K8T_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, "k8t_tmScrollSelection 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
) k8t_selCopy(term
);
173 static void k8t_tmScrollDown (K8Term
*term
, int orig
, int n
) {
176 K8T_LIMIT(n
, 0, term
->bot
-orig
+1);
178 k8t_tmScrollSelection(term
, orig
, n
, 0);
179 //fprintf(stderr, "k8t_tmScrollDown(%d, %d)\n", orig, n);
180 k8t_tmClearRegion(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 k8t_tmDirtyMark(term
, f
, 2);
186 k8t_tmDirtyMark(term
, f
-n
, 2);
191 static void k8t_tmScrollUp (K8Term
*term
, int orig
, int n
, int tohistory
) {
194 if (term
== NULL
) return;
195 K8T_LIMIT(n
, 0, term
->bot
-orig
+1);
197 //fprintf(stderr, "k8t_tmScrollUp(%d, %d)\n", orig, n);
198 if (tohistory
&& !K8T_ISSET(term
, K8T_MODE_ALTSCREEN
) && term
->maxhistory
> 0) {
199 K8TLine 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 k8t_tmScrollSelection(term
, orig
, -n
, tohistory
);
209 //k8t_tmClearRegion(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 k8t_tmDirtyMark(term
, f
, 2);
215 k8t_tmDirtyMark(term
, f
+n
, 2);
217 k8t_tmClearRegion(term
, 0, term
->bot
-n
+1, term
->col
-1, term
->bot
);
221 static void k8t_tmCharWrap (K8Term
*term
, int y
, int wrap
) {
222 if (y
>= 0 && y
< term
->row
) {
223 if (wrap
) term
->line
[y
][term
->col
-1].state
|= K8T_GLYPH_WRAP
;
224 else term
->line
[y
][term
->col
-1].state
&= ~K8T_GLYPH_WRAP
;
229 static void k8t_tmNewLine (K8Term
*term
, int first_col
) {
232 k8t_tmCharWrap(term
, y
, (first_col
== 2)); // 2: wrapping
233 if (y
== term
->bot
) k8t_tmScrollUp(term
, term
->top
, 1, 1); else ++y
;
234 k8t_tmMoveTo(term
, (first_col
? 0 : term
->c
.x
), y
);
238 static void k8t_tmSetChar (K8Term
*term
, const char *c
) {
240 int rev
= 0, gfx
= 0;
241 int x
= term
->c
.x
, y
= term
->c
.y
;
243 if (x
< 0 || x
>= term
->col
|| y
< 0 || y
>= term
->row
) return;
245 if (!term
->needConv
&& unimap
!= NULL
&& (unsigned char)c
[0] >= 0x80) {
248 k8t_UTF8Decode(&cc
, c
);
250 uint16_t uc
= unimap
[cc
];
252 //fprintf(stderr, "unimap: %d [%02X] 0x%04x -> 0x%04x\n", (unsigned char)c[0], cc, uc);
263 k8t_UTF8Encode(ub
, uc
);
270 k8t_tmDirtyMark(term
, y
, 1);
272 term
->line
[y
][x
] = term
->c
.attr
;
273 if (rev
) term
->line
[y
][x
].attr
^= K8T_ATTR_REVERSE
;
274 if (gfx
|| (term
->mode
&term
->charset
)) {
275 term
->line
[y
][x
].attr
|= K8T_ATTR_GFX
;
277 term
->line
[y
][x
].attr
&= ~K8T_ATTR_GFX
;
280 term
->line
[y
][x
].state
= (K8T_GLYPH_SET
|K8T_GLYPH_DIRTY
);
281 memcpy(term
->line
[y
][x
].c
, c
, UTF_SIZ
);
283 if (K8T_ISGFX(term
->line
[y
][x
].attr
)) {
284 unsigned char c
= (unsigned char)(term
->line
[y
][x
].c
[0]);
286 if (c
> 95 && c
< 128) term
->line
[y
][x
].c
[0] -= 95;
287 else if (c
> 127) term
->line
[y
][x
].c
[0] = ' ';
289 if (term
->sel
.bx
!= -1 && k8t_isSelected(term
, x
, y
)) k8t_selHide(term
);
290 //dlogf("k8t_tmSetChar(%d,%d): [%c] (%d); dirty=%d\n", x, y, c[0], term->wantRedraw, term->dirty[y]);
294 static void k8t_tmDeleteChar (K8Term
*term
, int n
) {
295 int src
= term
->c
.x
+n
;
297 int size
= term
->col
-src
;
299 k8t_tmDirtyMark(term
, term
->c
.y
, 2);
300 if (src
>= term
->col
) {
301 k8t_tmClearRegion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
303 memmove(&term
->line
[term
->c
.y
][dst
], &term
->line
[term
->c
.y
][src
], size
*sizeof(K8TGlyph
));
304 k8t_tmClearRegion(term
, term
->col
-n
, term
->c
.y
, term
->col
-1, term
->c
.y
);
309 static void k8t_tmInsertBlank (K8Term
*term
, int n
) {
312 int size
= term
->col
-dst
;
314 k8t_tmDirtyMark(term
, term
->c
.y
, 2);
315 if (dst
>= term
->col
) {
316 k8t_tmClearRegion(term
, term
->c
.x
, term
->c
.y
, term
->col
-1, term
->c
.y
);
318 memmove(&term
->line
[term
->c
.y
][dst
], &term
->line
[term
->c
.y
][src
], size
*sizeof(K8TGlyph
));
319 k8t_tmClearRegion(term
, src
, term
->c
.y
, dst
-1, term
->c
.y
);
324 static void k8t_tmInsertBlankLine (K8Term
*term
, int n
) {
325 if (term
->c
.y
< term
->top
|| term
->c
.y
> term
->bot
) return;
326 k8t_tmScrollDown(term
, term
->c
.y
, n
);
330 static void k8t_tmDeleteLine (K8Term
*term
, int n
) {
331 if (term
->c
.y
< term
->top
|| term
->c
.y
> term
->bot
) return;
332 k8t_tmScrollUp(term
, term
->c
.y
, n
, 0);
336 static void k8t_tmSetScrollRegion (K8Term
*term
, int t
, int b
) {
339 K8T_LIMIT(t
, 0, term
->row
-1);
340 K8T_LIMIT(b
, 0, term
->row
-1);
351 static void k8t_tmUnshowHistory (K8Term
*term
) {
352 if (term
!= NULL
&& term
->topline
!= 0) {
354 k8t_tmWantRedraw(term
, 1);
359 static void k8t_tmSendFocusEvent (K8Term
*term
, int focused
) {
360 if (term
!= NULL
&& K8T_ISSET(term
, K8T_MODE_FOCUSEVT
)) {
361 k8t_ttyWriteStr(term
, "\x1b[");
362 k8t_ttyWrite(term
, focused
?"I":"O", 1);
367 ////////////////////////////////////////////////////////////////////////////////
369 static void k8t_tmResetMode (K8Term
*term
) {
372 term
->bot
= term
->row
-1;
373 term
->mode
= K8T_MODE_WRAP
/*|K8T_MODE_MOUSEBTN*//*|K8T_MODE_GFX1*/;
374 term
->mousemode
= 1000;
375 term
->charset
= K8T_MODE_GFX0
;
377 term
->c
.state
= K8T_CURSOR_DEFAULT
;
378 k8t_tmResetAttrs(term
);
380 k8t_tmCursor(term
, K8T_CURSOR_SAVE
);
384 static void k8t_tmReset (K8Term
*term
) {
387 term
->c
= (K8TCursor
){{
388 .attr
= K8T_ATTR_NULL
|K8T_ATTR_DEFFG
|K8T_ATTR_DEFBG
,
391 }, .x
= 0, .y
= 0, .state
= K8T_CURSOR_DEFAULT
};
393 g
.state
= K8T_GLYPH_DIRTY
;
394 g
.attr
= term
->c
.attr
.attr
;
395 g
.fg
= term
->c
.attr
.fg
;
396 g
.bg
= term
->c
.attr
.bg
;
400 k8t_tmResetMode(term
);
401 //k8t_tmClearRegion(0, 0, term->col-1, term->row-1);
402 for (int y
= 0; y
< term
->row
; ++y
) {
403 k8t_tmDirtyMark(term
, y
, 2);
404 for (int x
= 0; x
< term
->col
; ++x
) term
->alt
[y
][x
] = term
->line
[y
][x
] = g
;
406 for (int y
= term
->row
; y
< term
->linecount
; ++y
) {
407 for (int x
= 0; x
< term
->col
; ++x
) term
->line
[y
][x
] = g
;
411 k8t_tmFullDirty(term
);
415 static int k8t_tmInitialize (K8Term
*term
, int col
, int row
) {
416 //memset(term, 0, sizeof(K8Term));
417 //term->needConv = needConversion ? 1 : 0;
418 term
->dumpescapes
= 0;
419 term
->dumpeskeys
= 0;
420 term
->ignoredecpad
= opt_ignoredecpad
;
421 term
->wrbufsize
= WBUFSIZ
;
422 term
->deffg
= term
->deffg
;
423 term
->defbg
= term
->defbg
;
426 term
->dirty
= calloc(term
->row
, sizeof(*term
->dirty
));
427 term
->maxhistory
= opt_maxhistory
;
428 term
->linecount
= term
->maxhistory
+term
->row
;
429 term
->line
= calloc(term
->linecount
, sizeof(K8TLine
));
430 term
->alt
= calloc(term
->row
, sizeof(K8TLine
));
431 for (int y
= 0; y
< term
->linecount
; ++y
) term
->line
[y
] = calloc(term
->col
, sizeof(K8TGlyph
));
432 for (int y
= 0; y
< term
->row
; ++y
) term
->alt
[y
] = calloc(term
->col
, sizeof(K8TGlyph
));
439 ////////////////////////////////////////////////////////////////////////////////
441 static int k8t_tmResize (K8Term
*term
, int col
, int row
) {
442 int mincol
= K8T_MIN(col
, term
->col
);
443 int slide
= term
->c
.y
-row
+1;
446 if (term
== NULL
|| col
< 1 || row
< 1) return 0;
450 g
.state
= K8T_GLYPH_DIRTY
;
451 g
.attr
= K8T_ATTR_NULL
|K8T_ATTR_DEFFG
|K8T_ATTR_DEFBG
;
458 k8t_tmSetScrollRegion(term
, 0, term
->row
-1);
459 for (; slide
> 0; --slide
) k8t_tmScrollUp(term
, 0, 1, 1); // to fill history
462 if (row
< term
->row
) {
463 /* free unneeded rows */
464 for (int f
= row
; f
< term
->row
; ++f
) free(term
->alt
[f
]);
465 for (int f
= term
->linecount
-(term
->row
-row
); f
< term
->linecount
; ++f
) free(term
->line
[f
]);
466 term
->linecount
-= (term
->row
-row
);
467 /* resize to new height */
468 term
->alt
= realloc(term
->alt
, row
*sizeof(K8TLine
));
469 term
->line
= realloc(term
->line
, term
->linecount
*sizeof(K8TLine
));
470 } else if (row
> term
->row
) {
471 /* resize to new height */
472 term
->alt
= realloc(term
->alt
, row
*sizeof(K8TLine
));
473 term
->line
= realloc(term
->line
, (row
+term
->maxhistory
)*sizeof(K8TLine
));
475 for (int f
= term
->row
; f
< row
; ++f
) {
476 term
->alt
[f
] = calloc(col
, sizeof(K8TGlyph
));
477 for (int x
= 0; x
< col
; ++x
) term
->alt
[f
][x
] = g
;
479 for (int f
= 0; f
< row
-term
->row
; ++f
) {
480 int y
= term
->linecount
++;
482 term
->line
[y
] = calloc(col
, sizeof(K8TGlyph
));
483 for (int x
= 0; x
< col
; ++x
) term
->line
[y
][x
] = g
;
487 if (row
!= term
->row
) {
488 term
->dirty
= realloc(term
->dirty
, row
*sizeof(*term
->dirty
));
491 /* resize each row to new width, zero-pad if needed */
492 for (int f
= 0; f
< term
->linecount
; ++f
) {
493 term
->line
[f
] = realloc(term
->line
[f
], col
*sizeof(K8TGlyph
));
494 for (int x
= mincol
; x
< col
; ++x
) term
->line
[f
][x
] = g
;
496 k8t_tmDirtyMark(term
, f
, 2);
497 term
->alt
[f
] = realloc(term
->alt
[f
], col
*sizeof(K8TGlyph
));
498 for (int x
= mincol
; x
< col
; ++x
) term
->alt
[f
][x
] = g
;
501 /* update terminal size */
505 /* make use of the limit in k8t_tmMoveTo */
506 k8t_tmMoveTo(term
, term
->c
.x
, term
->c
.y
);
507 /* reset scrolling region */
508 k8t_tmSetScrollRegion(term
, 0, row
-1);
509 k8t_tmFullDirty(term
);
514 ////////////////////////////////////////////////////////////////////////////////
516 static void k8t_ttyResize (K8Term
*term
) {
517 if (term
!= NULL
&& term
->cmdfd
>= 0) {
520 w
.ws_row
= term
->row
;
521 w
.ws_col
= term
->col
;
522 w
.ws_xpixel
= w
.ws_ypixel
= 0;
523 if (ioctl(term
->cmdfd
, TIOCSWINSZ
, &w
) < 0) fprintf(stderr
, "Warning: couldn't set window size: %s\n", strerror(errno
));
524 k8t_tmWantRedraw(term
, 1);