--- /dev/null
+#
+#
+#
+
+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)
+
+
--- /dev/null
+/*
+ */
+
+#include "test_common.h"
+#include <string.h>
+#include <display.h>
+
+// === 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);
+}
--- /dev/null
+/*
+ */
+#include "test_common.h"
+#include <stdio.h>
+#include <setjmp.h>
+#include <stdarg.h>
+
+// === 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);
+ }
+}
--- /dev/null
+#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
# include <acess/sys.h> // _SysDebug
# include <string.h>
# include <assert.h>
+# include <stdlib.h> // malloc/free
#endif
enum eExcapeMode {
};
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)
{
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;
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;
st->Mode = MODE_NORMAL;
return 1;
}
+ else {
+ // fall through
+ }
if( inc_len > 0 || *Buf == '\x1b' )
{
int Term_HandleVT100_Short(tTerminal *Term, int Len, const char *Buf)
{
- tVT100State *st = Display_GetTermState(Term);
int tmp;
switch(Buf[1])
{
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;
}
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]*;<str>
+ // > Clipboard data in base-64, cut/primary/select buffers 0-7
+ // > <str>='?' 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;
+}
+