yterm

commit 3de98d2ca76a6c3c57ac4e109d5394745f99e8ba

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);