alot of renaming...
[k8sterm.git] / src / ttyutils.c
blobd419cfbc55dab4f436a3ed831ae0dc674258defd
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) {
18 term->c.x = x;
19 term->c.y = y;
20 k8t_tmWantRedraw(term, 0);
25 static void k8t_tmClearRegion (K8Term *term, int x1, int y1, int x2, int y2) {
26 int temp;
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));
45 l[x].c[0] = ' ';
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) {
65 if (term != NULL) {
66 K8T_LIMIT(maxh, 0, 65535);
67 if (term->maxhistory < maxh) {
68 K8TLine *nl;
69 int newlc = term->linecount+(maxh-term->maxhistory);
71 // add history lines
72 if ((nl = realloc(term->line, sizeof(K8TLine)*newlc)) != NULL) {
73 K8TGlyph g;
75 term->topline = 0;
76 term->line = nl;
77 g.state = K8T_GLYPH_DIRTY;
78 g.attr = K8T_ATTR_NULL|K8T_ATTR_DEFFG|K8T_ATTR_DEFBG;
79 g.fg = term->deffg;
80 g.bg = term->defbg;
81 g.c[0] = ' ';
82 g.c[1] = 0;
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) {
91 K8TLine *nl;
93 term->topline = 0;
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) {
104 k8t_selHide(term);
105 k8t_tmDoWrap(term);
106 for (int f = 0; f < term->row; ++f) {
107 K8TLine t = term->line[f];
109 term->line[f] = term->alt[f];
110 term->alt[f] = t;
112 term->mode ^= K8T_MODE_ALTSCREEN;
113 k8t_tmFullDirty(term);
114 term->justSwapped = 1;
118 //FIXME: works bad with history
119 //FIXME: ugly code
120 static void k8t_tmScrollSelection (K8Term *term, int orig, int n, int tohistory) {
121 int docopy = 0;
123 if (term->sel.bx == -1) return;
125 k8t_tmFullDirty(term); // just in case
126 if (!tohistory) {
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) {
129 k8t_selClear(term);
130 return;
132 if (term->sel.by < term->top) {
133 term->sel.by = term->top;
134 term->sel.bx = 0;
135 docopy = 1;
137 if (term->sel.ey > term->bot) {
138 term->sel.ey = term->bot;
139 term->sel.ex = term->col;
140 docopy = 1;
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;
147 } else {
148 // tohistory!=0; always scrolls full screen up (n == -1)
149 //fprintf(stderr, "k8t_tmScrollSelection to history\n");
150 term->sel.by += n;
151 term->sel.ey += 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
155 k8t_selClear(term);
156 return;
158 if (term->sel.by < 0 && -(term->sel.by) > term->maxhistory) {
159 term->sel.by = -term->maxhistory;
160 term->sel.bx = 0;
161 docopy = 1;
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) {
174 K8TLine temp;
176 K8T_LIMIT(n, 0, term->bot-orig+1);
177 if (n < 1) return;
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) {
192 K8TLine temp;
194 if (term == NULL) return;
195 K8T_LIMIT(n, 0, term->bot-orig+1);
196 if (n < 1) return;
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];
204 } else {
205 tohistory = 0;
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) {
230 int y = term->c.y;
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) {
239 char ub[UTF_SIZ];
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) {
246 uint32_t cc;
248 k8t_UTF8Decode(&cc, c);
249 if (cc <= 65535) {
250 uint16_t uc = unimap[cc];
252 //fprintf(stderr, "unimap: %d [%02X] 0x%04x -> 0x%04x\n", (unsigned char)c[0], cc, uc);
253 if (uc) {
254 if (uc == 127) {
255 // inversed space
256 rev = 1;
257 ub[0] = ' ';
258 } else {
259 if (uc&0x8000) {
260 ub[0] = (uc&0x7f);
261 gfx = 1;
262 } else {
263 k8t_UTF8Encode(ub, uc);
266 c = ub;
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;
276 } else {
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;
296 int dst = term->c.x;
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);
302 } else {
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) {
310 int src = term->c.x;
311 int dst = src+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);
317 } else {
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) {
337 int temp;
339 K8T_LIMIT(t, 0, term->row-1);
340 K8T_LIMIT(b, 0, term->row-1);
341 if (t > b) {
342 temp = t;
343 t = b;
344 b = temp;
346 term->top = t;
347 term->bot = b;
351 static void k8t_tmUnshowHistory (K8Term *term) {
352 if (term != NULL && term->topline != 0) {
353 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 ////////////////////////////////////////////////////////////////////////////////
368 // term utilities
369 static void k8t_tmResetMode (K8Term *term) {
370 term->esc = 0;
371 term->top = 0;
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) {
385 K8TGlyph g;
387 term->c = (K8TCursor){{
388 .attr = K8T_ATTR_NULL|K8T_ATTR_DEFFG|K8T_ATTR_DEFBG,
389 .fg = 0,
390 .bg = 0
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;
397 g.c[0] = ' ';
398 g.c[1] = 0;
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;
410 term->topline = 0;
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;
424 term->row = row;
425 term->col = col;
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));
433 /* setup screen */
434 k8t_tmReset(term);
435 return 1;
439 ////////////////////////////////////////////////////////////////////////////////
440 // tty resising
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;
444 K8TGlyph g;
446 if (term == NULL || col < 1 || row < 1) return 0;
448 k8t_selHide(term);
450 g.state = K8T_GLYPH_DIRTY;
451 g.attr = K8T_ATTR_NULL|K8T_ATTR_DEFFG|K8T_ATTR_DEFBG;
452 g.fg = term->deffg;
453 g.bg = term->defbg;
454 g.c[0] = ' ';
455 g.c[1] = 0;
457 if (slide > 0) {
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));
474 /* add more lines */
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;
495 if (f < row) {
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 */
502 term->topline = 0;
503 term->col = col;
504 term->row = row;
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);
510 return (slide > 0);
514 ////////////////////////////////////////////////////////////////////////////////
515 // tty resize ioctl
516 static void k8t_ttyResize (K8Term *term) {
517 if (term != NULL && term->cmdfd >= 0) {
518 struct winsize w;
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);