do wrapping on screen swap
[k8sterm.git] / src / ttyutils.c
blobea335c4274b2a64e68f08684d7b387b1a845c14d
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) {
18 term->c.x = x;
19 term->c.y = y;
20 setWantRedraw(term, 0);
25 static void tclearregion (Term *term, int x1, int y1, int x2, int y2) {
26 int temp;
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));
45 l[x].c[0] = ' ';
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) {
65 if (term != NULL) {
66 LIMIT(maxh, 0, 65535);
67 if (term->maxhistory < maxh) {
68 Line *nl;
69 int newlc = term->linecount+(maxh-term->maxhistory);
71 // add history lines
72 if ((nl = realloc(term->line, sizeof(Line)*newlc)) != NULL) {
73 Glyph g;
75 term->topline = 0;
76 term->line = nl;
77 g.state = GLYPH_DIRTY;
78 g.attr = ATTR_NULL|ATTR_DEFFG|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(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) {
91 Line *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(Line)*term->linecount)) != NULL) term->line = nl;
97 term->maxhistory = maxh;
103 static void tswapscreen (Term *term) {
104 selhide(term);
105 tdowrap(term);
106 for (int f = 0; f < term->row; ++f) {
107 Line t = term->line[f];
109 term->line[f] = term->alt[f];
110 term->alt[f] = t;
112 term->mode ^= MODE_ALTSCREEN;
113 tfulldirt(term);
114 term->justSwapped = 1;
118 //FIXME: works bad with history
119 //FIXME: ugly code
120 static void selscroll (Term *term, int orig, int n, int tohistory) {
121 int docopy = 0;
123 if (term->sel.bx == -1) return;
125 tfulldirt(term); // just in case
126 if (!tohistory) {
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) {
129 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, "selscroll 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 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) selcopy(term);
173 static void tscrolldown (Term *term, int orig, int n) {
174 Line temp;
176 LIMIT(n, 0, term->bot-orig+1);
177 if (n < 1) return;
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) {
192 Line temp;
194 if (term == NULL) return;
195 LIMIT(n, 0, term->bot-orig+1);
196 if (n < 1) return;
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];
204 } else {
205 tohistory = 0;
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) {
230 int y = term->c.y;
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) {
250 ++term->escseq.narg;
251 ++p;
252 } else {
253 term->escseq.mode = *p;
254 ++term->escseq.narg;
255 break;
261 static void tsetchar (Term *term, const char *c) {
262 char ub[UTF_SIZ];
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) {
269 uint32 cc;
271 utf8decode(&cc, c);
272 if (cc <= 65535) {
273 ushort uc = unimap[cc];
275 //fprintf(stderr, "unimap: %d [%02X] 0x%04x -> 0x%04x\n", (unsigned char)c[0], cc, uc);
276 if (uc) {
277 if (uc == 127) {
278 // inversed space
279 rev = 1;
280 ub[0] = ' ';
281 } else {
282 if (uc&0x8000) {
283 ub[0] = (uc&0x7f);
284 gfx = 1;
285 } else {
286 utf8encode(ub, uc);
289 c = ub;
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;
299 } else {
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;
319 int dst = term->c.x;
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);
325 } else {
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) {
333 int src = term->c.x;
334 int dst = src+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);
340 } else {
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) {
360 int temp;
362 LIMIT(t, 0, term->row-1);
363 LIMIT(b, 0, term->row-1);
364 if (t > b) {
365 temp = t;
366 t = b;
367 b = temp;
369 term->top = t;
370 term->bot = b;
374 static void tunshowhistory (Term *term) {
375 if (term != NULL && term->topline != 0) {
376 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 ////////////////////////////////////////////////////////////////////////////////
391 // tty utilities
392 static void tresetmode (Term *term) {
393 term->esc = 0;
394 term->top = 0;
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;
401 tresetattrs(term);
403 tcursor(term, CURSOR_SAVE);
407 static void treset (Term *term) {
408 Glyph g;
410 term->c = (TCursor){{
411 .attr = ATTR_NULL|ATTR_DEFFG|ATTR_DEFBG,
412 .fg = 0,
413 .bg = 0
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;
420 g.c[0] = ' ';
421 g.c[1] = 0;
423 tresetmode(term);
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;
433 term->topline = 0;
434 tfulldirt(term);
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;
444 term->row = row;
445 term->col = col;
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));
453 /* setup screen */
454 treset(term);
455 return 1;