Author: mesyeti <mesyeti@mesyeti.uk>
colour sequences working
Makefile | 5 source/app.c | 10 + source/app.h | 1 source/backends/soft.c | 5 source/interpreter.c | 10 + source/interpreter.h | 18 +++ source/interpreter/vt100.c | 205 ++++++++++++++++++++++++++++++++++++++++ source/interpreter/vt100.h | 8 + source/pane.c | 16 +++ source/pane.h | 1 source/string.c | 52 ++++++++++ source/string.h | 18 +++ source/sysTerm/unix.c | 3 source/terminal.c | 36 +++--- source/terminal.h | 3 source/util.c | 34 ++++++ source/util.h | 1
diff --git a/Makefile b/Makefile index f4f53aec95303251c4b14910b02a489ccfda6a5a..a5ea18a5dd6246d77cbb7330d59e95141f1a9bfa 100644 --- a/Makefile +++ b/Makefile @@ -80,7 +80,10 @@ bin/sysTerm: mkdir -p bin/sysTerm -bin/%.o: source/%.c $(call deps,source/%.c) | bin/ bin/backends bin/input bin/window bin/event bin/platform bin/sysTerm +bin/interpreter: + mkdir -p bin/interpreter + +bin/%.o: source/%.c $(call deps,source/%.c) | bin/ bin/backends bin/input bin/window bin/event bin/platform bin/sysTerm bin/interpreter $(CC) $(CFLAGS) $(CPPFLAGS) $< -c -o $@ clean: diff --git a/source/app.c b/source/app.c index f7365e829e0fddf9236db61e4e9ad3f9f30ea031..61a9771ed4e29c3c33e456da4f4e80b0389e7843 100644 --- a/source/app.c +++ b/source/app.c @@ -22,6 +22,7 @@ app.tabs = SafeMalloc(sizeof(Pane)); app.tabNum = 1; app.tabSel = 0; app.tabs[0] = Pane_NewRootPane(&success); + app.paneSel = &app.tabs[0]; if (!success) { Error("Failed to start terminal"); @@ -40,7 +41,11 @@ while (Event_Poll(&e)) { switch (e.type) { case EVENT_QUIT: app.running = false; break; - default: continue; + default: { + for (size_t i = 0; i < app.tabNum; ++ i) { + Pane_HandleEvent(&app.tabs[i], &e); + } + } } } @@ -53,8 +58,9 @@ // render Buffer_Clear(&screen.buffer); BufCell bar; bar.ch = ' '; - bar.attr.fgMode = BUF_DEFAULT; + bar.attr.fgMode = BUF_16COL; bar.attr.bgMode = BUF_16COL; + bar.attr.fg.idx = 0x7; // white bar.attr.bg.idx = 0x8; // grey Buffer_HLine(&screen.buffer, 0, 0, screen.buffer.width, bar); diff --git a/source/app.h b/source/app.h index 003d93cb76a631f71806339a6b18763d310b529c..78916f749859d02e2617e78b896c50f35f20704c 100644 --- a/source/app.h +++ b/source/app.h @@ -13,6 +13,7 @@ Pane* tabs; size_t tabNum; size_t tabSel; + Pane* paneSel; } App; extern App app; diff --git a/source/backends/soft.c b/source/backends/soft.c index 508de517c1fb5eeb600ced53aeac8cc04a5de0e9..15d93abf719315ee6a9e80623cf70aa0aba6b225 100644 --- a/source/backends/soft.c +++ b/source/backends/soft.c @@ -172,6 +172,11 @@ uint8_t alpha = Component(srcPix, 3); if (alpha == 0) continue; + // TODO: proper blending + if (srcPix == 0xFFFFFFFF) { + srcPix = ColourToPixel(tint.r, tint.g, tint.b, tint.a); + } + DrawPixel(ix + x, iy + y, srcPix); } } diff --git a/source/interpreter/vt100.c b/source/interpreter/vt100.c new file mode 100644 index 0000000000000000000000000000000000000000..419f0bf47437e6f3a96a2ad1be5faf05560dca54 --- /dev/null +++ b/source/interpreter/vt100.c @@ -0,0 +1,205 @@ +// The interpreter for VT100 and friends + +#include "vt100.h" +#include "../mem.h" +#include "../util.h" + +enum { + MODE_READY = 0, + MODE_ESCAPE, + MODE_CSI +}; + +typedef struct { + int mode; + char reading[1024]; +} Vt100; + +static void SendInput(Interpreter* p_this, Terminal* terminal, Event* e) { + Vt100* this = (Vt100*) p_this->data; + + switch (e->type) { + case EVENT_KEY_DOWN: { + const char* seq = NULL; + + switch (e->key.key) { + case KEY_RETURN: seq = "\n"; break; + default: break; + } + + if (seq) { + for (size_t i = 0; seq[i]; ++ i) { + SysTerm_WriteByte(&terminal->term, seq[i]); + } + } + break; + } + case EVENT_TEXT_INPUT: { + for (size_t i = 0; e->textInput.input[i]; ++ i) { + SysTerm_WriteByte(&terminal->term, e->textInput.input[i]); + } + break; + } + default: break; + } +} + +static void HandleCSI(Vt100* this, Terminal* terminal, UChar ch) { + // parse sequences + const char* str = this->reading; + bool q = false; + + if (*str == '?') { + q = true; + ++ str; + } + + size_t num; + char** parts = Split(str, &num); + + switch (ch) { + case 'm': { + for (size_t i = 0; i < num; ++ i) { + int part = atoi(parts[i]); + + switch (part) { + case 0: { + terminal->attr.fgMode = BUF_DEFAULT; + terminal->attr.bgMode = BUF_DEFAULT; + break; + } + default: { + if (((part >= 30) && (part <= 39)) || ((part >= 90) && (part <= 97))) { + terminal->attr.fgMode = BUF_16COL; + + switch (part) { + case 30: terminal->attr.fg.idx = 0x0; break; // black + case 31: terminal->attr.fg.idx = 0x1; break; // red + case 32: terminal->attr.fg.idx = 0x2; break; // green + case 33: terminal->attr.fg.idx = 0x3; break; // yellow + case 34: terminal->attr.fg.idx = 0x4; break; // blue + case 35: terminal->attr.fg.idx = 0x5; break; // magenta + case 36: terminal->attr.fg.idx = 0x6; break; // cyan + case 37: terminal->attr.fg.idx = 0x7; break; // white + case 39: terminal->attr.fgMode = BUF_DEFAULT; break; + case 90: terminal->attr.fg.idx = 0x8; break; // grey/bright black + case 91: terminal->attr.fg.idx = 0x9; break; // bright red + case 92: terminal->attr.fg.idx = 0xA; break; // bright green + case 93: terminal->attr.fg.idx = 0xB; break; // bright yellow + case 94: terminal->attr.fg.idx = 0xC; break; // bright blue + case 95: terminal->attr.fg.idx = 0xD; break; // bright magenta + case 96: terminal->attr.fg.idx = 0xE; break; // bright cyan + case 97: terminal->attr.fg.idx = 0xF; break; // bright white + default: break; + } + } + else if (((part >= 40) && (part <= 49)) || ((part >= 100) && (part <= 107))) { + terminal->attr.bgMode = BUF_16COL; + + switch (part) { + case 40: terminal->attr.bg.idx = 0x0; break; // black + case 41: terminal->attr.bg.idx = 0x1; break; // red + case 42: terminal->attr.bg.idx = 0x2; break; // green + case 43: terminal->attr.bg.idx = 0x3; break; // yellow + case 44: terminal->attr.bg.idx = 0x4; break; // blue + case 45: terminal->attr.bg.idx = 0x5; break; // magenta + case 46: terminal->attr.bg.idx = 0x6; break; // cyan + case 47: terminal->attr.bg.idx = 0x7; break; // white + case 49: terminal->attr.bgMode = BUF_DEFAULT; break; + case 100: terminal->attr.bg.idx = 0x8; break; // grey/bright black + case 101: terminal->attr.bg.idx = 0x9; break; // bright red + case 102: terminal->attr.bg.idx = 0xA; break; // bright green + case 103: terminal->attr.bg.idx = 0xB; break; // bright yellow + case 104: terminal->attr.bg.idx = 0xC; break; // bright blue + case 105: terminal->attr.bg.idx = 0xD; break; // bright magenta + case 106: terminal->attr.bg.idx = 0xE; break; // bright cyan + case 107: terminal->attr.bg.idx = 0xF; break; // bright white + default: break; + } + } + } + } + } + + break; + } + default: { + printf("Unsupported: %s\n", this->reading); + break; // Unsupported + } + } + + FreeStrArray(parts); +} + +static void OnRead(Interpreter* p_this, Terminal* terminal, UChar ch) { + Vt100* this = (Vt100*) p_this->data; + + switch (this->mode) { + case MODE_READY: { + switch (ch) { + case '\r': { + terminal->cursor.x = 0; + break; + } + case '\n': { + ++ terminal->cursor.y; + terminal->cursor.x = 0; + break; + } + case '\x1b': { + this->mode = MODE_ESCAPE; + break; + } + default: { + BufCell cell; + cell.ch = (UChar) ch; + cell.attr = terminal->attr; + + Buffer_Set(&terminal->buffer, terminal->cursor.x, terminal->cursor.y, cell); + ++ terminal->cursor.x; + } + } + break; + } + case MODE_ESCAPE: { + switch (ch) { + case '[': this->mode = MODE_CSI; break; + default: this->mode = MODE_READY; // invalid + } + break; + } + case MODE_CSI: { + if (strlen(this->reading) > 1022) { + this->mode = MODE_READY; + this->reading[0] = 0; // TODO: use a String for this + break; + } + + if (((ch >= 'A') && (ch <= 'Z')) || ((ch >= 'a') && (ch <= 'z'))) { + this->mode = MODE_READY; + HandleCSI(this, terminal, ch); + + this->reading[0] = 0; + } + else { + char ch2 = (char) ch; + strncat(this->reading, &ch2, 1); + } + break; + } + } +} + +Interpreter Interpreter_VT100(void) { + Vt100* data = SafeMalloc(sizeof(Vt100)); + + data->reading[0] = 0; + data->mode = MODE_READY; + + return (Interpreter) { + .data = (void*) data, + .sendInput = &SendInput, + .onRead = &OnRead + }; +} diff --git a/source/interpreter/vt100.h b/source/interpreter/vt100.h new file mode 100644 index 0000000000000000000000000000000000000000..75db1b81f7f7e2ac8bd468fb31bd2079d87d89b0 --- /dev/null +++ b/source/interpreter/vt100.h @@ -0,0 +1,8 @@ +#ifndef YT_INTERPRETER_VT100_H +#define YT_INTERPRETER_VT100_H + +#include "../interpreter.h" + +Interpreter Interpreter_VT100(void); + +#endif diff --git a/source/interpreter.c b/source/interpreter.c new file mode 100644 index 0000000000000000000000000000000000000000..fe18bd1db4ae3ef5544d579c95dfec4857eefe3e --- /dev/null +++ b/source/interpreter.c @@ -0,0 +1,10 @@ +#include "interpreter.h" +#include "interpreter/vt100.h" + +void Interpreter_SendInput(Interpreter* this, Terminal* terminal, Event* e) { + this->sendInput(this, terminal, e); +} + +void Interpreter_OnRead(Interpreter* this, Terminal* terminal, UChar ch) { + this->onRead(this, terminal, ch); +} diff --git a/source/interpreter.h b/source/interpreter.h new file mode 100644 index 0000000000000000000000000000000000000000..ba25b6460eb506f4f7a42351171fe187658c61b0 --- /dev/null +++ b/source/interpreter.h @@ -0,0 +1,18 @@ +#ifndef YT_INTERPRETER_H +#define YT_INTERPRETER_H + +#include "terminal.h" + +typedef struct Interpreter Interpreter; + +struct Interpreter { + void* data; + + void (*sendInput)(Interpreter* this, Terminal* terminal, Event* e); + void (*onRead)(Interpreter* this, Terminal* terminal, UChar ch); +}; + +void Interpreter_SendInput(Interpreter* this, Terminal* terminal, Event* e); +void Interpreter_OnRead(Interpreter* this, Terminal* terminal, UChar ch); + +#endif diff --git a/source/pane.c b/source/pane.c index 13c465868946c8f3d512734620acd47233b5e022..d992e96a77e0e0ee4448006074481ee2c3748843 100644 --- a/source/pane.c +++ b/source/pane.c @@ -1,3 +1,4 @@ +#include "app.h" #include "pane.h" #include "screen.h" @@ -26,6 +27,21 @@ break; } case PANE_TERMINAL: { Terminal_Update(&pane->v.terminal); + break; + } + default: assert(0); + } +} + +void Pane_HandleEvent(Pane* pane, Event* e) { + switch (pane->type) { + case PANE_SPLIT: { + if (pane->v.split.left) Pane_HandleEvent(pane->v.split.left, e); + if (pane->v.split.right) Pane_HandleEvent(pane->v.split.right, e); + break; + } + case PANE_TERMINAL: { + Terminal_HandleEvent(&pane->v.terminal, e, pane == app.paneSel); break; } default: assert(0); diff --git a/source/pane.h b/source/pane.h index 0e6377dbe3272bb2ccf585244608b0ca3afbe11a..6424033d1fd30f8b656d69f340cfbb8c65d821e4 100644 --- a/source/pane.h +++ b/source/pane.h @@ -28,6 +28,7 @@ Pane Pane_NewRootPane(bool* success); void Pane_Free(Pane* pane); void Pane_Update(Pane* pane); +void Pane_HandleEvent(Pane* pane, Event* e); void Pane_Render(Pane* pane, Rect within); #endif diff --git a/source/string.c b/source/string.c new file mode 100644 index 0000000000000000000000000000000000000000..a0e67677dbb4750e4d07c68341dafa5af273d890 --- /dev/null +++ b/source/string.c @@ -0,0 +1,52 @@ +#include <string.h> +#include "mem.h" +#include "string.h" + +String String_New(const char* src) { + String ret; + ret.str = SafeMalloc(strlen(src) + 1); + ret.cap = strlen(src) + 1; + + strcpy(ret.str, src); + return ret; +} + +void String_Free(String* string) { + string->cap = 0; + free(string->str); +} + +void String_Clear(String* string) { + string->cap = 8; + string->str = SafeRealloc(string->str, 8); + string->str[0] = 0; +} + +void String_Add(String* string, UChar ch) { + size_t newLen = strlen(string->str) + 2; + + while (newLen >= string->cap) { + newLen *= 2; + } + + if (newLen > string->cap) { + string->cap = newLen; + string->str = SafeRealloc(string->str, string->cap); + } + + char ch2 = (char) ch; + + strncat(string->str, &ch2, 1); +} + +UChar String_Get(String* string, size_t idx) { + if (idx >= String_Len(string)) { + assert(0); + } + + return string->str[idx]; +} + +size_t String_Len(String* string) { + return strlen(string->str); +} diff --git a/source/string.h b/source/string.h new file mode 100644 index 0000000000000000000000000000000000000000..b3da2bab26c6c76221f34c04a3507ca8583d01ef --- /dev/null +++ b/source/string.h @@ -0,0 +1,18 @@ +#ifndef YT_STRING_H +#define YT_STRING_H + +#include "char.h" + +typedef struct { + char* str; + size_t cap; +} String; + +String String_New(const char* src); +void String_Free(String* string); +void String_Clear(String* string); +void String_Add(String* string, UChar ch); +UChar String_Get(String* string, size_t idx); +size_t String_Len(String* string); + +#endif diff --git a/source/sysTerm/unix.c b/source/sysTerm/unix.c index b887a3b3d58401a3252f92cf4668d702708ade2e..03f3a33c5de8c0d1418b6bd7cf105ef04dd56b96 100644 --- a/source/sysTerm/unix.c +++ b/source/sysTerm/unix.c @@ -15,7 +15,8 @@ #include "../mem.h" #include "../util.h" #include "../sysTerm.h" -#define TERM "xterm-16color" +// #define TERM "xterm-16color" +#define TERM "dumb" extern char** environ; diff --git a/source/terminal.c b/source/terminal.c index 93c52df5fd7c2e0489a258d5303b409ace32ece4..b24212d8c1610280ec862fdc3c5497da45f40a9c 100644 --- a/source/terminal.c +++ b/source/terminal.c @@ -1,4 +1,7 @@ +#include "mem.h" #include "terminal.h" +#include "interpreter.h" +#include "interpreter/vt100.h" Terminal Terminal_New(int w, int h, bool* success) { Terminal ret; @@ -15,6 +18,10 @@ Buffer_Free(&ret.buffer); return ret; } + ret.interpreter = SafeMalloc(sizeof(Interpreter)); + + *((Interpreter*) ret.interpreter) = Interpreter_VT100(); + *success = true; return ret; } @@ -28,24 +35,19 @@ while (SysTerm_DataAvailable(&terminal->term)) { char ch = 0; SysTerm_ReadByte(&terminal->term, &ch); - switch (ch) { - case '\r': { - terminal->cursor.x = 0; - break; - } - case '\n': { - ++ terminal->cursor.y; - terminal->cursor.x = 0; - break; - } - default: { - BufCell cell; - cell.ch = (UChar) ch; - cell.attr = terminal->attr; + Interpreter_OnRead((Interpreter*) terminal->interpreter, terminal, (UChar) ch); + } +} - Buffer_Set(&terminal->buffer, terminal->cursor.x, terminal->cursor.y, cell); - ++ terminal->cursor.x; - } +void Terminal_HandleEvent(Terminal* terminal, Event* e, bool isSelected) { + switch (e->type) { + case EVENT_KEY_DOWN: + case EVENT_TEXT_INPUT: { + if (!isSelected) break; + + Interpreter_SendInput((Interpreter*) terminal->interpreter, terminal, e); + break; } + default: break; } } diff --git a/source/terminal.h b/source/terminal.h index 56db665967d275602765966cea723eba7d930b1e..9a7e05fde2de187904766b4c42106269fdc1a0af 100644 --- a/source/terminal.h +++ b/source/terminal.h @@ -1,6 +1,7 @@ #ifndef YT_TERMINAL_H #define YT_TERMINAL_H +#include "event.h" #include "types.h" #include "buffer.h" #include "sysTerm.h" @@ -10,10 +11,12 @@ Vec2 cursor; BufAttr attr; Buffer buffer; SysTerm term; + void* interpreter; // Interpreter* } Terminal; Terminal Terminal_New(int w, int h, bool* success); void Terminal_Free(Terminal* terminal); void Terminal_Update(Terminal* terminal); +void Terminal_HandleEvent(Terminal* terminal , Event* e, bool isSelected); #endif diff --git a/source/util.c b/source/util.c index 2c0340b626cfac0ed4f9e980f8b19d252f616089..38c7d237aae21bc235127e9afa7f99d0d40bc488 100644 --- a/source/util.c +++ b/source/util.c @@ -192,6 +192,40 @@ return memcmp(str, with, strlen(with)) == 0; } +char** Split(const char* str, size_t* length) { + char** ret; + *length = 0; + + ret = SafeMalloc(sizeof(char*)); + ret[0] = NULL; + + char* thisString = NewString(""); + + for (size_t i = 0; i <= strlen(str); ++ i) { + switch (str[i]) { + case ' ': + case '\t': + case '\0': { + ret[*length] = thisString; + ++ *length; + + ret = SafeRealloc(ret, sizeof(char*) * (*length + 1)); + ret[*length] = NULL; + + thisString = NewString(""); + break; + } + default: { + char str2[2] = {str[i], 0}; + thisString = ConcatString(thisString, str2); + break; + } + } + } + + return ret; +} + const char* BaseName(const char* path) { char* ret = strrchr(path, '/'); diff --git a/source/util.h b/source/util.h index 0fd90269b294516520fc0097c6d43227fe6390a2..c85ff16367d5bbe694c33a4a63366980bc3faa2e 100644 --- a/source/util.h +++ b/source/util.h @@ -43,6 +43,7 @@ bool StrArrayContains(char** array, char* string); size_t StrArrayFind(char** array, char* string); void FreeStrArray(char** array); bool StringStartsWith(char* str, char* with); +char** Split(const char* str, size_t* length); const char* BaseName(const char* path);