From 20c6d217364a5da8407830b42211058be041906b Mon Sep 17 00:00:00 2001 From: mendrak Date: Sun, 17 Nov 2024 21:53:57 -0700 Subject: [PATCH 1/3] Support scrollback notification in callback These changes support notifications that allow an app to know if lines have been scrolled out of the window. This allows a higher level system to maintain a scrollback buffer. --- .gitignore | 2 ++ tmt.c | 56 +++++++++++++++++++++++++++++++++++++++++++++++++++++- tmt.h | 4 +++- 3 files changed, 60 insertions(+), 2 deletions(-) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1e10ead --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +*.o +*.os diff --git a/tmt.c b/tmt.c index 7ace141..d8aa99f 100644 --- a/tmt.c +++ b/tmt.c @@ -59,6 +59,8 @@ struct TMT{ TMTSCREEN screen; TMTLINE *tabs; + TMTSCREEN scroll; + TMTCALLBACK cb; void *p; const wchar_t *acschars; @@ -116,6 +118,16 @@ clearlines(TMT *vt, size_t r, size_t n) clearline(vt, vt->screen.lines[i], 0, vt->screen.ncol); } +static void +savescroll(TMT *vt, TMTLINE **lines, size_t n) +{ + for (int i=0; iscroll.lines[i]->dirty = true; + memcpy(vt->scroll.lines[i]->chars, lines[i]->chars, vt->screen.ncol * sizeof(TMTCHAR)); + } + CB(vt, TMT_MSG_SCROLL, &vt->scroll); +} + static void scrup(TMT *vt, size_t r, size_t n) { @@ -130,6 +142,9 @@ scrup(TMT *vt, size_t r, size_t n) memcpy(vt->screen.lines + (vt->screen.nline - n), buf, n * sizeof(TMTLINE *)); + if (r == 0) { + savescroll(vt, buf, n); + } clearlines(vt, vt->screen.nline - n, n); dirtylines(vt, r, vt->screen.nline); } @@ -161,7 +176,9 @@ HANDLER(ed) switch (P0(0)){ case 0: b = c->r + 1; clearline(vt, l, c->c, vt->screen.ncol); break; case 1: e = c->r - 1; clearline(vt, l, 0, c->c); break; - case 2: /* use defaults */ break; + case 2: + savescroll(vt, vt->screen.lines, vt->screen.nline); + break; default: /* do nothing */ return; } @@ -315,10 +332,12 @@ handlechar(TMT *vt, char i) } static void +//notify(TMT *vt, bool update, bool moved, bool scroll) notify(TMT *vt, bool update, bool moved) { if (update) CB(vt, TMT_MSG_UPDATE, &vt->screen); if (moved) CB(vt, TMT_MSG_MOVED, &vt->curs); + //if (scroll) CB(vt, TMT_MSG_SCROLL, &vt->scroll); } static TMTLINE * @@ -339,6 +358,12 @@ freelines(TMT *vt, size_t s, size_t n, bool screen) vt->screen.lines[i] = NULL; } if (screen) free(vt->screen.lines); + + for (size_t i = s; vt->scroll.lines && i < s + n; i++){ + free(vt->scroll.lines[i]); + vt->scroll.lines[i] = NULL; + } + if (screen) free(vt->scroll.lines); } TMT * @@ -386,10 +411,29 @@ tmt_resize(TMT *vt, size_t nline, size_t ncol) nl = allocline(vt, vt->screen.lines[i], ncol, pc); if (!nl) return false; + vt->screen.lines[i] = nl; } vt->screen.nline = nline; + TMTLINE **sl = realloc(vt->scroll.lines, nline * sizeof(TMTLINE *)); + if (!sl) return false; + + vt->scroll.lines = sl; + vt->scroll.ncol = ncol; + for (size_t i = 0; i < nline; i++){ + TMTLINE *nl = NULL; + if (i >= vt->scroll.nline) + nl = vt->scroll.lines[i] = allocline(vt, NULL, ncol, 0); + else + nl = allocline(vt, vt->scroll.lines[i], ncol, pc); + + if (!nl) return false; + nl->dirty = false; + vt->scroll.lines[i] = nl; + } + vt->scroll.nline = nline; + vt->tabs = allocline(vt, vt->tabs, ncol, 0); if (!vt->tabs) return free(l), false; vt->tabs->chars[0].c = vt->tabs->chars[ncol - 1].c = L'*'; @@ -398,6 +442,7 @@ tmt_resize(TMT *vt, size_t nline, size_t ncol) fixcursor(vt); dirtylines(vt, 0, nline); + //notify(vt, true, true, false); notify(vt, true, true); return true; } @@ -470,6 +515,7 @@ tmt_write(TMT *vt, const char *s, size_t n) } } + //notify(vt, vt->dirty, memcmp(&oc, &vt->curs, sizeof(oc)) != 0, (vt->scroll.lines[0]->dirty)); notify(vt, vt->dirty, memcmp(&oc, &vt->curs, sizeof(oc)) != 0); } @@ -492,6 +538,13 @@ tmt_clean(TMT *vt) vt->dirty = vt->screen.lines[i]->dirty = false; } +void +tmt_clean_scroll(TMT *vt) +{ + for (size_t i = 0; i < vt->scroll.nline; i++) + vt->scroll.lines[i]->dirty = false; +} + void tmt_reset(TMT *vt) { @@ -501,5 +554,6 @@ tmt_reset(TMT *vt) memset(&vt->ms, 0, sizeof(vt->ms)); clearlines(vt, 0, vt->screen.nline); CB(vt, TMT_MSG_CURSOR, "t"); + //notify(vt, true, true, false); notify(vt, true, true); } diff --git a/tmt.h b/tmt.h index ae0ddbb..7e1dfc9 100644 --- a/tmt.h +++ b/tmt.h @@ -121,7 +121,8 @@ typedef enum{ TMT_MSG_UPDATE, TMT_MSG_ANSWER, TMT_MSG_BELL, - TMT_MSG_CURSOR + TMT_MSG_CURSOR, + TMT_MSG_SCROLL } tmt_msg_t; typedef void (*TMTCALLBACK)(tmt_msg_t m, struct TMT *v, const void *r, void *p); @@ -135,6 +136,7 @@ void tmt_write(TMT *vt, const char *s, size_t n); const TMTSCREEN *tmt_screen(const TMT *vt); const TMTPOINT *tmt_cursor(const TMT *vt); void tmt_clean(TMT *vt); +void tmt_clean_scroll(TMT *vt); void tmt_reset(TMT *vt); #endif From c8eb43e23172e08aca309decbaab8579d1855e2b Mon Sep 17 00:00:00 2001 From: markeel Date: Sat, 7 Dec 2024 14:44:13 -0700 Subject: [PATCH 2/3] Handle windows terminal characteristics Specifically git rid of warnings for the windows VS compiler that doesn't allow the buffer to be allocated dynamically on the stack and change it to malloc and free. Also handle the cursor positioning so that it doesn't go to the next line unless there is actually a character past the end of the buffer, since the windows terminal will send a CRCRLF sequence when the end of the line is reached, and the writecharatcurs method would have already inserted a line. Also the Windows CMD.EXE tries to write out a screen title by doing an OS command using ]0; sequence which the handle_char now silently consumes. --- tmt.c | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/tmt.c b/tmt.c index d8aa99f..31e1abb 100644 --- a/tmt.c +++ b/tmt.c @@ -72,7 +72,7 @@ struct TMT{ size_t pars[PAR_MAX]; size_t npar; size_t arg; - enum {S_NUL, S_ESC, S_ARG} state; + enum {S_NUL, S_ESC, S_ARG, S_OS} state; }; static TMTATTRS defattrs = {.fg = TMT_COLOR_DEFAULT, .bg = TMT_COLOR_DEFAULT}; @@ -134,7 +134,7 @@ scrup(TMT *vt, size_t r, size_t n) n = MIN(n, vt->screen.nline - 1 - r); if (n){ - TMTLINE *buf[n]; + TMTLINE** buf = malloc(n * sizeof(TMTLINE*)); memcpy(buf, vt->screen.lines + r, n * sizeof(TMTLINE *)); memmove(vt->screen.lines + r, vt->screen.lines + r + n, @@ -147,6 +147,7 @@ scrup(TMT *vt, size_t r, size_t n) } clearlines(vt, vt->screen.nline - n, n); dirtylines(vt, r, vt->screen.nline); + free(buf); } } @@ -156,7 +157,7 @@ scrdn(TMT *vt, size_t r, size_t n) n = MIN(n, vt->screen.nline - 1 - r); if (n){ - TMTLINE *buf[n]; + TMTLINE** buf = malloc(n * sizeof(TMTLINE*)); memcpy(buf, vt->screen.lines + (vt->screen.nline - n), n * sizeof(TMTLINE *)); @@ -166,6 +167,7 @@ scrdn(TMT *vt, size_t r, size_t n) clearlines(vt, r, n); dirtylines(vt, r, vt->screen.nline); + free(buf); } } @@ -279,6 +281,7 @@ handlechar(TMT *vt, char i) #define ON(S, C, A) if (vt->state == (S) && strchr(C, i)){ A; return true;} #define DO(S, C, A) ON(S, C, consumearg(vt); if (!vt->ignored) {A;} \ fixcursor(vt); resetparser(vt);); + #define SK(S) if (vt->state == (S)) { return true; } DO(S_NUL, "\x07", CB(vt, TMT_MSG_BELL, NULL)) DO(S_NUL, "\x08", if (c->c) c->c--) @@ -293,6 +296,10 @@ handlechar(TMT *vt, char i) ON(S_ESC, "+*()", vt->ignored = true; vt->state = S_ARG) DO(S_ESC, "c", tmt_reset(vt)) ON(S_ESC, "[", vt->state = S_ARG) + ON(S_ESC, "]", vt->state = S_OS) + ON(S_OS, "\x1b", vt->state = S_ESC) + ON(S_OS, "\x07", vt->state = S_NUL) + SK(S_OS) ON(S_ARG, "\x1b", vt->state = S_ESC) ON(S_ARG, ";", consumearg(vt)) ON(S_ARG, "?", (void)0) @@ -458,21 +465,24 @@ writecharatcurs(TMT *vt, wchar_t w) if (wcwidth(w) < 0) return; #endif + /* If at end of screen, wrap to next line */ + if (c->c >= s->ncol) { + c->c = 0; + c->r++; + } + if (c->r >= s->nline){ + c->r = s->nline - 1; + scrup(vt, 0, 1); + } + CLINE(vt)->chars[vt->curs.c].c = w; CLINE(vt)->chars[vt->curs.c].a = vt->attrs; CLINE(vt)->dirty = vt->dirty = true; - if (c->c < s->ncol - 1) - c->c++; - else{ - c->c = 0; - c->r++; - } + /* Advance cursor to next column + Will wrap if necessary when trying to write next character. */ + c->c++; - if (c->r >= s->nline){ - c->r = s->nline - 1; - scrup(vt, 0, 1); - } } static inline size_t From 8901155da510a93f0362dd032a09e895926d37b2 Mon Sep 17 00:00:00 2001 From: markeel Date: Fri, 24 Jan 2025 15:11:55 -0700 Subject: [PATCH 3/3] Support 4 bit colors and 24 bit colors Support the SGL codes for the "bright" standard colors and also support the code for the complete specification of the color via RGB. This is to handle the requirements of a powershell's use of the ansi terminal codes --- tmt.c | 21 ++++++++++++++++++++- tmt.h | 16 ++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/tmt.c b/tmt.c index 31e1abb..b57d7df 100644 --- a/tmt.c +++ b/tmt.c @@ -221,7 +221,11 @@ HANDLER(el) } HANDLER(sgr) - #define FGBG(c) *(P0(i) < 40? &vt->attrs.fg : &vt->attrs.bg) = c + #define FGBG(c) *(P0(i) < 40? &vt->attrs.fg.code : &vt->attrs.bg.code) = c + #define FGBGB(c) *(P0(i) < 100? &vt->attrs.fg.code : &vt->attrs.bg.code) = c + #define FGBGRED() *(P0(i) < 40? &vt->attrs.fg.red : &vt->attrs.bg.red) = (i < vt->npar-2) ? vt->pars[i+2] : 0 + #define FGBGGRN() *(P0(i) < 40? &vt->attrs.fg.green : &vt->attrs.bg.green) = (i < vt->npar-3) ? vt->pars[i+3] : 0 + #define FGBGBLU() *(P0(i) < 40? &vt->attrs.fg.blue : &vt->attrs.bg.blue) = (i < vt->npar-4) ? vt->pars[i+4] : 0 for (size_t i = 0; i < vt->npar; i++) switch (P0(i)){ case 0: vt->attrs = defattrs; break; case 1: case 22: vt->attrs.bold = P0(0) < 20; break; @@ -239,7 +243,22 @@ HANDLER(sgr) case 35: case 45: FGBG(TMT_COLOR_MAGENTA); break; case 36: case 46: FGBG(TMT_COLOR_CYAN); break; case 37: case 47: FGBG(TMT_COLOR_WHITE); break; + case 38: case 48: if ((i < vt->npar-1) && (vt->pars[i+1] == 2)) { + FGBG(TMT_COLOR_RGB); + FGBGRED(); + FGBGGRN(); + FGBGBLU(); + } + break; case 39: case 49: FGBG(TMT_COLOR_DEFAULT); break; + case 90: case 100: FGBGB(TMT_COLOR_BRIGHT_BLACK); break; + case 91: case 101: FGBGB(TMT_COLOR_BRIGHT_RED); break; + case 92: case 102: FGBGB(TMT_COLOR_BRIGHT_GREEN); break; + case 93: case 103: FGBGB(TMT_COLOR_BRIGHT_YELLOW); break; + case 94: case 104: FGBGB(TMT_COLOR_BRIGHT_BLUE); break; + case 95: case 105: FGBGB(TMT_COLOR_BRIGHT_MAGENTA); break; + case 96: case 106: FGBGB(TMT_COLOR_BRIGHT_CYAN); break; + case 97: case 107: FGBGB(TMT_COLOR_BRIGHT_WHITE); break; } } diff --git a/tmt.h b/tmt.h index 7e1dfc9..b609bea 100644 --- a/tmt.h +++ b/tmt.h @@ -74,7 +74,23 @@ typedef enum{ TMT_COLOR_MAGENTA, TMT_COLOR_CYAN, TMT_COLOR_WHITE, + TMT_COLOR_BRIGHT_BLACK, + TMT_COLOR_BRIGHT_RED, + TMT_COLOR_BRIGHT_GREEN, + TMT_COLOR_BRIGHT_YELLOW, + TMT_COLOR_BRIGHT_BLUE, + TMT_COLOR_BRIGHT_MAGENTA, + TMT_COLOR_BRIGHT_CYAN, + TMT_COLOR_BRIGHT_WHITE, + TMT_COLOR_RGB, TMT_COLOR_MAX +} tmt_color_code_t; + +typedef struct { + tmt_color_code_t code; + unsigned char red; + unsigned char green; + unsigned char blue; } tmt_color_t; typedef struct TMTATTRS TMTATTRS;