From a9d679cbdef00d900fc37c57782759f49ec98cd5 Mon Sep 17 00:00:00 2001 From: John Hodge Date: Sat, 7 Dec 2013 18:16:17 +0800 Subject: [PATCH] Usermode/GUI Terminal - Fixed title support, added a unit test --- .../gui_terminal_src/UTEST/Makefile | 39 ++++++ .../gui_terminal_src/UTEST/TEST_vt100.c | 132 ++++++++++++++++++ .../gui_terminal_src/UTEST/test_common.c | 61 ++++++++ .../gui_terminal_src/UTEST/test_common.h | 28 ++++ .../Applications/gui_terminal_src/vt100.c | 85 +++++++++-- 5 files changed, 335 insertions(+), 10 deletions(-) create mode 100644 Usermode/Applications/gui_terminal_src/UTEST/Makefile create mode 100644 Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c create mode 100644 Usermode/Applications/gui_terminal_src/UTEST/test_common.c create mode 100644 Usermode/Applications/gui_terminal_src/UTEST/test_common.h diff --git a/Usermode/Applications/gui_terminal_src/UTEST/Makefile b/Usermode/Applications/gui_terminal_src/UTEST/Makefile new file mode 100644 index 00000000..0aa52130 --- /dev/null +++ b/Usermode/Applications/gui_terminal_src/UTEST/Makefile @@ -0,0 +1,39 @@ +# +# +# + +CPPFLAGS := -I ../include -I ../../../Libraries/ld-acess.so_src/include_exp +CFLAGS := -Wall -std=c99 +CFLAGS += $(CPPFLAGS) +TESTFILES := vt100 + +OBJDIR := stage/obj/ +BINDIR := stage/ + +.PHONY: all clean +# run $(TESTFILES:%=run_%) + +all: $(TESTFILES:%=$(BINDIR)TEST_%) + +#run: all $(TESTFILES:%=run_%) + +.PRECIOUS: + +#run_%: $(BINDIR)TEST_% +# ./$(BINDIR)TEST_% + +$(BINDIR)TEST_%: $(OBJDIR)TEST_%.o $(OBJDIR)__%.o $(OBJDIR)test_common.o + @mkdir -p $(dir $@) + @echo [CC] -o $@ + @$(CC) -o $@ $(OBJDIR)TEST_$*.o $(OBJDIR)__$*.o $(OBJDIR)test_common.o $(LDFLAGS) + +$(OBJDIR)%.o: %.c Makefile + @mkdir -p $(dir $@) + @echo [CC] -c $< -o $@ + @$(CC) -c $< -o $@ $(CFLAGS) +$(OBJDIR)__%.o: ../%.c Makefile + @mkdir -p $(dir $@) + @echo [CC] -c $< -o $@ + @$(CC) -c $< -o $@ $(CFLAGS) + + diff --git a/Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c b/Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c new file mode 100644 index 00000000..1d06fe81 --- /dev/null +++ b/Usermode/Applications/gui_terminal_src/UTEST/TEST_vt100.c @@ -0,0 +1,132 @@ +/* + */ + +#include "test_common.h" +#include +#include + +// === GLOBALS === +void (*gpTest_VT100_AddText)(size_t Length, const char *UTF8Text); +void (*gpTest_VT100_SetTitle)(const char *Title); + +// === CODE === +int Test_VT100_Setup(void) +{ + gpTest_VT100_AddText = NULL; + gpTest_VT100_SetTitle = NULL; + return 0; +} + +int Test_VT100_int_Command(int MaxCalls, const char *Command) +{ + const char *buf = Command; + int len = strlen(Command); + int nCalls = 0; + while( len > 0 ) + { + TEST_ASSERT_R(nCalls, <, MaxCalls); + int rv = Term_HandleVT100(NULL, len, buf); + if( rv < 0 ) + return buf - Command; + TEST_ASSERT_R(rv, !=, 0); + TEST_ASSERT_R(rv, <=, len); + len -= rv; + buf += rv; + nCalls ++; + } + + return buf - Command; +} + +void Test_VT100_TitleSet(void) +{ + #define TITLE1 "Testing Title, quite long and complex ;;" + const char cmd_long_long[] = "\033]0;"TITLE1"\033\\"; + #define TITLE2 "Another completely[]different title--" + const char cmd_long_st[] = "\033]0;"TITLE2"\x9c"; + #define TITLE3 "12345678b sbuog egu nz vlj=+~`][]\033 " + const char cmd_long_bel[] = "\033]0;"TITLE3"\007"; + const char *expected_title; + + int title_was_set=0, rv; + + void _settitle(const char *Title) { + TEST_ASSERT_SR( Title, ==, expected_title ); + title_was_set = 1; + } + gpTest_VT100_SetTitle = _settitle; + + expected_title = TITLE1; + title_was_set = 0; + rv = Test_VT100_int_Command(10, cmd_long_long); + TEST_ASSERT_R(rv, ==, sizeof(cmd_long_long)-1); + TEST_ASSERT(title_was_set); + + expected_title = TITLE2; + title_was_set = 0; + rv = Test_VT100_int_Command(10, cmd_long_st); + TEST_ASSERT_R(rv, ==, sizeof(cmd_long_st)-1); + TEST_ASSERT(title_was_set); + + expected_title = TITLE3; + title_was_set = 0; + rv = Test_VT100_int_Command(10, cmd_long_bel); + TEST_ASSERT_R(rv, ==, sizeof(cmd_long_bel)-1); + TEST_ASSERT(title_was_set); +} + +// === TESTS === +tTEST gaTests[] = { + {"VT100 TitleSet", NULL, Test_VT100_TitleSet, NULL} +}; +int giNumTests = sizeof(gaTests)/sizeof(gaTests[0]); + +// === Hooks === +void *gpTermStatePtr; +void *Display_GetTermState(tTerminal *Term) { + return gpTermStatePtr; +} +void Display_SetTermState(tTerminal *Term, void *State) { + gpTermStatePtr = State; +} + +void Display_AddText(tTerminal *Term, size_t Length, const char *UTF8Text) { + TEST_ASSERT(gpTest_VT100_AddText); + gpTest_VT100_AddText(Length, UTF8Text); +} +void Display_Newline(tTerminal *Term, bool bCarriageReturn) { + //if( gpTest_VT100_Newline ) + // gpTest_VT100_Newline(Term, bCarriageReturn); +} +void Display_SetScrollArea(tTerminal *Term, int Start, int Count) { + //if( gpTest_VT100_SetScrollArea ) + // gpTest_VT100_SetScrollArea(Term, Start, Count); +} +void Display_ScrollDown(tTerminal *Term, int Count) { +} +void Display_SetCursor(tTerminal *Term, int Row, int Col) { +} +void Display_MoveCursor(tTerminal *Term, int RelRow, int RelCol) { +} +void Display_SaveCursor(tTerminal *Term) { +} +void Display_RestoreCursor(tTerminal *Term) { +} +void Display_ClearLine(tTerminal *Term, int Dir) { // 0: All, 1: Forward, -1: Reverse +} +void Display_ClearLines(tTerminal *Term, int Dir) { // 0: All, 1: Forward, -1: Reverse +} +void Display_ResetAttributes(tTerminal *Term) { +} +void Display_SetForeground(tTerminal *Term, uint32_t RGB) { +} +void Display_SetBackground(tTerminal *Term, uint32_t RGB) { +} +void Display_Flush(tTerminal *Term) { +} +void Display_ShowAltBuffer(tTerminal *Term, bool AltBufEnabled) { +} +void Display_SetTitle(tTerminal *Term, const char *Title) { + TEST_ASSERT(gpTest_VT100_SetTitle); + gpTest_VT100_SetTitle(Title); +} diff --git a/Usermode/Applications/gui_terminal_src/UTEST/test_common.c b/Usermode/Applications/gui_terminal_src/UTEST/test_common.c new file mode 100644 index 00000000..a07dd1d3 --- /dev/null +++ b/Usermode/Applications/gui_terminal_src/UTEST/test_common.c @@ -0,0 +1,61 @@ +/* + */ +#include "test_common.h" +#include +#include +#include + +// === GLOBALS === +int gbDebugEnabled = 0; +jmp_buf gTest_FailJump; + +// === CODE === +int main(int argc, char *argv[]) +{ + int nFail = 0; + for( int i = 0; i < giNumTests; i ++ ) + { + tTEST *test = &gaTests[i]; + fprintf(stderr, "Test [%i] \"%s\": ", i, test->Name); + if( test->Setup && test->Setup() != 0 ) { + fprintf(stderr, "ERROR\n"); + } + else { + int res = setjmp(gTest_FailJump); + if( res == 0 ) + test->Run(); + + if(test->Teardown) + test->Teardown(); + if( res == 0 ) + fprintf(stderr, "PASS\n"); + else + nFail ++; + } + } + return nFail; +} + +void TestFailure(const char *ReasonFmt, ...) +{ + va_list args; + va_start(args, ReasonFmt); + fprintf(stderr, "FAILURE: "); + vfprintf(stderr, ReasonFmt, args); + fprintf(stderr, "\n"); + va_end(args); + longjmp(gTest_FailJump, 1); +} + +void _SysDebug(const char *Format, ...) +{ + if( gbDebugEnabled ) + { + va_list args; + va_start(args, Format); + printf("_SysDebug: "); + vprintf(Format, args); + printf("\n"); + va_end(args); + } +} diff --git a/Usermode/Applications/gui_terminal_src/UTEST/test_common.h b/Usermode/Applications/gui_terminal_src/UTEST/test_common.h new file mode 100644 index 00000000..e6fde414 --- /dev/null +++ b/Usermode/Applications/gui_terminal_src/UTEST/test_common.h @@ -0,0 +1,28 @@ +#ifndef _TEST_COMMON_H +#define _TEST_COMMON_H + +typedef struct +{ + const char *Name; + int (*Setup)(void); + void (*Run)(void); + void (*Teardown)(void); +} tTEST; + +extern tTEST gaTests[]; +extern int giNumTests; + +extern void TestFailure(const char *ReasonFmt, ...) __attribute__((noreturn)); + +#define TEST_ASSERT(expr) do{ \ + if( !(expr) ) TestFailure("Assertion %s", #expr );\ +} while(0) + +#define TEST_ASSERT_R(a,r,b) do{ \ + if( !((a) r (b)) ) TestFailure("Assertion %s(%i) %s %s(%i)", #a, (a), #r, #b, (b) );\ +} while(0) +#define TEST_ASSERT_SR(a,r,b) do{ \ + if( !(strcmp((a),(b)) r 0) ) TestFailure("Assertion %s(\"%s\") %s %s(\"%s\")", #a, (a), #r, #b, (b) );\ +} while(0) + +#endif diff --git a/Usermode/Applications/gui_terminal_src/vt100.c b/Usermode/Applications/gui_terminal_src/vt100.c index 4af6403d..91967ff6 100644 --- a/Usermode/Applications/gui_terminal_src/vt100.c +++ b/Usermode/Applications/gui_terminal_src/vt100.c @@ -15,6 +15,7 @@ # include // _SysDebug # include # include +# include // malloc/free #endif enum eExcapeMode { @@ -49,8 +50,9 @@ const uint32_t caVT100Colours[] = { }; int _locate_eos(size_t Len, const char *Buf); - int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf); int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf); + int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buf); + int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf); static inline int min(int a, int b) { @@ -59,8 +61,7 @@ static inline int min(int a, int b) int _locate_eos(size_t Len, const char *Buf) { - size_t ret = 0; - while( ret < Len ) + for( size_t ret = 0; ret < Len; ret ++ ) { if( Buf[ret] == '\007' ) return ret; @@ -88,20 +89,23 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf) if( st->Mode == MODE_IGNORE ) { st->Mode = MODE_NORMAL; // Used for multi-byte EOS + _SysDebug("Ignore 1 '%c'", *Buf); return 1; } - if( st->Mode == MODE_STRING ) + else if( st->Mode == MODE_STRING ) { // We're in a string mode int pos = _locate_eos(Len, Buf); size_t bytes = (pos >= 0 ? pos : Len); char *tmp = realloc(st->StringCache, st->StringLen + bytes+1); if(!tmp) return bytes; + st->StringCache = tmp; memcpy(tmp+st->StringLen, Buf, bytes); tmp[st->StringLen+bytes] = 0; + st->StringLen += bytes; _SysDebug("pos=%i", pos); - _SysDebug("'%.*s'", bytes, Buf); + _SysDebug("Buf[+%zi] = '%.*s'", bytes, bytes, Buf); // Only apply when we hit EOS at the start if( pos != 0 ) return bytes; @@ -125,6 +129,9 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf) st->Mode = MODE_NORMAL; return 1; } + else { + // fall through + } if( inc_len > 0 || *Buf == '\x1b' ) { @@ -210,7 +217,6 @@ int Term_HandleVT100(tTerminal *Term, int Len, const char *Buf) int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf) { - tVT100State *st = Display_GetTermState(Term); int tmp; switch(Buf[1]) { @@ -221,10 +227,11 @@ int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf) return 0; return tmp + 2; case ']': - st->Mode = MODE_STRING; - st->StringType = STRING_IGNORE; - _SysDebug("TODO: \\e] Support title changes"); - return 2; + tmp = Term_HandleVT100_OSC(Term, Len-2, Buf+2); + assert(tmp >= 0); + if( tmp == 0 ) + return 0; + return tmp + 2; case '=': _SysDebug("TODO: \\e= Application Keypad"); return 2; @@ -471,3 +478,61 @@ int Term_HandleVT100_Long(tTerminal *Term, int Len, const char *Buffer) } return j; } + +int Term_HandleVT100_OSC(tTerminal *Term, int Len, const char *Buf) +{ + tVT100State *st = Display_GetTermState(Term); + + int ofs = 0; + // OSC Ps ; Pt [ST/BEL] + if(Len < 2) return 0; // Need moar + + int Ps = 0; + while( ofs < Len && isdigit(Buf[ofs]) ) { + Ps = Ps * 10 + (Buf[ofs] - '0'); + ofs ++; + } + + if( ofs == Len ) return 0; + if( Buf[ofs] != ';' ) { + // Error + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + return ofs; + } + ofs ++; + + switch(Ps) + { + case 0: // Icon Name + Window Title + case 1: // Icon Name + case 2: // Window Title + st->Mode = MODE_STRING; + st->StringType = STRING_TITLE; + break; + case 3: // Set X Property + _SysDebug("TODO: \\e]3; Support X properties"); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + case 4: // Change colour number + case 5: // Change special colour number + _SysDebug("TODO: \\e]%i;c; Support X properties", Ps); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + case 10: // Change foreground to Pt + case 11: // Change background to Pt + case 52: + // TODO: Can be Pt = [cps01234567]*; + // > Clipboard data in base-64, cut/primary/select buffers 0-7 + // > ='?' returns the clipbard in the same format + default: + _SysDebug("Unknown VT100 OSC \\e]%i;", Ps); + st->Mode = MODE_STRING; + st->StringType = STRING_IGNORE; + break; + } + return ofs; +} + -- 2.20.1