aboutsummaryrefslogtreecommitdiff
path: root/archived
diff options
context:
space:
mode:
authorRaymaekers Luca <luca@spacehb.net>2025-04-27 12:52:06 +0200
committerRaymaekers Luca <luca@spacehb.net>2025-04-27 13:05:34 +0200
commitf87f7b4f0aaccc65d03ccee5bb11915ead6fb0e1 (patch)
treed54df0bfde3dbffa02b1f138af4f12456f261e54 /archived
parent0574f5a7c5159a2ae1d7d2182cec982509947db9 (diff)
First pass at preparing for Github
Diffstat (limited to 'archived')
-rw-r--r--archived/array.h70
-rw-r--r--archived/input_box.c70
-rw-r--r--archived/network_compression.c154
-rw-r--r--archived/ui_checkmark.c87
-rw-r--r--archived/ui_meter.c108
-rw-r--r--archived/ui_selection.c71
-rw-r--r--archived/utf8toASCII.c163
-rw-r--r--archived/v1/.gitignore7
-rw-r--r--archived/v1/README.md58
-rw-r--r--archived/v1/arena.h116
-rwxr-xr-xarchived/v1/build.sh6
-rw-r--r--archived/v1/client.c259
-rw-r--r--archived/v1/common.h181
-rw-r--r--archived/v1/compile_flags.txt5
-rw-r--r--archived/v1/recv.c59
-rw-r--r--archived/v1/send.c61
-rw-r--r--archived/v1/server.c148
-rw-r--r--archived/v1/termbox2.h3517
-rw-r--r--archived/wrap.c52
19 files changed, 0 insertions, 5192 deletions
diff --git a/archived/array.h b/archived/array.h
deleted file mode 100644
index 04d2f38..0000000
--- a/archived/array.h
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "chatty.h"
-
-u64 ArrayLength(u8 *Array);
-void ArrayInsert(u8 *Array, u64 Position, u8 Element);
-void ArrayCopy(u8 *To, u8 *From);
-void ArrayDelete(u8* Array, u64 Position);
-u8* ArrayCreate(u8* Container, u64 Capacity);
-
-// EXAMPLE: CREATE
-//
-// u64 Capacity = 15;
-// u8 ArrayContainer[Capacity + sizeof(Capacity)];
-// u8* Array = ArrayCreate(ArrayContainer, Capacity);
-//
-// EXAMPLE: API
-//
-// ArrayCopy(Array, (u8*)"Hello, world!");
-//
-// ArrayInsert(Array, 3, 'f');
-// ArrayInsert(Array, 3, 'e');
-// Array[14] = 'd';
-// ArrayDelete(Array, 3);
-
-#ifdef ARRAY_IMPL
-
-#include <strings.h>
-#include <string.h>
-
-u64
-ArrayLength(u8 *Array)
-{
- return *((u64*)(Array - sizeof(u64)));
-}
-
-void
-ArrayInsert(u8 *Array, u64 Position, u8 Element)
-{
- memmove(Array + Position + 1, Array + Position, ArrayLength(Array) - Position - 1);
- Array[Position] = Element;
-}
-
-// Copy null terminated string without copying over the null terminator
-void
-ArrayCopy(u8 *To, u8 *From)
-{
- u32 i = 0;
- while (From[i])
- {
- To[i] = From[i];
- i++;
- }
-}
-
-void
-ArrayDelete(u8* Array, u64 Position)
-{
- memmove(Array + Position, Array + Position + 1, ArrayLength(Array) - Position - 1);
- Array[ArrayLength(Array) - 1] = 0;
-}
-
-u8*
-ArrayCreate(u8* Container, u64 Capacity)
-{
- *(u64*)Container = Capacity;
- u8 *Array = Container + sizeof(Capacity);
- bzero(Array, Capacity);
- return Array;
-}
-
-#endif
diff --git a/archived/input_box.c b/archived/input_box.c
deleted file mode 100644
index 466c03d..0000000
--- a/archived/input_box.c
+++ /dev/null
@@ -1,70 +0,0 @@
-#define Assert(expr) if (!(expr)) { \
- tb_shutdown(); \
- raise(SIGTRAP); \
-}
-
-#define TB_IMPL
-#include "termbox2.h"
-#undef TB_IMPL
-
-#define TEXTBOX_MAX_INPUT 255
-
-#define UI_IMPL
-#include "ui.h"
-
-#define ARENA_IMPL
-#include "arena.h"
-
-#include <locale.h>
-
-int
-main(void)
-{
- struct tb_event ev = {0};
-
- u32 InputLen = 0;
- wchar_t Input[TEXTBOX_MAX_INPUT] = {0};
- rect TextBox = {0, 0, 32, 5};
- rect TextR = {
- 2, 1,
- TextBox.W - 2*TEXTBOX_PADDING_X - 2*TEXTBOX_BORDER_WIDTH,
- TextBox.H - 2*TEXTBOX_BORDER_WIDTH
- };
- // Used for scrolling the text. Text before TextOffset will not be printed.
- u32 TextOffset = 0;
- // Position in input based on cursor position.
- u32 InputPos = 0;
-
- Assert(setlocale(LC_ALL, ""));
- tb_init();
- bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
- global.cursor_x = TextR.X;
- global.cursor_y = TextR.Y;
-
- DrawBox(TextBox, 0);
-
- while (ev.key != TB_KEY_CTRL_C)
- {
- DrawTextBoxWrapped(TextR, Input + TextOffset, InputLen - TextOffset);
-
- InputPos = TextOffset + (global.cursor_x - TextR.X) + (global.cursor_y - TextR.Y) * TextR.W;
- Assert(InputPos <= InputLen);
-
- tb_present();
- tb_poll_event(&ev);
-
- u32 Ret = TextBoxKeypress(ev, TextR, Input, &InputLen, InputPos, &TextOffset);
-
- u32 ShouldInsert = (!Ret) && (ev.ch && InputLen < TEXTBOX_MAX_INPUT);
- // Insert new character in Input at InputPos
- if (ShouldInsert)
- {
- TextBoxInsert(Input, InputPos, InputLen++, ev.ch);
- TextBoxScrollRight(TextR, &TextOffset);
- }
- }
-
-
- tb_shutdown();
- return 0;
-}
diff --git a/archived/network_compression.c b/archived/network_compression.c
deleted file mode 100644
index eef9e3c..0000000
--- a/archived/network_compression.c
+++ /dev/null
@@ -1,154 +0,0 @@
-#include <strings.h>
-#include <stdio.h>
-#include <string.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <fcntl.h>
-#include <assert.h>
-#include <stddef.h>
-#include <stdint.h>
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t s8;
-typedef int16_t s16;
-typedef int32_t s32;
-typedef int64_t s64;
-
-size_t
-GetMaximumCompressedOutputSize(size_t FileSize)
-{
- // TODO: figure out equation
- return FileSize*2 + 256;
-}
-
-u32
-RLECompress(size_t InSize, u8* In, size_t MaxOutSize, u8* OutBase)
-{
- u8* Out = OutBase;
-
-#define MAX_LITERAL_COUNT 255
-#define MAX_RUN_COUNT 255
- u32 LiteralCount = 0;
- u8 Literals[MAX_LITERAL_COUNT] = {0};
-
- u8 *InEnd = In + InSize;
- while(In < InEnd)
- {
- u8 StartingValue = In[0];
- size_t Run = 1; // first one is the character itself
- while((Run < (InEnd - In)) &&
- (Run < MAX_RUN_COUNT) &&
- (In[Run] == StartingValue))
- {
- ++Run;
- }
-
- if ((Run > 1) ||
- (LiteralCount == MAX_LITERAL_COUNT)) // stop doing runs when there is no
- // space left in the buffer.
- {
- // Encode a literal/run pair
- u8 LiteralCount8 = (u8)LiteralCount;
- assert(LiteralCount8 == LiteralCount);
- *Out++ = LiteralCount8;
-
- for(u32 LiteralIndex = 0;
- LiteralIndex < LiteralCount;
- ++LiteralIndex)
- {
- *Out++ = Literals[LiteralIndex];
- }
- LiteralCount = 0;
-
- u8 Run8 = (u8)Run;
- assert(Run8 == Run);
- *Out++ = Run8;
-
- *Out++ = StartingValue;
-
- In += Run;
- }
- else
- {
- // Buffer literals, you have to because we are encoding them in pairs
- Literals[LiteralCount++] = StartingValue;
- ++In;
- }
-
- }
-#undef MAX_LITERAL_COUNT
-#undef MAX_RUN_COUNT
-
- assert(In == InEnd);
-
- size_t OutSize = Out - OutBase;
- assert(OutSize <= MaxOutSize);
-
- return OutSize;
-}
-
-void
-RLEDecompress(size_t InSize, u8* In, size_t OutSize, u8* Out)
-{
- u8 *InEnd = In + InSize;
- while(In < InEnd)
- {
- // TODO: I think Casey made a mistake and this should be an u8
- u8 LiteralCount = *In++;
- while(LiteralCount--)
- {
- *Out++ = *In++;
- }
-
- // Alternate to Run
- u8 RepCount = *In++;
- u8 RepValue = *In++;
- while(RepCount--)
- {
- *Out++ = RepValue;
- }
- }
-
- assert(In == InEnd);
-}
-
-int main(int Argc, char* Argv[]) {
-
- if (Argc < 2)
- {
- fprintf(stderr, "Usage: %s [compress|decompress]\n", Argv[0]);
- return 1;
- }
-
- char* Command = Argv[1];
-
- if (!strcmp(Command, "compress"))
- {
- u32* Message = (u32*)L"abcabcbbbbbbb";
- size_t MessageSize = wcslen((const wchar_t*)Message) * 4;
-
- size_t OutBufferSize = GetMaximumCompressedOutputSize(MessageSize);
- u8 OutBuffer[OutBufferSize + 8];
- bzero(OutBuffer, OutBufferSize + 8);
- *(u32*)OutBuffer = MessageSize;
-
- size_t CompressedSize = RLECompress(MessageSize, (u8*)Message, OutBufferSize, OutBuffer + 8);
-
- s32 OutFile = open("test.compressed", O_WRONLY | O_CREAT, 0600);
-
- write(OutFile, OutBuffer, CompressedSize);
-
- fprintf(stdout, "%lu -> %lu bytes\n", MessageSize, CompressedSize);
- }
- else if (!strcmp(Command, "decompress"))
- {
- fprintf(stderr, "Not implemented yet.\n");
- }
- else
- fprintf(stderr, "Unknown command: '%s'\n", Command);
-
- return 0;
-}
diff --git a/archived/ui_checkmark.c b/archived/ui_checkmark.c
deleted file mode 100644
index df7e507..0000000
--- a/archived/ui_checkmark.c
+++ /dev/null
@@ -1,87 +0,0 @@
-#define TB_IMPL
-#include "../chatty/external/termbox2.h"
-
-int
-main(void)
-{
- struct tb_event ev = {0};
-
- typedef struct {
- int X, Y;
- int Checked;
- } Checkmark;
-
-#define NUM_CHECKMARKS 4
- int Y = 0;
- Checkmark Marks[NUM_CHECKMARKS] = {
- {0, Y++, 0},
- {0, Y++, 0},
- {0, Y++, 1},
- {0, Y++, 0}
- };
- Y++;
-
- int Selected = 0;
-
- tb_init();
-
- int Quit = 0;
- while (!Quit)
- {
- tb_clear();
-
- for (int CheckmarkIndex = 0;
- CheckmarkIndex < NUM_CHECKMARKS;
- CheckmarkIndex++)
- {
- Checkmark Mark = Marks[CheckmarkIndex];
- if (Mark.Checked)
- {
- tb_printf(Mark.X, Mark.Y, 0, 0, "[x]");
- }
- else
- {
- tb_printf(Mark.X, Mark.Y, 0, 0, "[ ]");
- }
- }
- Checkmark Mark = Marks[Selected];
- if (Mark.Checked)
- {
- tb_set_cell(Mark.X + 1, Mark.Y, L'x', TB_UNDERLINE, 0);
- }
- else
- {
- tb_set_cell(Mark.X + 1, Mark.Y, L' ', TB_UNDERLINE, 0);
- }
-
- int BaseY = Y;
- tb_printf(0, BaseY, TB_BOLD, 0, "j"); tb_printf(2, BaseY++, 0, 0, "next");
- tb_printf(0, BaseY, TB_BOLD, 0, "k"); tb_printf(2, BaseY++, 0, 0, "previous");
- tb_printf(0, BaseY, TB_BOLD, 0, "c"); tb_printf(2, BaseY++, 0, 0, "toggle");
- tb_printf(0, BaseY, TB_BOLD, 0, "q"); tb_printf(2, BaseY++, 0, 0, "quit");
-
- tb_present();
-
- tb_poll_event(&ev);
- if (ev.ch == 'q')
- {
- Quit = 1;
- }
- else if (ev.ch == 'j')
- {
- if (Selected == NUM_CHECKMARKS - 1) Selected = 0;
- else Selected++;
- }
- else if (ev.ch == 'k')
- {
- if (Selected) Selected--;
- else Selected = NUM_CHECKMARKS - 1;
- }
- else if (ev.ch == 'c')
- {
- Marks[Selected].Checked = !Marks[Selected].Checked;
- }
- }
-
- tb_shutdown();
-}
diff --git a/archived/ui_meter.c b/archived/ui_meter.c
deleted file mode 100644
index 8636ba4..0000000
--- a/archived/ui_meter.c
+++ /dev/null
@@ -1,108 +0,0 @@
-#define TB_IMPL
-#include "../chatty/external/termbox2.h"
-
-#include <locale.h>
-#include <signal.h>
-#include <unistd.h>
-#include <sys/wait.h>
-
-#define Assert(expr) \
- if (!(expr)) \
- { \
- tb_shutdown(); \
- raise(SIGTRAP); \
- }
-
-#define METER_WIDTH 4
-
-static char ***EnvironmentPath = 0;
-
-void
-draw_meter(int X, int Y, int Height, int MaxLevel, int Level)
-{
- int FillColor = 0;
-
- Height -= 2; // substract top and bottom border
-
- tb_printf(X, Y, 0, 0, "%ls", L"┌──┐");
- tb_printf(X, Y + Height + 1, 0, 0, "%ls", L"└ ┘");
-
- if (Level == MaxLevel)
- {
- FillColor = TB_GREEN;
- tb_printf(X + 1, Y + Height + 1, 0, 0, "00");
- }
- else
- {
- FillColor = TB_CYAN;
- tb_printf(X + 1, Y + Height + 1, 0, 0, "%02d", Level);
- }
-
- int FilledLevel = (Level * Height)/MaxLevel;
-
- for (int LevelIndex = 0; LevelIndex < FilledLevel; LevelIndex++)
- {
- int YLevel = Y + (Height - LevelIndex);
- tb_set_cell(X + 1, YLevel, L'▒', FillColor, FillColor);
- tb_set_cell(X + 2, YLevel, L'▒', FillColor, FillColor);
- }
-
- for (int YOffset = 0; YOffset < Height; YOffset++)
- {
- tb_set_cell(X + 0, Y + YOffset + 1, L'│', 0, 0);
- tb_set_cell(X + 3, Y + YOffset + 1, L'│', 0, 0);
- }
-
-}
-
-int
-main(int Argc, char *Args[], char *Envp[])
-{
- EnvironmentPath = &Envp;
-
- struct tb_event ev = {0};
- Assert(setlocale(LC_ALL, ""));
- int Quit = 0;
- int Level = 0;
- int LevelStep = 10;
- int MeterMaxLevel = 100;
- int MeterHeight = 10 + 2;
-
- tb_init();
- tb_set_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
-
- while (!Quit)
- {
- tb_clear();
-
- int Y = 0;
- draw_meter(1, Y, MeterHeight, MeterMaxLevel, Level);
- Y += MeterHeight + 1;;
-
- tb_print(0, Y, TB_BOLD, 0, "j"); tb_print(2, Y++, 0, 0, "decrease");
- tb_print(0, Y, TB_BOLD, 0, "k"); tb_print(2, Y++, 0, 0, "increase");
- tb_print(0, Y, TB_BOLD, 0, "q"); tb_print(2, Y++, 0, 0, "to quit");
-
- tb_present();
-
- tb_poll_event(&ev);
-
- if (ev.ch == 'q' || ev.key == TB_KEY_CTRL_C) Quit = 1;
- else if ((ev.ch == 'j' || ev.key == TB_KEY_MOUSE_WHEEL_DOWN) &&
- Level > 0)
- {
- if (LevelStep > Level) Level = 0;
- else Level -= LevelStep;
- }
- else if ((ev.ch == 'k' || ev.key == TB_KEY_MOUSE_WHEEL_UP) &&
- Level < MeterMaxLevel)
- {
- if (Level + LevelStep > MeterMaxLevel) Level = MeterMaxLevel;
- else Level += LevelStep;
- }
-
- }
-
- tb_shutdown();
- return 0;
-}
diff --git a/archived/ui_selection.c b/archived/ui_selection.c
deleted file mode 100644
index 3e45ee2..0000000
--- a/archived/ui_selection.c
+++ /dev/null
@@ -1,71 +0,0 @@
-#define TB_IMPL
-#include "../chatty/external/termbox2.h"
-
-#include <signal.h>
-
-#define Assert(expr) if (!(expr)) raise(SIGTRAP)
-
-typedef struct {
- int X, Y;
-} Position;
-
-int
-main(void)
-{
- int Selected = 0;
- int NumChoices = 3;
- int Y = 0;
-
- Position Positions[NumChoices];
- Positions[0] = (Position){1, Y};
- Positions[1] = (Position){5, Y};
- Positions[2] = (Position){9, Y};
- Y += 2;
-
- struct tb_event ev = {0};
- int Color = TB_GREEN;
-
- tb_init();
-
- int Quit = 0;
- while (!Quit)
- {
- tb_clear();
-
- int BaseY = Y;
- tb_printf(0, BaseY, TB_BOLD, 0, "j"); tb_printf(2, BaseY++, 0, 0, "select next");
- tb_printf(0, BaseY, TB_BOLD, 0, "k"); tb_printf(2, BaseY++, 0, 0, "select previous");
- tb_printf(0, BaseY, TB_BOLD, 0, "s"); tb_printf(2, BaseY++, 0, 0, "change color");
- tb_printf(0, BaseY, TB_BOLD, 0, "q"); tb_printf(2, BaseY++, 0, 0, "quit");
-
- // Draw a box at position
- for (int PositionsIndex = 0; PositionsIndex < NumChoices; PositionsIndex++)
- {
- Assert(Positions[PositionsIndex].X > 0);
- tb_printf(Positions[PositionsIndex].X - 1, Positions[PositionsIndex].Y, Color, 0, "[ ]");
- }
- // Draw mark in selected box
- tb_printf(Positions[Selected].X, Positions[Selected].Y, Color, 0, "x");
-
- tb_present();
- tb_poll_event(&ev);
-
- if (ev.ch == 'q') Quit = 1;
- else if (ev.ch == 'j' && Selected < NumChoices - 1) Selected++;
- else if (ev.ch == 'k' && Selected) Selected--;
- else if (ev.ch == 's')
- {
- switch (Selected)
- {
- case 0: Color = TB_GREEN; break;
- case 1: Color = TB_BLUE; break;
- case 2: Color = TB_RED; break;
- default: Assert(0); break;
- }
- }
- }
-
- tb_shutdown();
-
- return 0;
-}
diff --git a/archived/utf8toASCII.c b/archived/utf8toASCII.c
deleted file mode 100644
index 988a69b..0000000
--- a/archived/utf8toASCII.c
+++ /dev/null
@@ -1,163 +0,0 @@
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdio.h>
-#include <locale.h>
-#include <assert.h>
-#include <wchar.h>
-#include <stdint.h>
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t s8;
-typedef int16_t s16;
-typedef int32_t s32;
-typedef int64_t s64;
-
-static size_t
-UTF8Compress(size_t InSize, wchar_t* In, size_t OutSize, u8* OutBase)
-{
- wchar_t* InEnd = (wchar_t*)((u8*)In + InSize);
- u8* Out = OutBase;
-
-#define MAX_LITERAL_COUNT 255
- u8 ASCIILiterals[MAX_LITERAL_COUNT] = {0};
- u8 ASCIILiteralsCount = 0;
- wchar_t UTF8Literals[MAX_LITERAL_COUNT] = {0};
- u8 UTF8LiteralsCount = 0;
-
- while (In < InEnd)
- {
- wchar_t CurrentChar = In[0];
-
- // Check consecutive ascii characters
- while(CurrentChar == (u8)CurrentChar &&
- ASCIILiteralsCount < MAX_LITERAL_COUNT)
- {
- ASCIILiterals[ASCIILiteralsCount++] = (u8)CurrentChar;
- CurrentChar = *++In;
- }
-
- while(CurrentChar != (u8)CurrentChar &&
- UTF8LiteralsCount < MAX_LITERAL_COUNT)
- {
- UTF8Literals[UTF8LiteralsCount++] = CurrentChar;
- CurrentChar = *++In;
- }
-
- // Encode ASCII/UTF8 pair
- *Out++ = ASCIILiteralsCount;
- for (u8 ch = 0;
- ch < ASCIILiteralsCount;
- ch++)
- {
- *Out = ASCIILiterals[ch];
- Out += sizeof(ASCIILiterals[ch]);
- }
- ASCIILiteralsCount = 0;
-
- *Out++ = UTF8LiteralsCount;
- for (u8 ch = 0;
- ch < UTF8LiteralsCount;
- ch++)
- {
- *(wchar_t*)Out = UTF8Literals[ch];
- Out += sizeof(UTF8Literals[ch]);
- }
- UTF8LiteralsCount = 0;
-
- }
-#undef MAX_LITERAL_COUNT
- assert(In == InEnd);
-
- return Out - OutBase;
-}
-
-static void
-PrintCompressedUTF8(u8* In, size_t InSize)
-{
- u8* InEnd = In + InSize;
-
- while (In < InEnd)
- {
- u8 ASCIICount = *In++;
- wprintf(L"%dA(\"", ASCIICount);
- while(ASCIICount--)
- {
- wprintf(L"%c", *In);
- In += sizeof(u8);
- }
- wprintf(L"\") ");
-
- u8 UTF8Count = *In++;
- wprintf(L"%dU(\"", UTF8Count);
- while(UTF8Count--)
- {
- wprintf(L"%lc", *(wchar_t*)In);
- In += sizeof(wchar_t);
- }
- wprintf(L"\") ");
- }
- wprintf(L"\n");
-
- assert(In == InEnd);
-}
-
-static void
-UTF8Decompress(size_t InSize, u8* In, size_t OutSize, wchar_t* Out)
-{
- u8* InEnd = In + InSize;
-
- while (In < InEnd)
- {
- u8 ASCIICount = *In++;
- while(ASCIICount--)
- {
- *Out++ = *In++;
- }
-
- u8 UTF8Count = *In++;
- while(UTF8Count--)
- {
- *Out++ = *(wchar_t*)In;
- In += sizeof(wchar_t);
- }
- }
- assert(In == InEnd);
-}
-
-// Size is the size of the UTF8 string in bytes. "aaa" would be 12.
-size_t
-UTF8GetMaximumCompressedSize(size_t Size)
-{
- // The largest would be if there was only one unicode point in which case we store 0 for ascii 1
- // for unicode and the raw codepoint. 1 + 1 + 4 * CodepointNum
- return Size + 2;
-}
-
-int
-main(int Argc, char* Argv[]) {
- assert(setlocale(LC_ALL, "") != 0);
-
- wchar_t* InBuf = L"text│tt│";
- size_t InSize = wcslen(InBuf) * 4;
-
- size_t OutSize = UTF8GetMaximumCompressedSize(InSize);
- u8 OutBuf[OutSize];
-
- size_t CompressedSize = UTF8Compress(InSize, InBuf, OutSize, OutBuf);
-
- fwprintf(stderr, L"Raw string: \"%ls\"\n", InBuf);
- fwprintf(stderr, L"Compressed %lu bytes -> %lu bytes.\n", InSize, CompressedSize);
-
- size_t DecompressedSize = InSize;
- wchar_t *DecompressedBuffer = malloc(DecompressedSize);
-
- UTF8Decompress(CompressedSize, OutBuf, DecompressedSize, DecompressedBuffer);
- fwprintf(stderr, L"Decompressed: \"%ls\"\n", DecompressedBuffer);
-
- PrintCompressedUTF8(OutBuf, CompressedSize);
-
- return 0;
-}
diff --git a/archived/v1/.gitignore b/archived/v1/.gitignore
deleted file mode 100644
index a9ac961..0000000
--- a/archived/v1/.gitignore
+++ /dev/null
@@ -1,7 +0,0 @@
-chatty
-server
-recv
-send
-client
-
-tags
diff --git a/archived/v1/README.md b/archived/v1/README.md
deleted file mode 100644
index 2cf0aee..0000000
--- a/archived/v1/README.md
+++ /dev/null
@@ -1,58 +0,0 @@
-# Chatty
-The idea is the following:
-- tcp server that you can send messages to
-- history upon connecting
-- date of messages sent
-- authentication
-- encrypted communication (tls?)
-- client for reading the messages and sending them at the same time
-
-# Common
-- use memory arena's to manage memory
-- manage memory for what if it will not fit
- - for just do nothing when the limit is reached
-
-# Server
-- min height & width
-- wrapping input
-- [ ] bug: retransmissed message have no text
-- [ ] history
-- [x] max y for new messages and make them scroll
-- [x] check resize event
-- [x] asynchronously receive/send a message
-- [x] send message to all other clients
-- [x] fix receiving messages with arbitrary text length
-- [x] bug: server copying the bytes correctly
-
-- rooms
-- encryption
-- authentication
-
-# Client
-- bug: when having multiple messages and resizing a lot, the output will be in shambles
-- bug: when resizing afters sending messages over network it crashes
-- bug: all messages using the same buffer for text
-- bug: 1. open chatty, send a message with send, messages won't be received by the server
-- bug: memcpy is overlapping a byte in the next message when messages_add
-- use pointer for add_message
-- validation of sent/received messages
-- handle disconnection
-
-# Questions
-- will two consecutive sends be read in one recv
- - not always.
-- can you recv a message in two messages
- - yes, done.
-
-# Message protocol
-Version 1
-1 version byte
-4 length bytes
-12 message_author bytes
-- 11 chars + \0
-9 timestamp bytes
-- 8chars + \0
-x text bytes
-- x bytes + \0
-
-The variable text bytes can be calculated by substracting the author and timestamp from the length
diff --git a/archived/v1/arena.h b/archived/v1/arena.h
deleted file mode 100644
index 6f371f4..0000000
--- a/archived/v1/arena.h
+++ /dev/null
@@ -1,116 +0,0 @@
-#ifndef ARENA_IMPL
-#define ARENA_IMPL
-
-#include <fcntl.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <sys/mman.h>
-#include <unistd.h>
-
-#define PAGESIZE 4096
-
-#ifndef ARENA_MEMORY
-#define ARENA_MEMORY PAGESIZE
-#endif
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t s8;
-typedef int16_t s16;
-typedef int32_t s32;
-typedef int64_t s64;
-
-struct Arena {
- void *memory;
- u64 size;
- u64 pos;
-} typedef Arena;
-
-// Create an arena
-Arena *ArenaAlloc(void);
-// Destroy an arena
-void ArenaRelease(Arena *arena);
-
-// Push bytes on to the arena | allocating
-void *ArenaPush(Arena *arena, u64 size);
-void *ArenaPushZero(Arena *arena, u64 size);
-
-#define PushArray(arena, type, count) (type *)ArenaPush((arena), sizeof(type) * (count))
-#define PushArrayZero(arena, type, count) (type *)ArenaPushZero((arena), sizeof(type) * (count))
-#define PushStruct(arena, type) PushArray((arena), (type), 1)
-#define PushStructZero(arena, type) PushArrayZero((arena), (type), 1)
-
-// Free some bytes by popping the stack
-void ArenaPop(Arena *arena, u64 size);
-// Get the number of bytes allocated
-u64 ArenaGetPos(Arena *arena);
-
-void ArenaSetPosBack(Arena *arena, u64 pos);
-void ArenaClear(Arena *arena);
-
-Arena *ArenaAlloc(void)
-{
- // NOTE: If the arena is created here the pointer to the memory get's overwritten with size in
- // ArenaPush, so we are forced to use malloc
- Arena *arena = malloc(sizeof(Arena));
-
- arena->memory = mmap(NULL, ARENA_MEMORY, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
- if (arena->memory == MAP_FAILED)
- return NULL;
-
- arena->pos = 0;
- arena->size = ARENA_MEMORY;
-
- return arena;
-}
-
-void ArenaRelease(Arena *arena)
-{
- munmap(arena->memory, ARENA_MEMORY);
- free(arena);
-}
-
-void *ArenaPush(Arena *arena, u64 size)
-{
- u64 *mem;
- mem = (u64 *)arena->memory + arena->pos;
- arena->pos += size;
- return mem;
-}
-
-void *ArenaPushZero(Arena *arena, u64 size)
-{
- u64 *mem;
- mem = (u64 *)arena->memory + arena->pos;
- bzero(mem, size);
- arena->pos += size;
- return mem;
-}
-
-void ArenaPop(Arena *arena, u64 size)
-{
- arena->pos -= size;
-}
-
-u64 ArenaGetPos(Arena *arena)
-{
- return arena->pos;
-}
-
-void ArenaSetPosBack(Arena *arena, u64 pos)
-{
- arena->pos -= pos;
-}
-
-void ArenaClear(Arena *arena)
-{
- bzero(arena->memory, arena->size);
- arena->pos = 0;
-}
-
-#endif
diff --git a/archived/v1/build.sh b/archived/v1/build.sh
deleted file mode 100755
index a80b193..0000000
--- a/archived/v1/build.sh
+++ /dev/null
@@ -1,6 +0,0 @@
-#!/bin/sh
-set -x
-gcc -ggdb -Wall -pedantic -std=c99 -o client client.c
-gcc -ggdb -Wall -pedantic -std=c99 -o server server.c
-gcc -ggdb -Wall -pedantic -std=c99 -o send send.c
-gcc -ggdb -Wall -pedantic -std=c99 -o recv recv.c
diff --git a/archived/v1/client.c b/archived/v1/client.c
deleted file mode 100644
index 878b678..0000000
--- a/archived/v1/client.c
+++ /dev/null
@@ -1,259 +0,0 @@
-// Client for chatty
-
-// initial size for the messages array
-#define MESSAGES_SIZE 5
-
-// clang-format off
-#define TB_IMPL
-#include "termbox2.h"
-// clang-format on
-#include "common.h"
-
-#include <arpa/inet.h>
-#include <errno.h>
-#include <poll.h>
-#include <stdarg.h>
-#include <sys/socket.h>
-#include <time.h>
-#include <unistd.h>
-
-enum { FD_SERVER = 0,
- FD_TTY,
- FD_RESIZE,
- FD_MAX };
-
-// offset of the input prompt
-int curs_offs_x = 2;
-int prompt_offs_y = 3;
-
-// filedescriptor for server
-static int serverfd;
-// Input message to be send
-Message input = {
- .author = USERNAME,
- .timestamp = {0},
- .text_len = 0,
-};
-// current amount of messages
-int nmessages = 0;
-// length of messages array
-int messages_size = MESSAGES_SIZE;
-// All messages sent and received in order
-Message messages[MESSAGES_SIZE] = {0};
-// incremented each time a new message is printed
-int msg_y = 0;
-
-// Cleans up resources, should called before exiting.
-void cleanup(void);
-// Displays an error message msg, followed by the errno variable and exits exeuction.
-void err_exit(const char *msg);
-// Display the welcome ui screen containing the prompt and messages array.
-void scren_welcome(void);
-// Append msg to the messages array. Returns -1 if there was no space in the messages array
-// otherwise returns 0 on success.
-u8 messages_add(Message *msg);
-
-void cleanup(void)
-{
- tb_shutdown();
- if (serverfd)
- if (close(serverfd))
- writef("Error while closing server socket. errno: %d\n", errno);
-}
-
-// panic
-void err_exit(const char *msg)
-{
- cleanup();
- writef("%s errno: %d\n", msg, errno);
- _exit(1);
-}
-
-void screen_welcome(void)
-{
- tb_set_cursor(curs_offs_x, global.height - prompt_offs_y);
- tb_print(0, global.height - prompt_offs_y, 0, 0, ">");
-
- // if there is not enough space to fit all messages, skip the n first messages of the array.
- int skip = 0;
- int lines_available = global.height - prompt_offs_y - 1; // pad by 1 from prompt
- if (lines_available - nmessages < 0)
- skip = nmessages - lines_available;
- for (msg_y = skip; msg_y < nmessages; msg_y++) {
- tb_printf(0, msg_y - skip, 0, 0, "%s [%s]: %s", messages[msg_y].timestamp, messages[msg_y].author, messages[msg_y].text);
- }
-}
-
-u8 messages_add(Message *msg)
-{
- if (nmessages == messages_size) {
- return -1;
- }
-
- memcpy(messages[nmessages].author, msg->author, MESSAGE_AUTHOR_LEN);
- memcpy(messages[nmessages].timestamp, msg->timestamp, MESSAGE_TIMESTAMP_LEN);
- messages[nmessages].text_len = msg->text_len;
- messages[nmessages].text = msg->text;
-
- nmessages++;
- msg_y++;
-
- return 0;
-}
-
-int main(void)
-{
- // current event
- struct tb_event ev;
- // time for a new entered message
- time_t now;
- // localtime of new sent message
- struct tm *ltime;
- char buf[MESSAGE_MAX];
- input.text = buf;
-
- int serverfd, ttyfd, resizefd;
- Message msg_recv = {0};
- const struct sockaddr_in address = {
- AF_INET,
- htons(PORT),
- {0},
- };
-
- tb_init();
- bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
-
- screen_welcome();
- tb_present();
-
- tb_get_fds(&ttyfd, &resizefd);
- serverfd = socket(AF_INET, SOCK_STREAM, 0);
-
- struct pollfd fds[FD_MAX] = {
- {serverfd, POLLIN, 0}, // FD_SERVER
- { ttyfd, POLLIN, 0}, // FD_TTY
- {resizefd, POLLIN, 0}, // FD_RESIZE
- };
-
- if (connect(serverfd, (struct sockaddr *)&address, sizeof(address)))
- err_exit("Error while connecting.");
-
- for (;;) {
- if (poll(fds, FD_MAX, 50000) == -1) {
- // check if it was a resize event that interrupted the system call
- if (errno == EINTR) {
- tb_peek_event(&ev, 80);
- if (ev.type != TB_EVENT_RESIZE)
- err_exit("Error while polling.");
- else {
- tb_clear();
- screen_welcome();
- }
- }
- }
-
- if (fds[FD_TTY].revents & POLLIN) {
- tb_poll_event(&ev);
- switch (ev.key) {
- // exit
- case TB_KEY_CTRL_C:
- case TB_KEY_CTRL_D:
- case TB_KEY_ESC:
- goto exit_loop;
- // remove line till cursor
- case TB_KEY_CTRL_U:
- while (global.cursor_x > curs_offs_x) {
- global.cursor_x--;
- tb_print(global.cursor_x, global.cursor_y, 0, 0, " ");
- }
- tb_set_cursor(curs_offs_x, global.cursor_y);
- input.text_len = 0;
- break;
- // send message
- case TB_KEY_CTRL_M:
- if (input.text_len <= 0)
- break;
- while (global.cursor_x > curs_offs_x) {
- global.cursor_x--;
- tb_print(global.cursor_x, global.cursor_y, 0, 0, " ");
- }
- tb_set_cursor(curs_offs_x, global.cursor_y);
-
- // zero terminate
- input.text[input.text_len] = 0;
-
- // print new message
- time(&now);
- ltime = localtime(&now);
- strftime((char*)input.timestamp, sizeof(input.timestamp), "%H:%M:%S", ltime);
-
- messages_add(&input);
-
- if (message_send(&input, serverfd) == -1)
- err_exit("Error while sending message.");
-
- // reset buffer
- input.text_len = 0;
-
- // update the screen
- // NOTE: kind of wasteful cause we should only display new message
- tb_clear();
- screen_welcome();
-
- break;
- // remove word
- case TB_KEY_CTRL_W:
- // Delete consecutive space
- while (input.text[input.text_len - 1] == ' ' && global.cursor_x > curs_offs_x) {
- global.cursor_x--;
- input.text_len--;
- tb_print(global.cursor_x, global.cursor_y, 0, 0, " ");
- }
- // Delete until next non-space
- while (input.text[input.text_len - 1] != ' ' && global.cursor_x > curs_offs_x) {
- global.cursor_x--;
- input.text_len--;
- tb_print(global.cursor_x, global.cursor_y, 0, 0, " ");
- }
- input.text[input.text_len] = 0;
- break;
- }
-
- // append pressed character to input.text
- // TODO: wrap instead, allocate more ram for the message instead
- if (ev.ch > 0 && input.text_len < MESSAGE_MAX && input.text_len < global.width - 3 - 1) {
- tb_printf(global.cursor_x, global.cursor_y, 0, 0, "%c", ev.ch);
- global.cursor_x++;
-
- input.text[input.text_len++] = ev.ch;
- }
-
- } else if (fds[FD_SERVER].revents & POLLIN) {
- int nrecv = message_receive(&msg_recv, serverfd);
-
- if (nrecv == 0) {
- // Server closed
- // TODO: error message like (disconnected)
- break;
- } else if (nrecv == -1) {
- err_exit("Error while receiveiving from server.");
- }
- messages_add(&msg_recv);
- tb_clear();
- screen_welcome();
-
- } else if (fds[FD_RESIZE].revents & POLLIN) {
- tb_poll_event(&ev);
- if (ev.type == TB_EVENT_RESIZE) {
- tb_clear();
- screen_welcome();
- }
- }
-
- tb_present();
- }
-exit_loop:;
-
- cleanup();
- return 0;
-}
diff --git a/archived/v1/common.h b/archived/v1/common.h
deleted file mode 100644
index c7bb0dd..0000000
--- a/archived/v1/common.h
+++ /dev/null
@@ -1,181 +0,0 @@
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#define PORT 9983
-// max buffer size sent over network
-// TODO: choose a better size
-#define BUF_MAX 256
-// max size for a message sent
-#define MESSAGE_MAX 256
-// max length of author field
-#define MESSAGE_AUTHOR_LEN 13
-// max length of timestamp field
-#define MESSAGE_TIMESTAMP_LEN 9
-// current user's name
-#define USERNAME "Jef Koek"
-
-typedef uint8_t u8;
-typedef uint16_t u16;
-typedef uint32_t u32;
-typedef uint64_t u64;
-typedef int8_t s8;
-typedef int16_t s16;
-typedef int32_t s32;
-typedef int64_t s64;
-
-// To serialize the text that could be arbitrary length the lenght is encoded after the author
-// string and before the text.
-struct Message {
- u8 author[MESSAGE_AUTHOR_LEN];
- u8 timestamp[MESSAGE_TIMESTAMP_LEN]; // HH:MM:SS
- u16 text_len; // length of the text including null terminator
- char *text;
-} typedef Message;
-
-// printf without buffering using write syscall, works when using sockets
-void writef(char *format, ...);
-
-u16 str_len(char *str);
-
-// save the message msg to file in binary format, returns zero on success, returns 1 if the msg.text
-// was empty which should not be allowed.
-u8 message_fsave(Message *msg, FILE *f);
-// load the message msg from file f, returns zero on success, returns 1 if the msg.text
-// was empty which should not be allowed.
-u8 message_fload(Message *msg, FILE *f);
-
-// Encode msg and send it to fd
-// return -1 if send() returns -1. Otherwise returns number of bytes sent.
-// NOTE: this function should not alter the content stored in msg.
-u32 message_send(Message *msg, u32 fd);
-// Decode data from fd and populate msg with it
-// if recv() returns 0 or -1 it will return early and return 0 or -1 accordingly.
-// Otherwise returns the number of bytes received
-u32 message_receive(Message *msg, u32 fd);
-
-void writef(char *format, ...)
-{
- char buf[255 + 1];
- va_list args;
- va_start(args, format);
-
- vsnprintf(buf, sizeof(buf), format, args);
- va_end(args);
-
- int n = 0;
- while (*(buf + n) != 0)
- n++;
- write(0, buf, n);
-}
-
-// Returns the length of the string plus the null terminator
-u16 str_len(char *str)
-{
- if (*str == 0)
- return 0;
-
- u16 i = 0;
- while (str[i])
- i++;
-
- return i + 1;
-}
-
-void str_cpy(char *to, char *from)
-{
- while ((*to++ = *from++))
- ;
-}
-
-// Save msg to file f
-// Returns 0 on success, returns 1 if msg->text is NULL, returns 2 if mfg->len is 0
-u8 message_fsave(Message *msg, FILE *f)
-{
- if (msg->text == NULL) {
- return 1;
- } else if (msg->text_len == 0)
- return 2;
-
- fwrite(&msg->timestamp, sizeof(*msg->timestamp) * MESSAGE_TIMESTAMP_LEN, 1, f);
- fwrite(&msg->author, sizeof(*msg->author) * MESSAGE_AUTHOR_LEN, 1, f);
- fwrite(&msg->text_len, sizeof(msg->text_len), 1, f);
- fwrite(&msg->text, msg->text_len, 1, f);
-
- return 0;
-}
-
-u8 message_fload(Message *msg, FILE *f)
-{
- fread(msg, sizeof(*msg->timestamp) * MESSAGE_TIMESTAMP_LEN + sizeof(*msg->author) * MESSAGE_AUTHOR_LEN, 1, f);
- u16 len;
- fread(&len, sizeof(len), 1, f);
- if (len == 0) {
- // TODO: Error: empty message should not be allowed
- // empty message
- msg->text = NULL;
- return 1;
- }
- char txt[len];
- fgets(txt, len, f);
- memcpy(msg->text, txt, len);
-
- return 0;
-}
-
-u32 message_send(Message *msg, u32 serverfd)
-{
- // for protocol see README.md
- u32 buf_len = sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN + msg->text_len;
- char buf[buf_len];
- u32 offset;
-
- memcpy(buf, &buf_len, sizeof(buf_len));
- offset = sizeof(buf_len);
- memcpy(buf + offset, msg->author, MESSAGE_AUTHOR_LEN);
- offset += MESSAGE_AUTHOR_LEN;
- memcpy(buf + offset, msg->timestamp, MESSAGE_TIMESTAMP_LEN);
- offset += MESSAGE_TIMESTAMP_LEN;
- memcpy(buf + offset, msg->text, msg->text_len);
-
- u32 n = send(serverfd, &buf, buf_len, 0);
- if (n == -1)
- return n;
-
- return n;
-}
-
-u32 message_receive(Message *msg, u32 clientfd)
-{
- // for protocol see README.md
- // must all be of the s
- u32 nrecv = 0, buf_len = 0;
- // limit on what can be received with recv()
- u32 buf_size = 20;
- // temporary buffer to receive message data over a stream
- char recv_buf[BUF_MAX] = {0};
-
- nrecv = recv(clientfd, recv_buf, buf_size, 0);
- if (nrecv == 0 || nrecv == -1)
- return nrecv;
-
- memcpy(&buf_len, recv_buf, sizeof(buf_len));
-
- u32 i = 0;
- while (nrecv < buf_len) {
- // advance the copying by the amounts of bytes received each time
- i = recv(clientfd, recv_buf + nrecv, buf_size, 0);
- if (i == 0 || i == -1)
- return nrecv;
- nrecv += i;
- }
-
- memcpy(msg, recv_buf + sizeof(buf_len), MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN);
- msg->text = recv_buf + sizeof(buf_len) + MESSAGE_AUTHOR_LEN + MESSAGE_TIMESTAMP_LEN;
- msg->text_len = buf_len - sizeof(buf_len) - MESSAGE_AUTHOR_LEN - MESSAGE_TIMESTAMP_LEN;
-
- return nrecv;
-}
diff --git a/archived/v1/compile_flags.txt b/archived/v1/compile_flags.txt
deleted file mode 100644
index 1a62790..0000000
--- a/archived/v1/compile_flags.txt
+++ /dev/null
@@ -1,5 +0,0 @@
--Wall
--Werror
--pedantic
--std=c99
--O3
diff --git a/archived/v1/recv.c b/archived/v1/recv.c
deleted file mode 100644
index 042d8a9..0000000
--- a/archived/v1/recv.c
+++ /dev/null
@@ -1,59 +0,0 @@
-// Minimal server implementation for probing out things
-
-#include "common.h"
-#include <arpa/inet.h>
-#include <assert.h>
-#include <poll.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-int main(void)
-{
- u32 serverfd, clientfd;
- u8 on = 1;
-
- const struct sockaddr_in address = {
- AF_INET,
- htons(PORT),
- {0},
- };
-
- serverfd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
- setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on));
- u32 err = bind(serverfd, (struct sockaddr *)&address, sizeof(address));
- assert(err == 0);
-
- err = listen(serverfd, 256);
- assert(err == 0);
-
- clientfd = accept(serverfd, 0, 0);
- assert(clientfd != -1);
-
- struct pollfd fds[1] = {
- {clientfd, POLLIN, 0},
- };
-
- for (;;) {
- int ret = poll(fds, 1, 50000);
- assert(ret != -1);
-
- if (fds[0].revents & POLLIN) {
- u8 recv_buf[BUF_MAX];
- u32 nrecv = recv(clientfd, recv_buf, sizeof(recv_buf), 0);
- assert(nrecv >= 0);
-
- writef("client(%d): %d bytes received.\n", clientfd, nrecv);
- if (nrecv == 0) {
- writef("client(%d): disconnected.\n", clientfd);
- fds[0].fd = -1;
- fds[0].revents = 0;
- err = close(clientfd);
- assert(err == 0);
-
- return 0;
- }
- }
- }
-
- return 0;
-}
diff --git a/archived/v1/send.c b/archived/v1/send.c
deleted file mode 100644
index 27e9793..0000000
--- a/archived/v1/send.c
+++ /dev/null
@@ -1,61 +0,0 @@
-// minimal client implementation
-#include "common.h"
-#include <arpa/inet.h>
-#include <errno.h>
-#include <signal.h>
-#include <time.h>
-#include <unistd.h>
-
-u32 serverfd;
-
-// NOTE: Errno could be unset and contain an error for a previous command
-void debug_panic(const char *msg)
-{
- writef("%s errno: %d\n", msg, errno);
- raise(SIGINT);
-}
-
-// get current time in timestamp string
-void timestamp(u8 timestamp[MESSAGE_TIMESTAMP_LEN])
-{
- time_t now;
- struct tm *ltime;
- time(&now);
- ltime = localtime(&now);
- strftime((char*)timestamp, MESSAGE_TIMESTAMP_LEN, "%H:%M:%S", ltime);
-}
-
-int main(int argc, char **argv)
-{
-
- if (argc < 2) {
- printf("usage: send <msg>\n");
- return 1;
- }
-
- Message input = {
- .author = "Friendship",
- };
- input.text = argv[1];
- input.text_len = str_len(input.text);
-
- serverfd = socket(AF_INET, SOCK_STREAM, 0);
- if (serverfd == -1)
- debug_panic("Error while getting socket.");
-
- const struct sockaddr_in address = {
- AF_INET,
- htons(PORT),
- {0},
- };
-
- if (connect(serverfd, (struct sockaddr *)&address, sizeof(address)))
- debug_panic("Error while connecting.");
-
- printf("input.len: %d\n", input.text_len);
- timestamp(input.timestamp);
-
- message_send(&input, serverfd);
-
- return 0;
-}
diff --git a/archived/v1/server.c b/archived/v1/server.c
deleted file mode 100644
index 7edf558..0000000
--- a/archived/v1/server.c
+++ /dev/null
@@ -1,148 +0,0 @@
-// Server for chatty
-#include "common.h"
-#include <arpa/inet.h>
-#include <errno.h>
-#include <poll.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/socket.h>
-#include <unistd.h>
-
-#define MAX_CONNECTIONS 5
-#define FD_MAX MAX_CONNECTIONS + 1
-
-// static const u8 *filename = "history.dat";
-
-enum { FD_SERVER = 0 };
-u32 serverfd;
-
-void err_exit(const char *msg)
-{
- if (serverfd)
- if (close(serverfd))
- writef("Error while closing server socket. errno: %d\n", errno);
- fprintf(stderr, "%s errno: %d\n", msg, errno);
- _exit(1);
-}
-
-int main(void)
-{
- u32 clientfd;
- u16 nclient = 0;
- u32 on = 1;
- Message msg_recv = {0};
-
- serverfd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP);
- if (setsockopt(serverfd, SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)))
- err_exit("Error while setting socket option.");
-
- const struct sockaddr_in address = {
- AF_INET,
- htons(PORT),
- {0},
- };
-
- if (bind(serverfd, (struct sockaddr *)&address, sizeof(address)))
- err_exit("Error while binding.");
-
- if (listen(serverfd, BUF_MAX))
- err_exit("Error while listening");
-
- writef("Listening on localhost:%d\n", PORT);
-
- struct pollfd fds[MAX_CONNECTIONS + 1] = {
- {serverfd, POLLIN, 0}, // FD_SERVER
- { -1, POLLIN, 0},
- { -1, POLLIN, 0},
- { -1, POLLIN, 0},
- { -1, POLLIN, 0},
- { -1, POLLIN, 0},
- };
-
- for (;;) {
- u32 ret = poll(fds, FD_MAX, 50000);
- if (ret == -1)
- err_exit("Error while polling");
- else if (ret == 0) {
- writef("polling timed out.\n");
- continue;
- }
-
- // New client tries to connect to serverfd
- if (fds[FD_SERVER].revents & POLLIN) {
- clientfd = accept(serverfd, NULL, NULL);
-
- // When MAX_CONNECTIONS is reached, close new clients trying to connect.
- if (nclient == MAX_CONNECTIONS) {
- writef("Max connections reached.\n");
- if (send(clientfd, 0, 0, 0) == -1)
- err_exit("Error while sending EOF to client socket.");
- if (shutdown(clientfd, SHUT_RDWR))
- err_exit("Error while shutting down client socket.");
- if (close(clientfd))
- err_exit("Error while closing client socket.");
- } else if (clientfd != -1) {
- nclient++;
-
- // get a new available spot in the fds array
- u32 i;
- for (i = 0; i < MAX_CONNECTIONS; i++)
- if (fds[i].fd == -1)
- break;
- fds[i].fd = clientfd;
- writef("New client: %d, %d\n", i, clientfd);
-
- } else {
- writef("Could not accept client errno: %d\n", errno);
- }
- }
-
- // Check for events on connected clients
- for (u32 i = 1; i <= nclient; i++) {
- if (!(fds[i].revents & POLLIN))
- continue;
-
- u32 nrecv;
-
- clientfd = fds[i].fd;
-
- nrecv = message_receive(&msg_recv, clientfd);
- if (nrecv == 0) {
- printf("client %d disconnected.\n", i);
- fds[i].fd = -1;
- fds[i].revents = 0;
- if (shutdown(clientfd, SHUT_RDWR))
- err_exit("Error while shutting down client %d socket.");
- if (close(clientfd))
- err_exit("Error while cloing client socket.");
- nclient--;
- break;
- } else if (nrecv == -1) {
- // TODO: this can happen when connect is reset by pear
- err_exit("Error while receiving from client socket.");
- }
-
- writef("Received %d bytes from client(%d): %s [%s] (%d)%s\n", nrecv, clientfd - serverfd, msg_recv.timestamp, msg_recv.author, msg_recv.text_len, msg_recv.text);
-
- // TODO:
- for (u32 j = 1; j <= nclient; j++) {
- // skip the client that sent the message
- if (j == i)
- continue;
- if (message_send(&msg_recv, fds[j].fd) == -1)
- printf("Error while sendig message to client %d. errno: %d\n", j, errno);
- else
- printf("Retransmitted message to client %d.\n", j);
- }
-
- // // TODO: Serialize received message
- // FILE *f = fopen(filename, "wb");
- // save_message(&msg_recv, f);
- // fclose(f);
- // // return 0;
- }
- }
-
- return 0;
-}
diff --git a/archived/v1/termbox2.h b/archived/v1/termbox2.h
deleted file mode 100644
index 265cdab..0000000
--- a/archived/v1/termbox2.h
+++ /dev/null
@@ -1,3517 +0,0 @@
-/*
-MIT License
-
-Copyright (c) 2010-2020 nsf <no.smile.face@gmail.com>
- 2015-2024 Adam Saponara <as@php.net>
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
-*/
-
-#ifndef TERMBOX_H_INCL
-#define TERMBOX_H_INCL
-
-#ifndef _XOPEN_SOURCE
-#define _XOPEN_SOURCE
-#endif
-
-#ifndef _DEFAULT_SOURCE
-#define _DEFAULT_SOURCE
-#endif
-
-#include <errno.h>
-#include <fcntl.h>
-#include <limits.h>
-#include <signal.h>
-#include <stdarg.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <sys/select.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <termios.h>
-#include <unistd.h>
-#include <wchar.h>
-#include <wctype.h>
-
-#ifdef PATH_MAX
-#define TB_PATH_MAX PATH_MAX
-#else
-#define TB_PATH_MAX 4096
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-// __ffi_start
-
-#define TB_VERSION_STR "2.5.0-dev"
-
-/* The following compile-time options are supported:
- *
- * TB_OPT_ATTR_W: Integer width of fg and bg attributes. Valid values
- * (assuming system support) are 16, 32, and 64. (See
- * uintattr_t). 32 or 64 enables output mode
- * TB_OUTPUT_TRUECOLOR. 64 enables additional style
- * attributes. (See tb_set_output_mode.) Larger values
- * consume more memory in exchange for more features.
- * Defaults to 16.
- *
- * TB_OPT_EGC: If set, enable extended grapheme cluster support
- * (tb_extend_cell, tb_set_cell_ex). Consumes more memory.
- * Defaults off.
- *
- * TB_OPT_PRINTF_BUF: Write buffer size for printf operations. Represents the
- * largest string that can be sent in one call to tb_print*
- * and tb_send* functions. Defaults to 4096.
- *
- * TB_OPT_READ_BUF: Read buffer size for tty reads. Defaults to 64.
- *
- * TB_OPT_TRUECOLOR: Deprecated. Sets TB_OPT_ATTR_W to 32 if not already set.
- */
-
-#if defined(TB_LIB_OPTS) || 0 // __tb_lib_opts
-/* Ensure consistent compile-time options when using as a shared library */
-#undef TB_OPT_ATTR_W
-#undef TB_OPT_EGC
-#undef TB_OPT_PRINTF_BUF
-#undef TB_OPT_READ_BUF
-#define TB_OPT_ATTR_W 64
-#define TB_OPT_EGC
-#endif
-
-/* Ensure sane `TB_OPT_ATTR_W` (16, 32, or 64) */
-#if defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 16
-#elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 32
-#elif defined TB_OPT_ATTR_W && TB_OPT_ATTR_W == 64
-#else
-#undef TB_OPT_ATTR_W
-#if defined TB_OPT_TRUECOLOR // Deprecated. Back-compat for old flag.
-#define TB_OPT_ATTR_W 32
-#else
-#define TB_OPT_ATTR_W 16
-#endif
-#endif
-
-/* ASCII key constants (`tb_event.key`) */
-#define TB_KEY_CTRL_TILDE 0x00
-#define TB_KEY_CTRL_2 0x00 // clash with `CTRL_TILDE`
-#define TB_KEY_CTRL_A 0x01
-#define TB_KEY_CTRL_B 0x02
-#define TB_KEY_CTRL_C 0x03
-#define TB_KEY_CTRL_D 0x04
-#define TB_KEY_CTRL_E 0x05
-#define TB_KEY_CTRL_F 0x06
-#define TB_KEY_CTRL_G 0x07
-#define TB_KEY_BACKSPACE 0x08
-#define TB_KEY_CTRL_H 0x08 // clash with `CTRL_BACKSPACE`
-#define TB_KEY_TAB 0x09
-#define TB_KEY_CTRL_I 0x09 // clash with `TAB`
-#define TB_KEY_CTRL_J 0x0a
-#define TB_KEY_CTRL_K 0x0b
-#define TB_KEY_CTRL_L 0x0c
-#define TB_KEY_ENTER 0x0d
-#define TB_KEY_CTRL_M 0x0d // clash with `ENTER`
-#define TB_KEY_CTRL_N 0x0e
-#define TB_KEY_CTRL_O 0x0f
-#define TB_KEY_CTRL_P 0x10
-#define TB_KEY_CTRL_Q 0x11
-#define TB_KEY_CTRL_R 0x12
-#define TB_KEY_CTRL_S 0x13
-#define TB_KEY_CTRL_T 0x14
-#define TB_KEY_CTRL_U 0x15
-#define TB_KEY_CTRL_V 0x16
-#define TB_KEY_CTRL_W 0x17
-#define TB_KEY_CTRL_X 0x18
-#define TB_KEY_CTRL_Y 0x19
-#define TB_KEY_CTRL_Z 0x1a
-#define TB_KEY_ESC 0x1b
-#define TB_KEY_CTRL_LSQ_BRACKET 0x1b // clash with 'ESC'
-#define TB_KEY_CTRL_3 0x1b // clash with 'ESC'
-#define TB_KEY_CTRL_4 0x1c
-#define TB_KEY_CTRL_BACKSLASH 0x1c // clash with 'CTRL_4'
-#define TB_KEY_CTRL_5 0x1d
-#define TB_KEY_CTRL_RSQ_BRACKET 0x1d // clash with 'CTRL_5'
-#define TB_KEY_CTRL_6 0x1e
-#define TB_KEY_CTRL_7 0x1f
-#define TB_KEY_CTRL_SLASH 0x1f // clash with 'CTRL_7'
-#define TB_KEY_CTRL_UNDERSCORE 0x1f // clash with 'CTRL_7'
-#define TB_KEY_SPACE 0x20
-#define TB_KEY_BACKSPACE2 0x7f
-#define TB_KEY_CTRL_8 0x7f // clash with 'BACKSPACE2'
-
-#define tb_key_i(i) 0xffff - (i)
-/* Terminal-dependent key constants (`tb_event.key`) and terminfo caps */
-/* BEGIN codegen h */
-/* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:47 +0000 */
-#define TB_KEY_F1 (0xffff - 0)
-#define TB_KEY_F2 (0xffff - 1)
-#define TB_KEY_F3 (0xffff - 2)
-#define TB_KEY_F4 (0xffff - 3)
-#define TB_KEY_F5 (0xffff - 4)
-#define TB_KEY_F6 (0xffff - 5)
-#define TB_KEY_F7 (0xffff - 6)
-#define TB_KEY_F8 (0xffff - 7)
-#define TB_KEY_F9 (0xffff - 8)
-#define TB_KEY_F10 (0xffff - 9)
-#define TB_KEY_F11 (0xffff - 10)
-#define TB_KEY_F12 (0xffff - 11)
-#define TB_KEY_INSERT (0xffff - 12)
-#define TB_KEY_DELETE (0xffff - 13)
-#define TB_KEY_HOME (0xffff - 14)
-#define TB_KEY_END (0xffff - 15)
-#define TB_KEY_PGUP (0xffff - 16)
-#define TB_KEY_PGDN (0xffff - 17)
-#define TB_KEY_ARROW_UP (0xffff - 18)
-#define TB_KEY_ARROW_DOWN (0xffff - 19)
-#define TB_KEY_ARROW_LEFT (0xffff - 20)
-#define TB_KEY_ARROW_RIGHT (0xffff - 21)
-#define TB_KEY_BACK_TAB (0xffff - 22)
-#define TB_KEY_MOUSE_LEFT (0xffff - 23)
-#define TB_KEY_MOUSE_RIGHT (0xffff - 24)
-#define TB_KEY_MOUSE_MIDDLE (0xffff - 25)
-#define TB_KEY_MOUSE_RELEASE (0xffff - 26)
-#define TB_KEY_MOUSE_WHEEL_UP (0xffff - 27)
-#define TB_KEY_MOUSE_WHEEL_DOWN (0xffff - 28)
-
-#define TB_CAP_F1 0
-#define TB_CAP_F2 1
-#define TB_CAP_F3 2
-#define TB_CAP_F4 3
-#define TB_CAP_F5 4
-#define TB_CAP_F6 5
-#define TB_CAP_F7 6
-#define TB_CAP_F8 7
-#define TB_CAP_F9 8
-#define TB_CAP_F10 9
-#define TB_CAP_F11 10
-#define TB_CAP_F12 11
-#define TB_CAP_INSERT 12
-#define TB_CAP_DELETE 13
-#define TB_CAP_HOME 14
-#define TB_CAP_END 15
-#define TB_CAP_PGUP 16
-#define TB_CAP_PGDN 17
-#define TB_CAP_ARROW_UP 18
-#define TB_CAP_ARROW_DOWN 19
-#define TB_CAP_ARROW_LEFT 20
-#define TB_CAP_ARROW_RIGHT 21
-#define TB_CAP_BACK_TAB 22
-#define TB_CAP__COUNT_KEYS 23
-#define TB_CAP_ENTER_CA 23
-#define TB_CAP_EXIT_CA 24
-#define TB_CAP_SHOW_CURSOR 25
-#define TB_CAP_HIDE_CURSOR 26
-#define TB_CAP_CLEAR_SCREEN 27
-#define TB_CAP_SGR0 28
-#define TB_CAP_UNDERLINE 29
-#define TB_CAP_BOLD 30
-#define TB_CAP_BLINK 31
-#define TB_CAP_ITALIC 32
-#define TB_CAP_REVERSE 33
-#define TB_CAP_ENTER_KEYPAD 34
-#define TB_CAP_EXIT_KEYPAD 35
-#define TB_CAP_DIM 36
-#define TB_CAP_INVISIBLE 37
-#define TB_CAP__COUNT 38
-/* END codegen h */
-
-/* Some hard-coded caps */
-#define TB_HARDCAP_ENTER_MOUSE "\x1b[?1000h\x1b[?1002h\x1b[?1015h\x1b[?1006h"
-#define TB_HARDCAP_EXIT_MOUSE "\x1b[?1006l\x1b[?1015l\x1b[?1002l\x1b[?1000l"
-#define TB_HARDCAP_STRIKEOUT "\x1b[9m"
-#define TB_HARDCAP_UNDERLINE_2 "\x1b[21m"
-#define TB_HARDCAP_OVERLINE "\x1b[53m"
-
-/* Colors (numeric) and attributes (bitwise) (`tb_cell.fg`, `tb_cell.bg`) */
-#define TB_DEFAULT 0x0000
-#define TB_BLACK 0x0001
-#define TB_RED 0x0002
-#define TB_GREEN 0x0003
-#define TB_YELLOW 0x0004
-#define TB_BLUE 0x0005
-#define TB_MAGENTA 0x0006
-#define TB_CYAN 0x0007
-#define TB_WHITE 0x0008
-
-#if TB_OPT_ATTR_W == 16
-#define TB_BOLD 0x0100
-#define TB_UNDERLINE 0x0200
-#define TB_REVERSE 0x0400
-#define TB_ITALIC 0x0800
-#define TB_BLINK 0x1000
-#define TB_HI_BLACK 0x2000
-#define TB_BRIGHT 0x4000
-#define TB_DIM 0x8000
-#define TB_256_BLACK TB_HI_BLACK // `TB_256_BLACK` is deprecated
-#else
-// `TB_OPT_ATTR_W` is 32 or 64
-#define TB_BOLD 0x01000000
-#define TB_UNDERLINE 0x02000000
-#define TB_REVERSE 0x04000000
-#define TB_ITALIC 0x08000000
-#define TB_BLINK 0x10000000
-#define TB_HI_BLACK 0x20000000
-#define TB_BRIGHT 0x40000000
-#define TB_DIM 0x80000000
-#define TB_TRUECOLOR_BOLD TB_BOLD // `TB_TRUECOLOR_*` is deprecated
-#define TB_TRUECOLOR_UNDERLINE TB_UNDERLINE
-#define TB_TRUECOLOR_REVERSE TB_REVERSE
-#define TB_TRUECOLOR_ITALIC TB_ITALIC
-#define TB_TRUECOLOR_BLINK TB_BLINK
-#define TB_TRUECOLOR_BLACK TB_HI_BLACK
-#endif
-
-#if TB_OPT_ATTR_W == 64
-#define TB_STRIKEOUT 0x0000000100000000
-#define TB_UNDERLINE_2 0x0000000200000000
-#define TB_OVERLINE 0x0000000400000000
-#define TB_INVISIBLE 0x0000000800000000
-#endif
-
-/* Event types (`tb_event.type`) */
-#define TB_EVENT_KEY 1
-#define TB_EVENT_RESIZE 2
-#define TB_EVENT_MOUSE 3
-
-/* Key modifiers (bitwise) (`tb_event.mod`) */
-#define TB_MOD_ALT 1
-#define TB_MOD_CTRL 2
-#define TB_MOD_SHIFT 4
-#define TB_MOD_MOTION 8
-
-/* Input modes (bitwise) (`tb_set_input_mode`) */
-#define TB_INPUT_CURRENT 0
-#define TB_INPUT_ESC 1
-#define TB_INPUT_ALT 2
-#define TB_INPUT_MOUSE 4
-
-/* Output modes (`tb_set_output_mode`) */
-#define TB_OUTPUT_CURRENT 0
-#define TB_OUTPUT_NORMAL 1
-#define TB_OUTPUT_256 2
-#define TB_OUTPUT_216 3
-#define TB_OUTPUT_GRAYSCALE 4
-#if TB_OPT_ATTR_W >= 32
-#define TB_OUTPUT_TRUECOLOR 5
-#endif
-
-/* Common function return values unless otherwise noted.
- *
- * Library behavior is undefined after receiving `TB_ERR_MEM`. Callers may
- * attempt reinitializing by freeing memory, invoking `tb_shutdown`, then
- * `tb_init`.
- */
-#define TB_OK 0
-#define TB_ERR -1
-#define TB_ERR_NEED_MORE -2
-#define TB_ERR_INIT_ALREADY -3
-#define TB_ERR_INIT_OPEN -4
-#define TB_ERR_MEM -5
-#define TB_ERR_NO_EVENT -6
-#define TB_ERR_NO_TERM -7
-#define TB_ERR_NOT_INIT -8
-#define TB_ERR_OUT_OF_BOUNDS -9
-#define TB_ERR_READ -10
-#define TB_ERR_RESIZE_IOCTL -11
-#define TB_ERR_RESIZE_PIPE -12
-#define TB_ERR_RESIZE_SIGACTION -13
-#define TB_ERR_POLL -14
-#define TB_ERR_TCGETATTR -15
-#define TB_ERR_TCSETATTR -16
-#define TB_ERR_UNSUPPORTED_TERM -17
-#define TB_ERR_RESIZE_WRITE -18
-#define TB_ERR_RESIZE_POLL -19
-#define TB_ERR_RESIZE_READ -20
-#define TB_ERR_RESIZE_SSCANF -21
-#define TB_ERR_CAP_COLLISION -22
-
-#define TB_ERR_SELECT TB_ERR_POLL
-#define TB_ERR_RESIZE_SELECT TB_ERR_RESIZE_POLL
-
-/* Deprecated. Function types to be used with `tb_set_func`. */
-#define TB_FUNC_EXTRACT_PRE 0
-#define TB_FUNC_EXTRACT_POST 1
-
-/* Define this to set the size of the buffer used in `tb_printf`
- * and `tb_sendf`
- */
-#ifndef TB_OPT_PRINTF_BUF
-#define TB_OPT_PRINTF_BUF 4096
-#endif
-
-/* Define this to set the size of the read buffer used when reading
- * from the tty
- */
-#ifndef TB_OPT_READ_BUF
-#define TB_OPT_READ_BUF 64
-#endif
-
-/* Define this for limited back compat with termbox v1 */
-#ifdef TB_OPT_V1_COMPAT
-#define tb_change_cell tb_set_cell
-#define tb_put_cell(x, y, c) tb_set_cell((x), (y), (c)->ch, (c)->fg, (c)->bg)
-#define tb_set_clear_attributes tb_set_clear_attrs
-#define tb_select_input_mode tb_set_input_mode
-#define tb_select_output_mode tb_set_output_mode
-#endif
-
-/* Define these to swap in a different allocator */
-#ifndef tb_malloc
-#define tb_malloc malloc
-#define tb_realloc realloc
-#define tb_free free
-#endif
-
-#if TB_OPT_ATTR_W == 64
-typedef uint64_t uintattr_t;
-#elif TB_OPT_ATTR_W == 32
-typedef uint32_t uintattr_t;
-#else // 16
-typedef uint16_t uintattr_t;
-#endif
-
-/* A cell in a 2d grid representing the terminal screen.
- *
- * The terminal screen is represented as 2d array of cells. The structure is
- * optimized for dealing with single-width (`wcwidth==1`) Unicode codepoints,
- * however some support for grapheme clusters (e.g., combining diacritical
- * marks) and wide codepoints (e.g., Hiragana) is provided through `ech`,
- * `nech`, and `cech` via `tb_set_cell_ex`. `ech` is only valid when `nech>0`,
- * otherwise `ch` is used.
- *
- * For non-single-width codepoints, given `N=wcwidth(ch)/wcswidth(ech)`:
- *
- * when `N==0`: termbox forces a single-width cell. Callers should avoid this
- * if aiming to render text accurately. Callers may use
- * `tb_set_cell_ex` or `tb_print*` to render `N==0` combining
- * characters.
- *
- * when `N>1`: termbox zeroes out the following `N-1` cells and skips sending
- * them to the tty. So, e.g., if the caller sets `x=0,y=0` to an
- * `N==2` codepoint, the caller's next set should be at `x=2,y=0`.
- * Anything set at `x=1,y=0` will be ignored. If there are not
- * enough columns remaining on the line to render `N` width, spaces
- * are sent instead.
- *
- * See `tb_present` for implementation.
- */
-struct tb_cell {
- uint32_t ch; // a Unicode codepoint
- uintattr_t fg; // bitwise foreground attributes
- uintattr_t bg; // bitwise background attributes
-#ifdef TB_OPT_EGC
- uint32_t *ech; // a grapheme cluster of Unicode codepoints, 0-terminated
- size_t nech; // num elements in ech, 0 means use ch instead of ech
- size_t cech; // num elements allocated for ech
-#endif
-};
-
-/* An incoming event from the tty.
- *
- * Given the event type, the following fields are relevant:
- *
- * when `TB_EVENT_KEY`: `key` xor `ch` (one will be zero) and `mod`. Note
- * there is overlap between `TB_MOD_CTRL` and
- * `TB_KEY_CTRL_*`. `TB_MOD_CTRL` and `TB_MOD_SHIFT` are
- * only set as modifiers to `TB_KEY_ARROW_*`.
- *
- * when `TB_EVENT_RESIZE`: `w` and `h`
- *
- * when `TB_EVENT_MOUSE`: `key` (`TB_KEY_MOUSE_*`), `x`, and `y`
- */
-struct tb_event {
- uint8_t type; // one of `TB_EVENT_*` constants
- uint8_t mod; // bitwise `TB_MOD_*` constants
- uint16_t key; // one of `TB_KEY_*` constants
- uint32_t ch; // a Unicode codepoint
- int32_t w; // resize width
- int32_t h; // resize height
- int32_t x; // mouse x
- int32_t y; // mouse y
-};
-
-/* Initialize the termbox library. This function should be called before any
- * other functions. `tb_init` is equivalent to `tb_init_file("/dev/tty")`. After
- * successful initialization, the library must be finalized using `tb_shutdown`.
- */
-int tb_init(void);
-int tb_init_file(const char *path);
-int tb_init_fd(int ttyfd);
-int tb_init_rwfd(int rfd, int wfd);
-int tb_shutdown(void);
-
-/* Return the size of the internal back buffer (which is the same as terminal's
- * window size in rows and columns). The internal buffer can be resized after
- * `tb_clear` or `tb_present` calls. Both dimensions have an unspecified
- * negative value when called before `tb_init` or after `tb_shutdown`.
- */
-int tb_width(void);
-int tb_height(void);
-
-/* Clear the internal back buffer using `TB_DEFAULT` or the attributes set by
- * `tb_set_clear_attrs`.
- */
-int tb_clear(void);
-int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg);
-
-/* Synchronize the internal back buffer with the terminal by writing to tty. */
-int tb_present(void);
-
-/* Clear the internal front buffer effectively forcing a complete re-render of
- * the back buffer to the tty. It is not necessary to call this under normal
- * circumstances. */
-int tb_invalidate(void);
-
-/* Set the position of the cursor. Upper-left cell is (0, 0). */
-int tb_set_cursor(int cx, int cy);
-int tb_hide_cursor(void);
-
-/* Set cell contents in the internal back buffer at the specified position.
- *
- * Use `tb_set_cell_ex` for rendering grapheme clusters (e.g., combining
- * diacritical marks).
- *
- * Calling `tb_set_cell(x, y, ch, fg, bg)` is equivalent to
- * `tb_set_cell_ex(x, y, &ch, 1, fg, bg)`.
- *
- * `tb_extend_cell` is a shortcut for appending 1 codepoint to `tb_cell.ech`.
- *
- * Non-printable (`iswprint(3)`) codepoints are replaced with `U+FFFD` at render
- * time.
- */
-int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg);
-int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
- uintattr_t bg);
-int tb_extend_cell(int x, int y, uint32_t ch);
-
-/* Set the input mode. Termbox has two input modes:
- *
- * 1. `TB_INPUT_ESC`
- * When escape (`\x1b`) is in the buffer and there's no match for an escape
- * sequence, a key event for `TB_KEY_ESC` is returned.
- *
- * 2. `TB_INPUT_ALT`
- * When escape (`\x1b`) is in the buffer and there's no match for an escape
- * sequence, the next keyboard event is returned with a `TB_MOD_ALT`
- * modifier.
- *
- * You can also apply `TB_INPUT_MOUSE` via bitwise OR operation to either of the
- * modes (e.g., `TB_INPUT_ESC | TB_INPUT_MOUSE`) to receive `TB_EVENT_MOUSE`
- * events. If none of the main two modes were set, but the mouse mode was,
- * `TB_INPUT_ESC` is used. If for some reason you've decided to use
- * `TB_INPUT_ESC | TB_INPUT_ALT`, it will behave as if only `TB_INPUT_ESC` was
- * selected.
- *
- * If mode is `TB_INPUT_CURRENT`, return the current input mode.
- *
- * The default input mode is `TB_INPUT_ESC`.
- */
-int tb_set_input_mode(int mode);
-
-/* Set the output mode. Termbox has multiple output modes:
- *
- * 1. `TB_OUTPUT_NORMAL` => [0..8]
- *
- * This mode provides 8 different colors:
- * `TB_BLACK`, `TB_RED`, `TB_GREEN`, `TB_YELLOW`,
- * `TB_BLUE`, `TB_MAGENTA`, `TB_CYAN`, `TB_WHITE`
- *
- * Plus `TB_DEFAULT` which skips sending a color code (i.e., uses the
- * terminal's default color).
- *
- * Colors (including `TB_DEFAULT`) may be bitwise OR'd with attributes:
- * `TB_BOLD`, `TB_UNDERLINE`, `TB_REVERSE`, `TB_ITALIC`, `TB_BLINK`,
- * `TB_BRIGHT`, `TB_DIM`
- *
- * The following style attributes are also available if compiled with
- * `TB_OPT_ATTR_W` set to 64:
- * `TB_STRIKEOUT`, `TB_UNDERLINE_2`, `TB_OVERLINE`, `TB_INVISIBLE`
- *
- * As in all modes, the value 0 is interpreted as `TB_DEFAULT` for
- * convenience.
- *
- * Some notes: `TB_REVERSE` and `TB_BRIGHT` can be applied as either `fg` or
- * `bg` attributes for the same effect. The rest of the attributes apply to
- * `fg` only and are ignored as `bg` attributes.
- *
- * Example usage: `tb_set_cell(x, y, '@', TB_BLACK | TB_BOLD, TB_RED)`
- *
- * 2. `TB_OUTPUT_256` => [0..255] + `TB_HI_BLACK`
- *
- * In this mode you get 256 distinct colors (plus default):
- * 0x00 (1): `TB_DEFAULT`
- * `TB_HI_BLACK` (1): `TB_BLACK` in `TB_OUTPUT_NORMAL`
- * 0x01..0x07 (7): the next 7 colors as in `TB_OUTPUT_NORMAL`
- * 0x08..0x0f (8): bright versions of the above
- * 0x10..0xe7 (216): 216 different colors
- * 0xe8..0xff (24): 24 different shades of gray
- *
- * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in
- * `TB_OUTPUT_NORMAL`.
- *
- * Note `TB_HI_BLACK` must be used for black, as 0x00 represents default.
- *
- * 3. `TB_OUTPUT_216` => [0..216]
- *
- * This mode supports the 216-color range of `TB_OUTPUT_256` only, but you
- * don't need to provide an offset:
- * 0x00 (1): `TB_DEFAULT`
- * 0x01..0xd8 (216): 216 different colors
- *
- * 4. `TB_OUTPUT_GRAYSCALE` => [0..24]
- *
- * This mode supports the 24-color range of `TB_OUTPUT_256` only, but you
- * don't need to provide an offset:
- * 0x00 (1): `TB_DEFAULT`
- * 0x01..0x18 (24): 24 different shades of gray
- *
- * 5. `TB_OUTPUT_TRUECOLOR` => [0x000000..0xffffff] + `TB_HI_BLACK`
- *
- * This mode provides 24-bit color on supported terminals. The format is
- * 0xRRGGBB.
- *
- * All `TB_*` style attributes except `TB_BRIGHT` may be bitwise OR'd as in
- * `TB_OUTPUT_NORMAL`.
- *
- * Note `TB_HI_BLACK` must be used for black, as 0x000000 represents default.
- *
- * To use the terminal default color (i.e., to not send an escape code), pass
- * `TB_DEFAULT`. For convenience, the value 0 is interpreted as `TB_DEFAULT` in
- * all modes.
- *
- * Note, cell attributes persist after switching output modes. Any translation
- * between, for example, `TB_OUTPUT_NORMAL`'s `TB_RED` and
- * `TB_OUTPUT_TRUECOLOR`'s 0xff0000 must be performed by the caller. Also note
- * that cells previously rendered in one mode may persist unchanged until the
- * front buffer is cleared (such as after a resize event) at which point it will
- * be re-interpreted and flushed according to the current mode. Callers may
- * invoke `tb_invalidate` if it is desirable to immediately re-interpret and
- * flush the entire screen according to the current mode.
- *
- * Note, not all terminals support all output modes, especially beyond
- * `TB_OUTPUT_NORMAL`. There is also no very reliable way to determine color
- * support dynamically. If portability is desired, callers are recommended to
- * use `TB_OUTPUT_NORMAL` or make output mode end-user configurable. The same
- * advice applies to style attributes.
- *
- * If mode is `TB_OUTPUT_CURRENT`, return the current output mode.
- *
- * The default output mode is `TB_OUTPUT_NORMAL`.
- */
-int tb_set_output_mode(int mode);
-
-/* Wait for an event up to `timeout_ms` milliseconds and populate `event` with
- * it. If no event is available within the timeout period, `TB_ERR_NO_EVENT`
- * is returned. On a resize event, the underlying `select(2)` call may be
- * interrupted, yielding a return code of `TB_ERR_POLL`. In this case, you may
- * check `errno` via `tb_last_errno`. If it's `EINTR`, you may elect to ignore
- * that and call `tb_peek_event` again.
- */
-int tb_peek_event(struct tb_event *event, int timeout_ms);
-
-/* Same as `tb_peek_event` except no timeout. */
-int tb_poll_event(struct tb_event *event);
-
-/* Internal termbox fds that can be used with `poll(2)`, `select(2)`, etc.
- * externally. Callers must invoke `tb_poll_event` or `tb_peek_event` if
- * fds become readable. */
-int tb_get_fds(int *ttyfd, int *resizefd);
-
-/* Print and printf functions. Specify param `out_w` to determine width of
- * printed string. Strings are interpreted as UTF-8.
- *
- * Non-printable characters (`iswprint(3)`) and truncated UTF-8 byte sequences
- * are replaced with U+FFFD.
- *
- * Newlines (`\n`) are supported with the caveat that `out_w` will return the
- * width of the string as if it were on a single line.
- *
- * If the starting coordinate is out of bounds, `TB_ERR_OUT_OF_BOUNDS` is
- * returned. If the starting coordinate is in bounds, but goes out of bounds,
- * then the out-of-bounds portions of the string are ignored.
- *
- * For finer control, use `tb_set_cell`.
- */
-int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str);
-int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt, ...);
-int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
- const char *str);
-int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
- const char *fmt, ...);
-
-/* Send raw bytes to terminal. */
-int tb_send(const char *buf, size_t nbuf);
-int tb_sendf(const char *fmt, ...);
-
-/* Deprecated. Set custom callbacks. `fn_type` is one of `TB_FUNC_*` constants,
- * `fn` is a compatible function pointer, or NULL to clear.
- *
- * `TB_FUNC_EXTRACT_PRE`:
- * If specified, invoke this function BEFORE termbox tries to extract any
- * escape sequences from the input buffer.
- *
- * `TB_FUNC_EXTRACT_POST`:
- * If specified, invoke this function AFTER termbox tries (and fails) to
- * extract any escape sequences from the input buffer.
- */
-int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *));
-
-/* Return byte length of codepoint given first byte of UTF-8 sequence (1-6). */
-int tb_utf8_char_length(char c);
-
-/* Convert UTF-8 null-terminated byte sequence to UTF-32 codepoint.
- *
- * If `c` is an empty C string, return 0. `out` is left unchanged.
- *
- * If a null byte is encountered in the middle of the codepoint, return a
- * negative number indicating how many bytes were processed. `out` is left
- * unchanged.
- *
- * Otherwise, return byte length of codepoint (1-6).
- */
-int tb_utf8_char_to_unicode(uint32_t *out, const char *c);
-
-/* Convert UTF-32 codepoint to UTF-8 null-terminated byte sequence.
- *
- * `out` must be char[7] or greater. Return byte length of codepoint (1-6).
- */
-int tb_utf8_unicode_to_char(char *out, uint32_t c);
-
-/* Library utility functions */
-int tb_last_errno(void);
-const char *tb_strerror(int err);
-struct tb_cell *tb_cell_buffer(void); // Deprecated
-int tb_has_truecolor(void);
-int tb_has_egc(void);
-int tb_attr_width(void);
-const char *tb_version(void);
-
-/* Deprecation notice!
- *
- * The following will be removed in version 3.x (ABI version 3):
- *
- * TB_256_BLACK (use TB_HI_BLACK)
- * TB_OPT_TRUECOLOR (use TB_OPT_ATTR_W)
- * TB_TRUECOLOR_BOLD (use TB_BOLD)
- * TB_TRUECOLOR_UNDERLINE (use TB_UNDERLINE)
- * TB_TRUECOLOR_REVERSE (use TB_REVERSE)
- * TB_TRUECOLOR_ITALIC (use TB_ITALICe)
- * TB_TRUECOLOR_BLINK (use TB_BLINK)
- * TB_TRUECOLOR_BLACK (use TB_HI_BLACK)
- * tb_cell_buffer
- * tb_set_func
- * TB_FUNC_EXTRACT_PRE
- * TB_FUNC_EXTRACT_POST
- */
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif // TERMBOX_H_INCL
-
-#ifdef TB_IMPL
-
-#define if_err_return(rv, expr) \
- if (((rv) = (expr)) != TB_OK) return (rv)
-#define if_err_break(rv, expr) \
- if (((rv) = (expr)) != TB_OK) break
-#define if_ok_return(rv, expr) \
- if (((rv) = (expr)) == TB_OK) return (rv)
-#define if_ok_or_need_more_return(rv, expr) \
- if (((rv) = (expr)) == TB_OK || (rv) == TB_ERR_NEED_MORE) return (rv)
-
-#define send_literal(rv, a) \
- if_err_return((rv), bytebuf_nputs(&global.out, (a), sizeof(a) - 1))
-
-#define send_num(rv, nbuf, n) \
- if_err_return((rv), \
- bytebuf_nputs(&global.out, (nbuf), convert_num((n), (nbuf))))
-
-#define snprintf_or_return(rv, str, sz, fmt, ...) \
- do { \
- (rv) = snprintf((str), (sz), (fmt), __VA_ARGS__); \
- if ((rv) < 0 || (rv) >= (int)(sz)) return TB_ERR; \
- } while (0)
-
-#define if_not_init_return() \
- if (!global.initialized) return TB_ERR_NOT_INIT
-
-struct bytebuf_t {
- char *buf;
- size_t len;
- size_t cap;
-};
-
-struct cellbuf_t {
- int width;
- int height;
- struct tb_cell *cells;
-};
-
-struct cap_trie_t {
- char c;
- struct cap_trie_t *children;
- size_t nchildren;
- int is_leaf;
- uint16_t key;
- uint8_t mod;
-};
-
-struct tb_global_t {
- int ttyfd;
- int rfd;
- int wfd;
- int ttyfd_open;
- int resize_pipefd[2];
- int width;
- int height;
- int cursor_x;
- int cursor_y;
- int last_x;
- int last_y;
- uintattr_t fg;
- uintattr_t bg;
- uintattr_t last_fg;
- uintattr_t last_bg;
- int input_mode;
- int output_mode;
- char *terminfo;
- size_t nterminfo;
- const char *caps[TB_CAP__COUNT];
- struct cap_trie_t cap_trie;
- struct bytebuf_t in;
- struct bytebuf_t out;
- struct cellbuf_t back;
- struct cellbuf_t front;
- struct termios orig_tios;
- int has_orig_tios;
- int last_errno;
- int initialized;
- int (*fn_extract_esc_pre)(struct tb_event *, size_t *);
- int (*fn_extract_esc_post)(struct tb_event *, size_t *);
- char errbuf[1024];
-};
-
-static struct tb_global_t global = {0};
-
-/* BEGIN codegen c */
-/* Produced by ./codegen.sh on Tue, 03 Sep 2024 04:17:48 +0000 */
-
-static const int16_t terminfo_cap_indexes[] = {
- 66, // kf1 (TB_CAP_F1)
- 68, // kf2 (TB_CAP_F2)
- 69, // kf3 (TB_CAP_F3)
- 70, // kf4 (TB_CAP_F4)
- 71, // kf5 (TB_CAP_F5)
- 72, // kf6 (TB_CAP_F6)
- 73, // kf7 (TB_CAP_F7)
- 74, // kf8 (TB_CAP_F8)
- 75, // kf9 (TB_CAP_F9)
- 67, // kf10 (TB_CAP_F10)
- 216, // kf11 (TB_CAP_F11)
- 217, // kf12 (TB_CAP_F12)
- 77, // kich1 (TB_CAP_INSERT)
- 59, // kdch1 (TB_CAP_DELETE)
- 76, // khome (TB_CAP_HOME)
- 164, // kend (TB_CAP_END)
- 82, // kpp (TB_CAP_PGUP)
- 81, // knp (TB_CAP_PGDN)
- 87, // kcuu1 (TB_CAP_ARROW_UP)
- 61, // kcud1 (TB_CAP_ARROW_DOWN)
- 79, // kcub1 (TB_CAP_ARROW_LEFT)
- 83, // kcuf1 (TB_CAP_ARROW_RIGHT)
- 148, // kcbt (TB_CAP_BACK_TAB)
- 28, // smcup (TB_CAP_ENTER_CA)
- 40, // rmcup (TB_CAP_EXIT_CA)
- 16, // cnorm (TB_CAP_SHOW_CURSOR)
- 13, // civis (TB_CAP_HIDE_CURSOR)
- 5, // clear (TB_CAP_CLEAR_SCREEN)
- 39, // sgr0 (TB_CAP_SGR0)
- 36, // smul (TB_CAP_UNDERLINE)
- 27, // bold (TB_CAP_BOLD)
- 26, // blink (TB_CAP_BLINK)
- 311, // sitm (TB_CAP_ITALIC)
- 34, // rev (TB_CAP_REVERSE)
- 89, // smkx (TB_CAP_ENTER_KEYPAD)
- 88, // rmkx (TB_CAP_EXIT_KEYPAD)
- 30, // dim (TB_CAP_DIM)
- 32, // invis (TB_CAP_INVISIBLE)
-};
-
-// xterm
-static const char *xterm_caps[] = {
- "\033OP", // kf1 (TB_CAP_F1)
- "\033OQ", // kf2 (TB_CAP_F2)
- "\033OR", // kf3 (TB_CAP_F3)
- "\033OS", // kf4 (TB_CAP_F4)
- "\033[15~", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033OH", // khome (TB_CAP_HOME)
- "\033OF", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033OA", // kcuu1 (TB_CAP_ARROW_UP)
- "\033OB", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033OD", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "\033[Z", // kcbt (TB_CAP_BACK_TAB)
- "\033[?1049h\033[22;0;0t", // smcup (TB_CAP_ENTER_CA)
- "\033[?1049l\033[23;0;0t", // rmcup (TB_CAP_EXIT_CA)
- "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033(B\033[m", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "\033[3m", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD)
- "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
- "\033[2m", // dim (TB_CAP_DIM)
- "\033[8m", // invis (TB_CAP_INVISIBLE)
-};
-
-// linux
-static const char *linux_caps[] = {
- "\033[[A", // kf1 (TB_CAP_F1)
- "\033[[B", // kf2 (TB_CAP_F2)
- "\033[[C", // kf3 (TB_CAP_F3)
- "\033[[D", // kf4 (TB_CAP_F4)
- "\033[[E", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033[1~", // khome (TB_CAP_HOME)
- "\033[4~", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033[A", // kcuu1 (TB_CAP_ARROW_UP)
- "\033[B", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033[D", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "\033\011", // kcbt (TB_CAP_BACK_TAB)
- "", // smcup (TB_CAP_ENTER_CA)
- "", // rmcup (TB_CAP_EXIT_CA)
- "\033[?25h\033[?0c", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l\033[?1c", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033[m\017", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "", // smkx (TB_CAP_ENTER_KEYPAD)
- "", // rmkx (TB_CAP_EXIT_KEYPAD)
- "\033[2m", // dim (TB_CAP_DIM)
- "", // invis (TB_CAP_INVISIBLE)
-};
-
-// screen
-static const char *screen_caps[] = {
- "\033OP", // kf1 (TB_CAP_F1)
- "\033OQ", // kf2 (TB_CAP_F2)
- "\033OR", // kf3 (TB_CAP_F3)
- "\033OS", // kf4 (TB_CAP_F4)
- "\033[15~", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033[1~", // khome (TB_CAP_HOME)
- "\033[4~", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033OA", // kcuu1 (TB_CAP_ARROW_UP)
- "\033OB", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033OD", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033OC", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "\033[Z", // kcbt (TB_CAP_BACK_TAB)
- "\033[?1049h", // smcup (TB_CAP_ENTER_CA)
- "\033[?1049l", // rmcup (TB_CAP_EXIT_CA)
- "\033[34h\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033[m\017", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "\033[?1h\033=", // smkx (TB_CAP_ENTER_KEYPAD)
- "\033[?1l\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
- "\033[2m", // dim (TB_CAP_DIM)
- "", // invis (TB_CAP_INVISIBLE)
-};
-
-// rxvt-256color
-static const char *rxvt_256color_caps[] = {
- "\033[11~", // kf1 (TB_CAP_F1)
- "\033[12~", // kf2 (TB_CAP_F2)
- "\033[13~", // kf3 (TB_CAP_F3)
- "\033[14~", // kf4 (TB_CAP_F4)
- "\033[15~", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033[7~", // khome (TB_CAP_HOME)
- "\033[8~", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033[A", // kcuu1 (TB_CAP_ARROW_UP)
- "\033[B", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033[D", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "\033[Z", // kcbt (TB_CAP_BACK_TAB)
- "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA)
- "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
- "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033[m\017", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "\033=", // smkx (TB_CAP_ENTER_KEYPAD)
- "\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
- "", // dim (TB_CAP_DIM)
- "", // invis (TB_CAP_INVISIBLE)
-};
-
-// rxvt-unicode
-static const char *rxvt_unicode_caps[] = {
- "\033[11~", // kf1 (TB_CAP_F1)
- "\033[12~", // kf2 (TB_CAP_F2)
- "\033[13~", // kf3 (TB_CAP_F3)
- "\033[14~", // kf4 (TB_CAP_F4)
- "\033[15~", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033[7~", // khome (TB_CAP_HOME)
- "\033[8~", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033[A", // kcuu1 (TB_CAP_ARROW_UP)
- "\033[B", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033[D", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "\033[Z", // kcbt (TB_CAP_BACK_TAB)
- "\033[?1049h", // smcup (TB_CAP_ENTER_CA)
- "\033[r\033[?1049l", // rmcup (TB_CAP_EXIT_CA)
- "\033[?12l\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033[m\033(B", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "\033[3m", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "\033=", // smkx (TB_CAP_ENTER_KEYPAD)
- "\033>", // rmkx (TB_CAP_EXIT_KEYPAD)
- "", // dim (TB_CAP_DIM)
- "", // invis (TB_CAP_INVISIBLE)
-};
-
-// Eterm
-static const char *eterm_caps[] = {
- "\033[11~", // kf1 (TB_CAP_F1)
- "\033[12~", // kf2 (TB_CAP_F2)
- "\033[13~", // kf3 (TB_CAP_F3)
- "\033[14~", // kf4 (TB_CAP_F4)
- "\033[15~", // kf5 (TB_CAP_F5)
- "\033[17~", // kf6 (TB_CAP_F6)
- "\033[18~", // kf7 (TB_CAP_F7)
- "\033[19~", // kf8 (TB_CAP_F8)
- "\033[20~", // kf9 (TB_CAP_F9)
- "\033[21~", // kf10 (TB_CAP_F10)
- "\033[23~", // kf11 (TB_CAP_F11)
- "\033[24~", // kf12 (TB_CAP_F12)
- "\033[2~", // kich1 (TB_CAP_INSERT)
- "\033[3~", // kdch1 (TB_CAP_DELETE)
- "\033[7~", // khome (TB_CAP_HOME)
- "\033[8~", // kend (TB_CAP_END)
- "\033[5~", // kpp (TB_CAP_PGUP)
- "\033[6~", // knp (TB_CAP_PGDN)
- "\033[A", // kcuu1 (TB_CAP_ARROW_UP)
- "\033[B", // kcud1 (TB_CAP_ARROW_DOWN)
- "\033[D", // kcub1 (TB_CAP_ARROW_LEFT)
- "\033[C", // kcuf1 (TB_CAP_ARROW_RIGHT)
- "", // kcbt (TB_CAP_BACK_TAB)
- "\0337\033[?47h", // smcup (TB_CAP_ENTER_CA)
- "\033[2J\033[?47l\0338", // rmcup (TB_CAP_EXIT_CA)
- "\033[?25h", // cnorm (TB_CAP_SHOW_CURSOR)
- "\033[?25l", // civis (TB_CAP_HIDE_CURSOR)
- "\033[H\033[2J", // clear (TB_CAP_CLEAR_SCREEN)
- "\033[m\017", // sgr0 (TB_CAP_SGR0)
- "\033[4m", // smul (TB_CAP_UNDERLINE)
- "\033[1m", // bold (TB_CAP_BOLD)
- "\033[5m", // blink (TB_CAP_BLINK)
- "", // sitm (TB_CAP_ITALIC)
- "\033[7m", // rev (TB_CAP_REVERSE)
- "", // smkx (TB_CAP_ENTER_KEYPAD)
- "", // rmkx (TB_CAP_EXIT_KEYPAD)
- "", // dim (TB_CAP_DIM)
- "", // invis (TB_CAP_INVISIBLE)
-};
-
-static struct {
- const char *name;
- const char **caps;
- const char *alias;
-} builtin_terms[] = {
- {"xterm", xterm_caps, "" },
- {"linux", linux_caps, "" },
- {"screen", screen_caps, "tmux"},
- {"rxvt-256color", rxvt_256color_caps, "" },
- {"rxvt-unicode", rxvt_unicode_caps, "rxvt"},
- {"Eterm", eterm_caps, "" },
- {NULL, NULL, NULL },
-};
-
-/* END codegen c */
-
-static struct {
- const char *cap;
- const uint16_t key;
- const uint8_t mod;
-} builtin_mod_caps[] = {
- // xterm arrows
- {"\x1b[1;2A", TB_KEY_ARROW_UP, TB_MOD_SHIFT },
- {"\x1b[1;3A", TB_KEY_ARROW_UP, TB_MOD_ALT },
- {"\x1b[1;4A", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5A", TB_KEY_ARROW_UP, TB_MOD_CTRL },
- {"\x1b[1;6A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8A", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },
- {"\x1b[1;3B", TB_KEY_ARROW_DOWN, TB_MOD_ALT },
- {"\x1b[1;4B", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },
- {"\x1b[1;6B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8B", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },
- {"\x1b[1;3C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT },
- {"\x1b[1;4C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },
- {"\x1b[1;6C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8C", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },
- {"\x1b[1;3D", TB_KEY_ARROW_LEFT, TB_MOD_ALT },
- {"\x1b[1;4D", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },
- {"\x1b[1;6D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8D", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- // xterm keys
- {"\x1b[1;2H", TB_KEY_HOME, TB_MOD_SHIFT },
- {"\x1b[1;3H", TB_KEY_HOME, TB_MOD_ALT },
- {"\x1b[1;4H", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5H", TB_KEY_HOME, TB_MOD_CTRL },
- {"\x1b[1;6H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8H", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2F", TB_KEY_END, TB_MOD_SHIFT },
- {"\x1b[1;3F", TB_KEY_END, TB_MOD_ALT },
- {"\x1b[1;4F", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5F", TB_KEY_END, TB_MOD_CTRL },
- {"\x1b[1;6F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8F", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[2;2~", TB_KEY_INSERT, TB_MOD_SHIFT },
- {"\x1b[2;3~", TB_KEY_INSERT, TB_MOD_ALT },
- {"\x1b[2;4~", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[2;5~", TB_KEY_INSERT, TB_MOD_CTRL },
- {"\x1b[2;6~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[2;7~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[2;8~", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[3;2~", TB_KEY_DELETE, TB_MOD_SHIFT },
- {"\x1b[3;3~", TB_KEY_DELETE, TB_MOD_ALT },
- {"\x1b[3;4~", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[3;5~", TB_KEY_DELETE, TB_MOD_CTRL },
- {"\x1b[3;6~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[3;7~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[3;8~", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[5;2~", TB_KEY_PGUP, TB_MOD_SHIFT },
- {"\x1b[5;3~", TB_KEY_PGUP, TB_MOD_ALT },
- {"\x1b[5;4~", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[5;5~", TB_KEY_PGUP, TB_MOD_CTRL },
- {"\x1b[5;6~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[5;7~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[5;8~", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[6;2~", TB_KEY_PGDN, TB_MOD_SHIFT },
- {"\x1b[6;3~", TB_KEY_PGDN, TB_MOD_ALT },
- {"\x1b[6;4~", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[6;5~", TB_KEY_PGDN, TB_MOD_CTRL },
- {"\x1b[6;6~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[6;7~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[6;8~", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2P", TB_KEY_F1, TB_MOD_SHIFT },
- {"\x1b[1;3P", TB_KEY_F1, TB_MOD_ALT },
- {"\x1b[1;4P", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5P", TB_KEY_F1, TB_MOD_CTRL },
- {"\x1b[1;6P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8P", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2Q", TB_KEY_F2, TB_MOD_SHIFT },
- {"\x1b[1;3Q", TB_KEY_F2, TB_MOD_ALT },
- {"\x1b[1;4Q", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5Q", TB_KEY_F2, TB_MOD_CTRL },
- {"\x1b[1;6Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8Q", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2R", TB_KEY_F3, TB_MOD_SHIFT },
- {"\x1b[1;3R", TB_KEY_F3, TB_MOD_ALT },
- {"\x1b[1;4R", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5R", TB_KEY_F3, TB_MOD_CTRL },
- {"\x1b[1;6R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8R", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[1;2S", TB_KEY_F4, TB_MOD_SHIFT },
- {"\x1b[1;3S", TB_KEY_F4, TB_MOD_ALT },
- {"\x1b[1;4S", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[1;5S", TB_KEY_F4, TB_MOD_CTRL },
- {"\x1b[1;6S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[1;7S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[1;8S", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[15;2~", TB_KEY_F5, TB_MOD_SHIFT },
- {"\x1b[15;3~", TB_KEY_F5, TB_MOD_ALT },
- {"\x1b[15;4~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[15;5~", TB_KEY_F5, TB_MOD_CTRL },
- {"\x1b[15;6~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[15;7~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[15;8~", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[17;2~", TB_KEY_F6, TB_MOD_SHIFT },
- {"\x1b[17;3~", TB_KEY_F6, TB_MOD_ALT },
- {"\x1b[17;4~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[17;5~", TB_KEY_F6, TB_MOD_CTRL },
- {"\x1b[17;6~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[17;7~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[17;8~", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[18;2~", TB_KEY_F7, TB_MOD_SHIFT },
- {"\x1b[18;3~", TB_KEY_F7, TB_MOD_ALT },
- {"\x1b[18;4~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[18;5~", TB_KEY_F7, TB_MOD_CTRL },
- {"\x1b[18;6~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[18;7~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[18;8~", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[19;2~", TB_KEY_F8, TB_MOD_SHIFT },
- {"\x1b[19;3~", TB_KEY_F8, TB_MOD_ALT },
- {"\x1b[19;4~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[19;5~", TB_KEY_F8, TB_MOD_CTRL },
- {"\x1b[19;6~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[19;7~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[19;8~", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[20;2~", TB_KEY_F9, TB_MOD_SHIFT },
- {"\x1b[20;3~", TB_KEY_F9, TB_MOD_ALT },
- {"\x1b[20;4~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[20;5~", TB_KEY_F9, TB_MOD_CTRL },
- {"\x1b[20;6~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[20;7~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[20;8~", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[21;2~", TB_KEY_F10, TB_MOD_SHIFT },
- {"\x1b[21;3~", TB_KEY_F10, TB_MOD_ALT },
- {"\x1b[21;4~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[21;5~", TB_KEY_F10, TB_MOD_CTRL },
- {"\x1b[21;6~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[21;7~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[21;8~", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[23;2~", TB_KEY_F11, TB_MOD_SHIFT },
- {"\x1b[23;3~", TB_KEY_F11, TB_MOD_ALT },
- {"\x1b[23;4~", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[23;5~", TB_KEY_F11, TB_MOD_CTRL },
- {"\x1b[23;6~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[23;7~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[23;8~", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b[24;2~", TB_KEY_F12, TB_MOD_SHIFT },
- {"\x1b[24;3~", TB_KEY_F12, TB_MOD_ALT },
- {"\x1b[24;4~", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[24;5~", TB_KEY_F12, TB_MOD_CTRL },
- {"\x1b[24;6~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[24;7~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b[24;8~", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- // rxvt arrows
- {"\x1b[a", TB_KEY_ARROW_UP, TB_MOD_SHIFT },
- {"\x1b\x1b[A", TB_KEY_ARROW_UP, TB_MOD_ALT },
- {"\x1b\x1b[a", TB_KEY_ARROW_UP, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL },
- {"\x1b\x1bOa", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },
-
- {"\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },
- {"\x1b\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_ALT },
- {"\x1b\x1b[b", TB_KEY_ARROW_DOWN, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },
- {"\x1b\x1bOb", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },
-
- {"\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },
- {"\x1b\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_ALT },
- {"\x1b\x1b[c", TB_KEY_ARROW_RIGHT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },
- {"\x1b\x1bOc", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },
-
- {"\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },
- {"\x1b\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_ALT },
- {"\x1b\x1b[d", TB_KEY_ARROW_LEFT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },
- {"\x1b\x1bOd", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },
-
- // rxvt keys
- {"\x1b[7$", TB_KEY_HOME, TB_MOD_SHIFT },
- {"\x1b\x1b[7~", TB_KEY_HOME, TB_MOD_ALT },
- {"\x1b\x1b[7$", TB_KEY_HOME, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL },
- {"\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b\x1b[7^", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[7@", TB_KEY_HOME, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
-
- {"\x1b\x1b[8~", TB_KEY_END, TB_MOD_ALT },
- {"\x1b\x1b[8$", TB_KEY_END, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[8^", TB_KEY_END, TB_MOD_CTRL },
- {"\x1b\x1b[8^", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[8@", TB_KEY_END, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[8$", TB_KEY_END, TB_MOD_SHIFT },
-
- {"\x1b\x1b[2~", TB_KEY_INSERT, TB_MOD_ALT },
- {"\x1b\x1b[2$", TB_KEY_INSERT, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL },
- {"\x1b\x1b[2^", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[2@", TB_KEY_INSERT, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[2$", TB_KEY_INSERT, TB_MOD_SHIFT },
-
- {"\x1b\x1b[3~", TB_KEY_DELETE, TB_MOD_ALT },
- {"\x1b\x1b[3$", TB_KEY_DELETE, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL },
- {"\x1b\x1b[3^", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[3@", TB_KEY_DELETE, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[3$", TB_KEY_DELETE, TB_MOD_SHIFT },
-
- {"\x1b\x1b[5~", TB_KEY_PGUP, TB_MOD_ALT },
- {"\x1b\x1b[5$", TB_KEY_PGUP, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL },
- {"\x1b\x1b[5^", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[5@", TB_KEY_PGUP, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[5$", TB_KEY_PGUP, TB_MOD_SHIFT },
-
- {"\x1b\x1b[6~", TB_KEY_PGDN, TB_MOD_ALT },
- {"\x1b\x1b[6$", TB_KEY_PGDN, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL },
- {"\x1b\x1b[6^", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[6@", TB_KEY_PGDN, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[6$", TB_KEY_PGDN, TB_MOD_SHIFT },
-
- {"\x1b\x1b[11~", TB_KEY_F1, TB_MOD_ALT },
- {"\x1b\x1b[23~", TB_KEY_F1, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[11^", TB_KEY_F1, TB_MOD_CTRL },
- {"\x1b\x1b[11^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[23^", TB_KEY_F1, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[23~", TB_KEY_F1, TB_MOD_SHIFT },
-
- {"\x1b\x1b[12~", TB_KEY_F2, TB_MOD_ALT },
- {"\x1b\x1b[24~", TB_KEY_F2, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[12^", TB_KEY_F2, TB_MOD_CTRL },
- {"\x1b\x1b[12^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[24^", TB_KEY_F2, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[24~", TB_KEY_F2, TB_MOD_SHIFT },
-
- {"\x1b\x1b[13~", TB_KEY_F3, TB_MOD_ALT },
- {"\x1b\x1b[25~", TB_KEY_F3, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[13^", TB_KEY_F3, TB_MOD_CTRL },
- {"\x1b\x1b[13^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[25^", TB_KEY_F3, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[25~", TB_KEY_F3, TB_MOD_SHIFT },
-
- {"\x1b\x1b[14~", TB_KEY_F4, TB_MOD_ALT },
- {"\x1b\x1b[26~", TB_KEY_F4, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[14^", TB_KEY_F4, TB_MOD_CTRL },
- {"\x1b\x1b[14^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[26^", TB_KEY_F4, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[26~", TB_KEY_F4, TB_MOD_SHIFT },
-
- {"\x1b\x1b[15~", TB_KEY_F5, TB_MOD_ALT },
- {"\x1b\x1b[28~", TB_KEY_F5, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[15^", TB_KEY_F5, TB_MOD_CTRL },
- {"\x1b\x1b[15^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[28^", TB_KEY_F5, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[28~", TB_KEY_F5, TB_MOD_SHIFT },
-
- {"\x1b\x1b[17~", TB_KEY_F6, TB_MOD_ALT },
- {"\x1b\x1b[29~", TB_KEY_F6, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[17^", TB_KEY_F6, TB_MOD_CTRL },
- {"\x1b\x1b[17^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[29^", TB_KEY_F6, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[29~", TB_KEY_F6, TB_MOD_SHIFT },
-
- {"\x1b\x1b[18~", TB_KEY_F7, TB_MOD_ALT },
- {"\x1b\x1b[31~", TB_KEY_F7, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[18^", TB_KEY_F7, TB_MOD_CTRL },
- {"\x1b\x1b[18^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[31^", TB_KEY_F7, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[31~", TB_KEY_F7, TB_MOD_SHIFT },
-
- {"\x1b\x1b[19~", TB_KEY_F8, TB_MOD_ALT },
- {"\x1b\x1b[32~", TB_KEY_F8, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[19^", TB_KEY_F8, TB_MOD_CTRL },
- {"\x1b\x1b[19^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[32^", TB_KEY_F8, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[32~", TB_KEY_F8, TB_MOD_SHIFT },
-
- {"\x1b\x1b[20~", TB_KEY_F9, TB_MOD_ALT },
- {"\x1b\x1b[33~", TB_KEY_F9, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[20^", TB_KEY_F9, TB_MOD_CTRL },
- {"\x1b\x1b[20^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[33^", TB_KEY_F9, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[33~", TB_KEY_F9, TB_MOD_SHIFT },
-
- {"\x1b\x1b[21~", TB_KEY_F10, TB_MOD_ALT },
- {"\x1b\x1b[34~", TB_KEY_F10, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[21^", TB_KEY_F10, TB_MOD_CTRL },
- {"\x1b\x1b[21^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[34^", TB_KEY_F10, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[34~", TB_KEY_F10, TB_MOD_SHIFT },
-
- {"\x1b\x1b[23~", TB_KEY_F11, TB_MOD_ALT },
- {"\x1b\x1b[23$", TB_KEY_F11, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[23^", TB_KEY_F11, TB_MOD_CTRL },
- {"\x1b\x1b[23^", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[23@", TB_KEY_F11, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[23$", TB_KEY_F11, TB_MOD_SHIFT },
-
- {"\x1b\x1b[24~", TB_KEY_F12, TB_MOD_ALT },
- {"\x1b\x1b[24$", TB_KEY_F12, TB_MOD_ALT | TB_MOD_SHIFT },
- {"\x1b[24^", TB_KEY_F12, TB_MOD_CTRL },
- {"\x1b\x1b[24^", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1b\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_ALT | TB_MOD_SHIFT},
- {"\x1b[24@", TB_KEY_F12, TB_MOD_CTRL | TB_MOD_SHIFT },
- {"\x1b[24$", TB_KEY_F12, TB_MOD_SHIFT },
-
- // linux console/putty arrows
- {"\x1b[A", TB_KEY_ARROW_UP, TB_MOD_SHIFT },
- {"\x1b[B", TB_KEY_ARROW_DOWN, TB_MOD_SHIFT },
- {"\x1b[C", TB_KEY_ARROW_RIGHT, TB_MOD_SHIFT },
- {"\x1b[D", TB_KEY_ARROW_LEFT, TB_MOD_SHIFT },
-
- // more putty arrows
- {"\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL },
- {"\x1b\x1bOA", TB_KEY_ARROW_UP, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL },
- {"\x1b\x1bOB", TB_KEY_ARROW_DOWN, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL },
- {"\x1b\x1bOC", TB_KEY_ARROW_RIGHT, TB_MOD_CTRL | TB_MOD_ALT },
- {"\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL },
- {"\x1b\x1bOD", TB_KEY_ARROW_LEFT, TB_MOD_CTRL | TB_MOD_ALT },
-
- {NULL, 0, 0 },
-};
-
-static const unsigned char utf8_length[256] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
- 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
- 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
- 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 1, 1};
-
-static const unsigned char utf8_mask[6] = {0x7f, 0x1f, 0x0f, 0x07, 0x03, 0x01};
-
-static int tb_reset(void);
-static int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg,
- size_t *out_w, const char *fmt, va_list vl);
-static int init_term_attrs(void);
-static int init_term_caps(void);
-static int init_cap_trie(void);
-static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod);
-static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
- size_t *depth);
-static int cap_trie_deinit(struct cap_trie_t *node);
-static int init_resize_handler(void);
-static int send_init_escape_codes(void);
-static int send_clear(void);
-static int update_term_size(void);
-static int update_term_size_via_esc(void);
-static int init_cellbuf(void);
-static int tb_deinit(void);
-static int load_terminfo(void);
-static int load_terminfo_from_path(const char *path, const char *term);
-static int read_terminfo_path(const char *path);
-static int parse_terminfo_caps(void);
-static int load_builtin_caps(void);
-static const char *get_terminfo_string(int16_t str_offsets_pos,
- int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len,
- int16_t str_index);
-static int wait_event(struct tb_event *event, int timeout);
-static int extract_event(struct tb_event *event);
-static int extract_esc(struct tb_event *event);
-static int extract_esc_user(struct tb_event *event, int is_post);
-static int extract_esc_cap(struct tb_event *event);
-static int extract_esc_mouse(struct tb_event *event);
-static int resize_cellbufs(void);
-static void handle_resize(int sig);
-static int send_attr(uintattr_t fg, uintattr_t bg);
-static int send_sgr(uint32_t fg, uint32_t bg, int fg_is_default,
- int bg_is_default);
-static int send_cursor_if(int x, int y);
-static int send_char(int x, int y, uint32_t ch);
-static int send_cluster(int x, int y, uint32_t *ch, size_t nch);
-static int convert_num(uint32_t num, char *buf);
-static int cell_cmp(struct tb_cell *a, struct tb_cell *b);
-static int cell_copy(struct tb_cell *dst, struct tb_cell *src);
-static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
- uintattr_t fg, uintattr_t bg);
-static int cell_reserve_ech(struct tb_cell *cell, size_t n);
-static int cell_free(struct tb_cell *cell);
-static int cellbuf_init(struct cellbuf_t *c, int w, int h);
-static int cellbuf_free(struct cellbuf_t *c);
-static int cellbuf_clear(struct cellbuf_t *c);
-static int cellbuf_get(struct cellbuf_t *c, int x, int y, struct tb_cell **out);
-static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y);
-static int cellbuf_resize(struct cellbuf_t *c, int w, int h);
-static int bytebuf_puts(struct bytebuf_t *b, const char *str);
-static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr);
-static int bytebuf_shift(struct bytebuf_t *b, size_t n);
-static int bytebuf_flush(struct bytebuf_t *b, int fd);
-static int bytebuf_reserve(struct bytebuf_t *b, size_t sz);
-static int bytebuf_free(struct bytebuf_t *b);
-
-int tb_init(void) {
- return tb_init_file("/dev/tty");
-}
-
-int tb_init_file(const char *path) {
- if (global.initialized) return TB_ERR_INIT_ALREADY;
- int ttyfd = open(path, O_RDWR);
- if (ttyfd < 0) {
- global.last_errno = errno;
- return TB_ERR_INIT_OPEN;
- }
- global.ttyfd_open = 1;
- return tb_init_fd(ttyfd);
-}
-
-int tb_init_fd(int ttyfd) {
- return tb_init_rwfd(ttyfd, ttyfd);
-}
-
-int tb_init_rwfd(int rfd, int wfd) {
- int rv;
-
- tb_reset();
- global.ttyfd = rfd == wfd && isatty(rfd) ? rfd : -1;
- global.rfd = rfd;
- global.wfd = wfd;
-
- do {
- if_err_break(rv, init_term_attrs());
- if_err_break(rv, init_term_caps());
- if_err_break(rv, init_cap_trie());
- if_err_break(rv, init_resize_handler());
- if_err_break(rv, send_init_escape_codes());
- if_err_break(rv, send_clear());
- if_err_break(rv, update_term_size());
- if_err_break(rv, init_cellbuf());
- global.initialized = 1;
- } while (0);
-
- if (rv != TB_OK) {
- tb_deinit();
- }
-
- return rv;
-}
-
-int tb_shutdown(void) {
- if_not_init_return();
- tb_deinit();
- return TB_OK;
-}
-
-int tb_width(void) {
- if_not_init_return();
- return global.width;
-}
-
-int tb_height(void) {
- if_not_init_return();
- return global.height;
-}
-
-int tb_clear(void) {
- if_not_init_return();
- return cellbuf_clear(&global.back);
-}
-
-int tb_set_clear_attrs(uintattr_t fg, uintattr_t bg) {
- if_not_init_return();
- global.fg = fg;
- global.bg = bg;
- return TB_OK;
-}
-
-int tb_present(void) {
- if_not_init_return();
-
- int rv;
-
- // TODO: Assert global.back.(width,height) == global.front.(width,height)
-
- global.last_x = -1;
- global.last_y = -1;
-
- int x, y, i;
- for (y = 0; y < global.front.height; y++) {
- for (x = 0; x < global.front.width;) {
- struct tb_cell *back, *front;
- if_err_return(rv, cellbuf_get(&global.back, x, y, &back));
- if_err_return(rv, cellbuf_get(&global.front, x, y, &front));
-
- int w;
- {
-#ifdef TB_OPT_EGC
- if (back->nech > 0)
- w = wcswidth((wchar_t *)back->ech, back->nech);
- else
-#endif
- // wcwidth simply returns -1 on overflow of wchar_t
- w = wcwidth((wchar_t)back->ch);
- }
- if (w < 1) w = 1;
-
- if (cell_cmp(back, front) != 0) {
- cell_copy(front, back);
-
- send_attr(back->fg, back->bg);
- if (w > 1 && x >= global.front.width - (w - 1)) {
- // Not enough room for wide char, send spaces
- for (i = x; i < global.front.width; i++) {
- send_char(i, y, ' ');
- }
- } else {
- {
-#ifdef TB_OPT_EGC
- if (back->nech > 0)
- send_cluster(x, y, back->ech, back->nech);
- else
-#endif
- send_char(x, y, back->ch);
- }
-
- // When wcwidth>1, we need to advance the cursor by more
- // than 1, thereby skipping some cells. Set these skipped
- // cells to an invalid codepoint in the front buffer, so
- // that if this cell is later replaced by a wcwidth==1 char,
- // we'll get a cell_cmp diff for the skipped cells and
- // properly re-render.
- for (i = 1; i < w; i++) {
- struct tb_cell *front_wide;
- uint32_t invalid = -1;
- if_err_return(rv,
- cellbuf_get(&global.front, x + i, y, &front_wide));
- if_err_return(rv,
- cell_set(front_wide, &invalid, 1, -1, -1));
- }
- }
- }
- x += w;
- }
- }
-
- if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
- if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
-
- return TB_OK;
-}
-
-int tb_invalidate(void) {
- int rv;
- if_not_init_return();
- if_err_return(rv, resize_cellbufs());
- return TB_OK;
-}
-
-int tb_set_cursor(int cx, int cy) {
- if_not_init_return();
- int rv;
- if (cx < 0) cx = 0;
- if (cy < 0) cy = 0;
- if (global.cursor_x == -1) {
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]));
- }
- if_err_return(rv, send_cursor_if(cx, cy));
- global.cursor_x = cx;
- global.cursor_y = cy;
- return TB_OK;
-}
-
-int tb_hide_cursor(void) {
- if_not_init_return();
- int rv;
- if (global.cursor_x >= 0) {
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
- }
- global.cursor_x = -1;
- global.cursor_y = -1;
- return TB_OK;
-}
-
-int tb_set_cell(int x, int y, uint32_t ch, uintattr_t fg, uintattr_t bg) {
- return tb_set_cell_ex(x, y, &ch, 1, fg, bg);
-}
-
-int tb_set_cell_ex(int x, int y, uint32_t *ch, size_t nch, uintattr_t fg,
- uintattr_t bg) {
- if_not_init_return();
- int rv;
- struct tb_cell *cell;
- if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
- if_err_return(rv, cell_set(cell, ch, nch, fg, bg));
- return TB_OK;
-}
-
-int tb_extend_cell(int x, int y, uint32_t ch) {
- if_not_init_return();
-#ifdef TB_OPT_EGC
- int rv;
- struct tb_cell *cell;
- size_t nech;
- if_err_return(rv, cellbuf_get(&global.back, x, y, &cell));
- if (cell->nech > 0) { // append to ech
- nech = cell->nech + 1;
- if_err_return(rv, cell_reserve_ech(cell, nech));
- cell->ech[nech - 1] = ch;
- } else { // make new ech
- nech = 2;
- if_err_return(rv, cell_reserve_ech(cell, nech));
- cell->ech[0] = cell->ch;
- cell->ech[1] = ch;
- }
- cell->ech[nech] = '\0';
- cell->nech = nech;
- return TB_OK;
-#else
- (void)x;
- (void)y;
- (void)ch;
- return TB_ERR;
-#endif
-}
-
-int tb_set_input_mode(int mode) {
- if_not_init_return();
- if (mode == TB_INPUT_CURRENT) {
- return global.input_mode;
- }
-
- if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == 0) {
- mode |= TB_INPUT_ESC;
- }
-
- if ((mode & (TB_INPUT_ESC | TB_INPUT_ALT)) == (TB_INPUT_ESC | TB_INPUT_ALT))
- {
- mode &= ~TB_INPUT_ALT;
- }
-
- if (mode & TB_INPUT_MOUSE) {
- bytebuf_puts(&global.out, TB_HARDCAP_ENTER_MOUSE);
- bytebuf_flush(&global.out, global.wfd);
- } else {
- bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
- bytebuf_flush(&global.out, global.wfd);
- }
-
- global.input_mode = mode;
- return TB_OK;
-}
-
-int tb_set_output_mode(int mode) {
- if_not_init_return();
- switch (mode) {
- case TB_OUTPUT_CURRENT:
- return global.output_mode;
- case TB_OUTPUT_NORMAL:
- case TB_OUTPUT_256:
- case TB_OUTPUT_216:
- case TB_OUTPUT_GRAYSCALE:
-#if TB_OPT_ATTR_W >= 32
- case TB_OUTPUT_TRUECOLOR:
-#endif
- global.last_fg = ~global.fg;
- global.last_bg = ~global.bg;
- global.output_mode = mode;
- return TB_OK;
- }
- return TB_ERR;
-}
-
-int tb_peek_event(struct tb_event *event, int timeout_ms) {
- if_not_init_return();
- return wait_event(event, timeout_ms);
-}
-
-int tb_poll_event(struct tb_event *event) {
- if_not_init_return();
- return wait_event(event, -1);
-}
-
-int tb_get_fds(int *ttyfd, int *resizefd) {
- if_not_init_return();
-
- *ttyfd = global.rfd;
- *resizefd = global.resize_pipefd[0];
-
- return TB_OK;
-}
-
-int tb_print(int x, int y, uintattr_t fg, uintattr_t bg, const char *str) {
- return tb_print_ex(x, y, fg, bg, NULL, str);
-}
-
-int tb_print_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
- const char *str) {
- int rv, w, ix;
- uint32_t uni;
-
- if_not_init_return();
-
- if (!cellbuf_in_bounds(&global.back, x, y)) {
- return TB_ERR_OUT_OF_BOUNDS;
- }
-
- ix = x;
- if (out_w) *out_w = 0;
-
- while (*str) {
- rv = tb_utf8_char_to_unicode(&uni, str);
-
- if (rv < 0) {
- uni = 0xfffd; // replace invalid UTF-8 char with U+FFFD
- str += rv * -1;
- } else if (rv > 0) {
- str += rv;
- } else {
- break; // shouldn't get here
- }
-
- if (uni == '\n') { // TODO: \r, \t, \v, \f, etc?
- x = ix;
- y += 1;
- continue;
- } else if (!iswprint((wint_t)uni)) {
- uni = 0xfffd; // replace non-printable with U+FFFD
- }
-
- w = wcwidth((wchar_t)uni);
- if (w < 0) {
- return TB_ERR; // shouldn't happen if iswprint
- } else if (w == 0) { // combining character
- if (cellbuf_in_bounds(&global.back, x - 1, y)) {
- if_err_return(rv, tb_extend_cell(x - 1, y, uni));
- }
- } else {
- if (cellbuf_in_bounds(&global.back, x, y)) {
- if_err_return(rv, tb_set_cell(x, y, uni, fg, bg));
- }
- }
-
- x += w;
- if (out_w) *out_w += w;
- }
-
- return TB_OK;
-}
-
-int tb_printf(int x, int y, uintattr_t fg, uintattr_t bg, const char *fmt,
- ...) {
- int rv;
- va_list vl;
- va_start(vl, fmt);
- rv = tb_printf_inner(x, y, fg, bg, NULL, fmt, vl);
- va_end(vl);
- return rv;
-}
-
-int tb_printf_ex(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
- const char *fmt, ...) {
- int rv;
- va_list vl;
- va_start(vl, fmt);
- rv = tb_printf_inner(x, y, fg, bg, out_w, fmt, vl);
- va_end(vl);
- return rv;
-}
-
-int tb_send(const char *buf, size_t nbuf) {
- return bytebuf_nputs(&global.out, buf, nbuf);
-}
-
-int tb_sendf(const char *fmt, ...) {
- int rv;
- char buf[TB_OPT_PRINTF_BUF];
- va_list vl;
- va_start(vl, fmt);
- rv = vsnprintf(buf, sizeof(buf), fmt, vl);
- va_end(vl);
- if (rv < 0 || rv >= (int)sizeof(buf)) {
- return TB_ERR;
- }
- return tb_send(buf, (size_t)rv);
-}
-
-int tb_set_func(int fn_type, int (*fn)(struct tb_event *, size_t *)) {
- switch (fn_type) {
- case TB_FUNC_EXTRACT_PRE:
- global.fn_extract_esc_pre = fn;
- return TB_OK;
- case TB_FUNC_EXTRACT_POST:
- global.fn_extract_esc_post = fn;
- return TB_OK;
- }
- return TB_ERR;
-}
-
-struct tb_cell *tb_cell_buffer(void) {
- if (!global.initialized) return NULL;
- return global.back.cells;
-}
-
-int tb_utf8_char_length(char c) {
- return utf8_length[(unsigned char)c];
-}
-
-int tb_utf8_char_to_unicode(uint32_t *out, const char *c) {
- if (*c == '\0') return 0;
-
- int i;
- unsigned char len = tb_utf8_char_length(*c);
- unsigned char mask = utf8_mask[len - 1];
- uint32_t result = c[0] & mask;
- for (i = 1; i < len && c[i] != '\0'; ++i) {
- result <<= 6;
- result |= c[i] & 0x3f;
- }
-
- if (i != len) return i * -1;
-
- *out = result;
- return (int)len;
-}
-
-int tb_utf8_unicode_to_char(char *out, uint32_t c) {
- int len = 0;
- int first;
- int i;
-
- if (c < 0x80) {
- first = 0;
- len = 1;
- } else if (c < 0x800) {
- first = 0xc0;
- len = 2;
- } else if (c < 0x10000) {
- first = 0xe0;
- len = 3;
- } else if (c < 0x200000) {
- first = 0xf0;
- len = 4;
- } else if (c < 0x4000000) {
- first = 0xf8;
- len = 5;
- } else {
- first = 0xfc;
- len = 6;
- }
-
- for (i = len - 1; i > 0; --i) {
- out[i] = (c & 0x3f) | 0x80;
- c >>= 6;
- }
- out[0] = c | first;
- out[len] = '\0';
-
- return len;
-}
-
-int tb_last_errno(void) {
- return global.last_errno;
-}
-
-const char *tb_strerror(int err) {
- switch (err) {
- case TB_OK:
- return "Success";
- case TB_ERR_NEED_MORE:
- return "Not enough input";
- case TB_ERR_INIT_ALREADY:
- return "Termbox initialized already";
- case TB_ERR_MEM:
- return "Out of memory";
- case TB_ERR_NO_EVENT:
- return "No event";
- case TB_ERR_NO_TERM:
- return "No TERM in environment";
- case TB_ERR_NOT_INIT:
- return "Termbox not initialized";
- case TB_ERR_OUT_OF_BOUNDS:
- return "Out of bounds";
- case TB_ERR_UNSUPPORTED_TERM:
- return "Unsupported terminal";
- case TB_ERR_CAP_COLLISION:
- return "Termcaps collision";
- case TB_ERR_RESIZE_SSCANF:
- return "Terminal width/height not received by sscanf() after "
- "resize";
- case TB_ERR:
- case TB_ERR_INIT_OPEN:
- case TB_ERR_READ:
- case TB_ERR_RESIZE_IOCTL:
- case TB_ERR_RESIZE_PIPE:
- case TB_ERR_RESIZE_SIGACTION:
- case TB_ERR_POLL:
- case TB_ERR_TCGETATTR:
- case TB_ERR_TCSETATTR:
- case TB_ERR_RESIZE_WRITE:
- case TB_ERR_RESIZE_POLL:
- case TB_ERR_RESIZE_READ:
- default:
- strerror_r(global.last_errno, global.errbuf, sizeof(global.errbuf));
- return (const char *)global.errbuf;
- }
-}
-
-int tb_has_truecolor(void) {
-#if TB_OPT_ATTR_W >= 32
- return 1;
-#else
- return 0;
-#endif
-}
-
-int tb_has_egc(void) {
-#ifdef TB_OPT_EGC
- return 1;
-#else
- return 0;
-#endif
-}
-
-int tb_attr_width(void) {
- return TB_OPT_ATTR_W;
-}
-
-const char *tb_version(void) {
- return TB_VERSION_STR;
-}
-
-static int tb_reset(void) {
- int ttyfd_open = global.ttyfd_open;
- memset(&global, 0, sizeof(global));
- global.ttyfd = -1;
- global.rfd = -1;
- global.wfd = -1;
- global.ttyfd_open = ttyfd_open;
- global.resize_pipefd[0] = -1;
- global.resize_pipefd[1] = -1;
- global.width = -1;
- global.height = -1;
- global.cursor_x = -1;
- global.cursor_y = -1;
- global.last_x = -1;
- global.last_y = -1;
- global.fg = TB_DEFAULT;
- global.bg = TB_DEFAULT;
- global.last_fg = ~global.fg;
- global.last_bg = ~global.bg;
- global.input_mode = TB_INPUT_ESC;
- global.output_mode = TB_OUTPUT_NORMAL;
- return TB_OK;
-}
-
-static int init_term_attrs(void) {
- if (global.ttyfd < 0) {
- return TB_OK;
- }
-
- if (tcgetattr(global.ttyfd, &global.orig_tios) != 0) {
- global.last_errno = errno;
- return TB_ERR_TCGETATTR;
- }
-
- struct termios tios;
- memcpy(&tios, &global.orig_tios, sizeof(tios));
- global.has_orig_tios = 1;
-
- cfmakeraw(&tios);
- tios.c_cc[VMIN] = 1;
- tios.c_cc[VTIME] = 0;
-
- if (tcsetattr(global.ttyfd, TCSAFLUSH, &tios) != 0) {
- global.last_errno = errno;
- return TB_ERR_TCSETATTR;
- }
-
- return TB_OK;
-}
-
-int tb_printf_inner(int x, int y, uintattr_t fg, uintattr_t bg, size_t *out_w,
- const char *fmt, va_list vl) {
- int rv;
- char buf[TB_OPT_PRINTF_BUF];
- rv = vsnprintf(buf, sizeof(buf), fmt, vl);
- if (rv < 0 || rv >= (int)sizeof(buf)) {
- return TB_ERR;
- }
- return tb_print_ex(x, y, fg, bg, out_w, buf);
-}
-
-static int init_term_caps(void) {
- if (load_terminfo() == TB_OK) {
- return parse_terminfo_caps();
- }
- return load_builtin_caps();
-}
-
-static int init_cap_trie(void) {
- int rv, i;
-
- // Add caps from terminfo or built-in
- //
- // Collisions are expected as some terminfo entries have dupes. (For
- // example, att605-pc collides on TB_CAP_F4 and TB_CAP_DELETE.) First cap
- // in TB_CAP_* index order will win.
- //
- // TODO: Reorder TB_CAP_* so more critical caps come first.
- for (i = 0; i < TB_CAP__COUNT_KEYS; i++) {
- rv = cap_trie_add(global.caps[i], tb_key_i(i), 0);
- if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
- }
-
- // Add built-in mod caps
- //
- // Collisions are OK here as well. This can happen if global.caps collides
- // with builtin_mod_caps. It is desirable to give precedence to global.caps
- // here.
- for (i = 0; builtin_mod_caps[i].cap != NULL; i++) {
- rv = cap_trie_add(builtin_mod_caps[i].cap, builtin_mod_caps[i].key,
- builtin_mod_caps[i].mod);
- if (rv != TB_OK && rv != TB_ERR_CAP_COLLISION) return rv;
- }
-
- return TB_OK;
-}
-
-static int cap_trie_add(const char *cap, uint16_t key, uint8_t mod) {
- struct cap_trie_t *next, *node = &global.cap_trie;
- size_t i, j;
-
- if (!cap || strlen(cap) <= 0) return TB_OK; // Nothing to do for empty caps
-
- for (i = 0; cap[i] != '\0'; i++) {
- char c = cap[i];
- next = NULL;
-
- // Check if c is already a child of node
- for (j = 0; j < node->nchildren; j++) {
- if (node->children[j].c == c) {
- next = &node->children[j];
- break;
- }
- }
- if (!next) {
- // We need to add a new child to node
- node->nchildren += 1;
- node->children = (struct cap_trie_t *)tb_realloc(node->children,
- sizeof(*node) * node->nchildren);
- if (!node->children) {
- return TB_ERR_MEM;
- }
- next = &node->children[node->nchildren - 1];
- memset(next, 0, sizeof(*next));
- next->c = c;
- }
-
- // Continue
- node = next;
- }
-
- if (node->is_leaf) {
- // Already a leaf here
- return TB_ERR_CAP_COLLISION;
- }
-
- node->is_leaf = 1;
- node->key = key;
- node->mod = mod;
- return TB_OK;
-}
-
-static int cap_trie_find(const char *buf, size_t nbuf, struct cap_trie_t **last,
- size_t *depth) {
- struct cap_trie_t *next, *node = &global.cap_trie;
- size_t i, j;
- *last = node;
- *depth = 0;
- for (i = 0; i < nbuf; i++) {
- char c = buf[i];
- next = NULL;
-
- // Find c in node.children
- for (j = 0; j < node->nchildren; j++) {
- if (node->children[j].c == c) {
- next = &node->children[j];
- break;
- }
- }
- if (!next) {
- // Not found
- return TB_OK;
- }
- node = next;
- *last = node;
- *depth += 1;
- if (node->is_leaf && node->nchildren < 1) {
- break;
- }
- }
- return TB_OK;
-}
-
-static int cap_trie_deinit(struct cap_trie_t *node) {
- size_t j;
- for (j = 0; j < node->nchildren; j++) {
- cap_trie_deinit(&node->children[j]);
- }
- if (node->children) {
- tb_free(node->children);
- }
- memset(node, 0, sizeof(*node));
- return TB_OK;
-}
-
-static int init_resize_handler(void) {
- if (pipe(global.resize_pipefd) != 0) {
- global.last_errno = errno;
- return TB_ERR_RESIZE_PIPE;
- }
-
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = handle_resize;
- if (sigaction(SIGWINCH, &sa, NULL) != 0) {
- global.last_errno = errno;
- return TB_ERR_RESIZE_SIGACTION;
- }
-
- return TB_OK;
-}
-
-static int send_init_escape_codes(void) {
- int rv;
- if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_CA]));
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_ENTER_KEYPAD]));
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_HIDE_CURSOR]));
- return TB_OK;
-}
-
-static int send_clear(void) {
- int rv;
-
- if_err_return(rv, send_attr(global.fg, global.bg));
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]));
-
- if_err_return(rv, send_cursor_if(global.cursor_x, global.cursor_y));
- if_err_return(rv, bytebuf_flush(&global.out, global.wfd));
-
- global.last_x = -1;
- global.last_y = -1;
-
- return TB_OK;
-}
-
-static int update_term_size(void) {
- int rv, ioctl_errno;
-
- if (global.ttyfd < 0) {
- return TB_OK;
- }
-
- struct winsize sz;
- memset(&sz, 0, sizeof(sz));
-
- // Try ioctl TIOCGWINSZ
- if (ioctl(global.ttyfd, TIOCGWINSZ, &sz) == 0) {
- global.width = sz.ws_col;
- global.height = sz.ws_row;
- return TB_OK;
- }
- ioctl_errno = errno;
-
- // Try >cursor(9999,9999), >u7, <u6
- if_ok_return(rv, update_term_size_via_esc());
-
- global.last_errno = ioctl_errno;
- return TB_ERR_RESIZE_IOCTL;
-}
-
-static int update_term_size_via_esc(void) {
-#ifndef TB_RESIZE_FALLBACK_MS
-#define TB_RESIZE_FALLBACK_MS 1000
-#endif
-
- char move_and_report[] = "\x1b[9999;9999H\x1b[6n";
- ssize_t write_rv =
- write(global.wfd, move_and_report, strlen(move_and_report));
- if (write_rv != (ssize_t)strlen(move_and_report)) {
- return TB_ERR_RESIZE_WRITE;
- }
-
- fd_set fds;
- FD_ZERO(&fds);
- FD_SET(global.rfd, &fds);
-
- struct timeval timeout;
- timeout.tv_sec = 0;
- timeout.tv_usec = TB_RESIZE_FALLBACK_MS * 1000;
-
- int select_rv = select(global.rfd + 1, &fds, NULL, NULL, &timeout);
-
- if (select_rv != 1) {
- global.last_errno = errno;
- return TB_ERR_RESIZE_POLL;
- }
-
- char buf[TB_OPT_READ_BUF];
- ssize_t read_rv = read(global.rfd, buf, sizeof(buf) - 1);
- if (read_rv < 1) {
- global.last_errno = errno;
- return TB_ERR_RESIZE_READ;
- }
- buf[read_rv] = '\0';
-
- int rw, rh;
- if (sscanf(buf, "\x1b[%d;%dR", &rh, &rw) != 2) {
- return TB_ERR_RESIZE_SSCANF;
- }
-
- global.width = rw;
- global.height = rh;
- return TB_OK;
-}
-
-static int init_cellbuf(void) {
- int rv;
- if_err_return(rv, cellbuf_init(&global.back, global.width, global.height));
- if_err_return(rv, cellbuf_init(&global.front, global.width, global.height));
- if_err_return(rv, cellbuf_clear(&global.back));
- if_err_return(rv, cellbuf_clear(&global.front));
- return TB_OK;
-}
-
-static int tb_deinit(void) {
- if (global.caps[0] != NULL && global.wfd >= 0) {
- bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
- bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]);
- bytebuf_puts(&global.out, global.caps[TB_CAP_CLEAR_SCREEN]);
- bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_CA]);
- bytebuf_puts(&global.out, global.caps[TB_CAP_EXIT_KEYPAD]);
- bytebuf_puts(&global.out, TB_HARDCAP_EXIT_MOUSE);
- bytebuf_flush(&global.out, global.wfd);
- }
- if (global.ttyfd >= 0) {
- if (global.has_orig_tios) {
- tcsetattr(global.ttyfd, TCSAFLUSH, &global.orig_tios);
- }
- if (global.ttyfd_open) {
- close(global.ttyfd);
- global.ttyfd_open = 0;
- }
- }
-
- struct sigaction sa;
- memset(&sa, 0, sizeof(sa));
- sa.sa_handler = SIG_DFL;
- sigaction(SIGWINCH, &sa, NULL);
- if (global.resize_pipefd[0] >= 0) close(global.resize_pipefd[0]);
- if (global.resize_pipefd[1] >= 0) close(global.resize_pipefd[1]);
-
- cellbuf_free(&global.back);
- cellbuf_free(&global.front);
- bytebuf_free(&global.in);
- bytebuf_free(&global.out);
-
- if (global.terminfo) tb_free(global.terminfo);
-
- cap_trie_deinit(&global.cap_trie);
-
- tb_reset();
- return TB_OK;
-}
-
-static int load_terminfo(void) {
- int rv;
- char tmp[TB_PATH_MAX];
-
- // See terminfo(5) "Fetching Compiled Descriptions" for a description of
- // this behavior. Some of these paths are compile-time ncurses options, so
- // best guesses are used here.
- const char *term = getenv("TERM");
- if (!term) {
- return TB_ERR;
- }
-
- // If TERMINFO is set, try that directory and stop
- const char *terminfo = getenv("TERMINFO");
- if (terminfo) {
- return load_terminfo_from_path(terminfo, term);
- }
-
- // Next try ~/.terminfo
- const char *home = getenv("HOME");
- if (home) {
- snprintf_or_return(rv, tmp, sizeof(tmp), "%s/.terminfo", home);
- if_ok_return(rv, load_terminfo_from_path(tmp, term));
- }
-
- // Next try TERMINFO_DIRS
- //
- // Note, empty entries are supposed to be interpretted as the "compiled-in
- // default", which is of course system-dependent. Previously /etc/terminfo
- // was used here. Let's skip empty entries altogether rather than give
- // precedence to a guess, and check common paths after this loop.
- const char *dirs = getenv("TERMINFO_DIRS");
- if (dirs) {
- snprintf_or_return(rv, tmp, sizeof(tmp), "%s", dirs);
- char *dir = strtok(tmp, ":");
- while (dir) {
- const char *cdir = dir;
- if (*cdir != '\0') {
- if_ok_return(rv, load_terminfo_from_path(cdir, term));
- }
- dir = strtok(NULL, ":");
- }
- }
-
-#ifdef TB_TERMINFO_DIR
- if_ok_return(rv, load_terminfo_from_path(TB_TERMINFO_DIR, term));
-#endif
- if_ok_return(rv, load_terminfo_from_path("/usr/local/etc/terminfo", term));
- if_ok_return(rv,
- load_terminfo_from_path("/usr/local/share/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/usr/local/lib/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/etc/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/usr/share/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/usr/lib/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/usr/share/lib/terminfo", term));
- if_ok_return(rv, load_terminfo_from_path("/lib/terminfo", term));
-
- return TB_ERR;
-}
-
-static int load_terminfo_from_path(const char *path, const char *term) {
- int rv;
- char tmp[TB_PATH_MAX];
-
- // Look for term at this terminfo location, e.g., <terminfo>/x/xterm
- snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%c/%s", path, term[0], term);
- if_ok_return(rv, read_terminfo_path(tmp));
-
-#ifdef __APPLE__
- // Try the Darwin equivalent path, e.g., <terminfo>/78/xterm
- snprintf_or_return(rv, tmp, sizeof(tmp), "%s/%x/%s", path, term[0], term);
- return read_terminfo_path(tmp);
-#endif
-
- return TB_ERR;
-}
-
-static int read_terminfo_path(const char *path) {
- FILE *fp = fopen(path, "rb");
- if (!fp) {
- return TB_ERR;
- }
-
- struct stat st;
- if (fstat(fileno(fp), &st) != 0) {
- fclose(fp);
- return TB_ERR;
- }
-
- size_t fsize = st.st_size;
- char *data = (char *)tb_malloc(fsize);
- if (!data) {
- fclose(fp);
- return TB_ERR;
- }
-
- if (fread(data, 1, fsize, fp) != fsize) {
- fclose(fp);
- tb_free(data);
- return TB_ERR;
- }
-
- global.terminfo = data;
- global.nterminfo = fsize;
-
- fclose(fp);
- return TB_OK;
-}
-
-static int parse_terminfo_caps(void) {
- // See term(5) "LEGACY STORAGE FORMAT" and "EXTENDED STORAGE FORMAT" for a
- // description of this behavior.
-
- // Ensure there's at least a header's worth of data
- if (global.nterminfo < 6) {
- return TB_ERR;
- }
-
- int16_t *header = (int16_t *)global.terminfo;
- // header[0] the magic number (octal 0432 or 01036)
- // header[1] the size, in bytes, of the names section
- // header[2] the number of bytes in the boolean section
- // header[3] the number of short integers in the numbers section
- // header[4] the number of offsets (short integers) in the strings section
- // header[5] the size, in bytes, of the string table
-
- // Legacy ints are 16-bit, extended ints are 32-bit
- const int bytes_per_int = header[0] == 01036 ? 4 // 32-bit
- : 2; // 16-bit
-
- // > Between the boolean section and the number section, a null byte will be
- // > inserted, if necessary, to ensure that the number section begins on an
- // > even byte
- const int align_offset = (header[1] + header[2]) % 2 != 0 ? 1 : 0;
-
- const int pos_str_offsets =
- (6 * sizeof(int16_t)) // header (12 bytes)
- + header[1] // length of names section
- + header[2] // length of boolean section
- + align_offset +
- (header[3] * bytes_per_int); // length of numbers section
-
- const int pos_str_table =
- pos_str_offsets +
- (header[4] * sizeof(int16_t)); // length of string offsets table
-
- // Load caps
- int i;
- for (i = 0; i < TB_CAP__COUNT; i++) {
- const char *cap = get_terminfo_string(pos_str_offsets, header[4],
- pos_str_table, header[5], terminfo_cap_indexes[i]);
- if (!cap) {
- // Something is not right
- return TB_ERR;
- }
- global.caps[i] = cap;
- }
-
- return TB_OK;
-}
-
-static int load_builtin_caps(void) {
- int i, j;
- const char *term = getenv("TERM");
-
- if (!term) {
- return TB_ERR_NO_TERM;
- }
-
- // Check for exact TERM match
- for (i = 0; builtin_terms[i].name != NULL; i++) {
- if (strcmp(term, builtin_terms[i].name) == 0) {
- for (j = 0; j < TB_CAP__COUNT; j++) {
- global.caps[j] = builtin_terms[i].caps[j];
- }
- return TB_OK;
- }
- }
-
- // Check for partial TERM or alias match
- for (i = 0; builtin_terms[i].name != NULL; i++) {
- if (strstr(term, builtin_terms[i].name) != NULL ||
- (*(builtin_terms[i].alias) != '\0' &&
- strstr(term, builtin_terms[i].alias) != NULL))
- {
- for (j = 0; j < TB_CAP__COUNT; j++) {
- global.caps[j] = builtin_terms[i].caps[j];
- }
- return TB_OK;
- }
- }
-
- return TB_ERR_UNSUPPORTED_TERM;
-}
-
-static const char *get_terminfo_string(int16_t str_offsets_pos,
- int16_t str_offsets_len, int16_t str_table_pos, int16_t str_table_len,
- int16_t str_index) {
- const int str_byte_index = (int)str_index * (int)sizeof(int16_t);
- if (str_byte_index >= (int)str_offsets_len * (int)sizeof(int16_t)) {
- // An offset beyond the table indicates absent
- // See `convert_strings` in tinfo `read_entry.c`
- return "";
- }
- const int16_t *str_offset =
- (int16_t *)(global.terminfo + (int)str_offsets_pos + str_byte_index);
- if ((char *)str_offset >= global.terminfo + global.nterminfo) {
- // str_offset points beyond end of entry
- // Truncated/corrupt terminfo entry?
- return NULL;
- }
- if (*str_offset < 0 || *str_offset >= str_table_len) {
- // A negative offset indicates absent
- // An offset beyond the table indicates absent
- // See `convert_strings` in tinfo `read_entry.c`
- return "";
- }
- if (((size_t)((int)str_table_pos + (int)*str_offset)) >= global.nterminfo) {
- // string points beyond end of entry
- // Truncated/corrupt terminfo entry?
- return NULL;
- }
- return (
- const char *)(global.terminfo + (int)str_table_pos + (int)*str_offset);
-}
-
-static int wait_event(struct tb_event *event, int timeout) {
- int rv;
- char buf[TB_OPT_READ_BUF];
-
- memset(event, 0, sizeof(*event));
- if_ok_return(rv, extract_event(event));
-
- fd_set fds;
- struct timeval tv;
- tv.tv_sec = timeout / 1000;
- tv.tv_usec = (timeout - (tv.tv_sec * 1000)) * 1000;
-
- do {
- FD_ZERO(&fds);
- FD_SET(global.rfd, &fds);
- FD_SET(global.resize_pipefd[0], &fds);
-
- int maxfd = global.resize_pipefd[0] > global.rfd
- ? global.resize_pipefd[0]
- : global.rfd;
-
- int select_rv =
- select(maxfd + 1, &fds, NULL, NULL, (timeout < 0) ? NULL : &tv);
-
- if (select_rv < 0) {
- // Let EINTR/EAGAIN bubble up
- global.last_errno = errno;
- return TB_ERR_POLL;
- } else if (select_rv == 0) {
- return TB_ERR_NO_EVENT;
- }
-
- int tty_has_events = (FD_ISSET(global.rfd, &fds));
- int resize_has_events = (FD_ISSET(global.resize_pipefd[0], &fds));
-
- if (tty_has_events) {
- ssize_t read_rv = read(global.rfd, buf, sizeof(buf));
- if (read_rv < 0) {
- global.last_errno = errno;
- return TB_ERR_READ;
- } else if (read_rv > 0) {
- bytebuf_nputs(&global.in, buf, read_rv);
- }
- }
-
- if (resize_has_events) {
- int ignore = 0;
- read(global.resize_pipefd[0], &ignore, sizeof(ignore));
- // TODO: Harden against errors encountered mid-resize
- if_err_return(rv, update_term_size());
- if_err_return(rv, resize_cellbufs());
- event->type = TB_EVENT_RESIZE;
- event->w = global.width;
- event->h = global.height;
- return TB_OK;
- }
-
- memset(event, 0, sizeof(*event));
- if_ok_return(rv, extract_event(event));
- } while (timeout == -1);
-
- return rv;
-}
-
-static int extract_event(struct tb_event *event) {
- int rv;
- struct bytebuf_t *in = &global.in;
-
- if (in->len == 0) {
- return TB_ERR;
- }
-
- if (in->buf[0] == '\x1b') {
- // Escape sequence?
- // In TB_INPUT_ESC, skip if the buffer is a single escape char
- if (!((global.input_mode & TB_INPUT_ESC) && in->len == 1)) {
- if_ok_or_need_more_return(rv, extract_esc(event));
- }
-
- // Escape key?
- if (global.input_mode & TB_INPUT_ESC) {
- event->type = TB_EVENT_KEY;
- event->ch = 0;
- event->key = TB_KEY_ESC;
- event->mod = 0;
- bytebuf_shift(in, 1);
- return TB_OK;
- }
-
- // Recurse for alt key
- event->mod |= TB_MOD_ALT;
- bytebuf_shift(in, 1);
- return extract_event(event);
- }
-
- // ASCII control key?
- if ((uint16_t)in->buf[0] < TB_KEY_SPACE || in->buf[0] == TB_KEY_BACKSPACE2)
- {
- event->type = TB_EVENT_KEY;
- event->ch = 0;
- event->key = (uint16_t)in->buf[0];
- event->mod |= TB_MOD_CTRL;
- bytebuf_shift(in, 1);
- return TB_OK;
- }
-
- // UTF-8?
- if (in->len >= (size_t)tb_utf8_char_length(in->buf[0])) {
- event->type = TB_EVENT_KEY;
- tb_utf8_char_to_unicode(&event->ch, in->buf);
- event->key = 0;
- bytebuf_shift(in, tb_utf8_char_length(in->buf[0]));
- return TB_OK;
- }
-
- // Need more input
- return TB_ERR;
-}
-
-static int extract_esc(struct tb_event *event) {
- int rv;
- if_ok_or_need_more_return(rv, extract_esc_user(event, 0));
- if_ok_or_need_more_return(rv, extract_esc_cap(event));
- if_ok_or_need_more_return(rv, extract_esc_mouse(event));
- if_ok_or_need_more_return(rv, extract_esc_user(event, 1));
- return TB_ERR;
-}
-
-static int extract_esc_user(struct tb_event *event, int is_post) {
- int rv;
- size_t consumed = 0;
- struct bytebuf_t *in = &global.in;
- int (*fn)(struct tb_event *, size_t *);
-
- fn = is_post ? global.fn_extract_esc_post : global.fn_extract_esc_pre;
-
- if (!fn) {
- return TB_ERR;
- }
-
- rv = fn(event, &consumed);
- if (rv == TB_OK) {
- bytebuf_shift(in, consumed);
- }
-
- if_ok_or_need_more_return(rv, rv);
- return TB_ERR;
-}
-
-static int extract_esc_cap(struct tb_event *event) {
- int rv;
- struct bytebuf_t *in = &global.in;
- struct cap_trie_t *node;
- size_t depth;
-
- if_err_return(rv, cap_trie_find(in->buf, in->len, &node, &depth));
- if (node->is_leaf) {
- // Found a leaf node
- event->type = TB_EVENT_KEY;
- event->ch = 0;
- event->key = node->key;
- event->mod = node->mod;
- bytebuf_shift(in, depth);
- return TB_OK;
- } else if (node->nchildren > 0 && in->len <= depth) {
- // Found a branch node (not enough input)
- return TB_ERR_NEED_MORE;
- }
-
- return TB_ERR;
-}
-
-static int extract_esc_mouse(struct tb_event *event) {
- struct bytebuf_t *in = &global.in;
-
- enum { TYPE_VT200 = 0, TYPE_1006, TYPE_1015, TYPE_MAX };
-
- const char *cmp[TYPE_MAX] = {//
- // X10 mouse encoding, the simplest one
- // \x1b [ M Cb Cx Cy
- [TYPE_VT200] = "\x1b[M",
- // xterm 1006 extended mode or urxvt 1015 extended mode
- // xterm: \x1b [ < Cb ; Cx ; Cy (M or m)
- [TYPE_1006] = "\x1b[<",
- // urxvt: \x1b [ Cb ; Cx ; Cy M
- [TYPE_1015] = "\x1b["};
-
- int type = 0;
- int ret = TB_ERR;
-
- // Unrolled at compile-time (probably)
- for (; type < TYPE_MAX; type++) {
- size_t size = strlen(cmp[type]);
-
- if (in->len >= size && (strncmp(cmp[type], in->buf, size)) == 0) {
- break;
- }
- }
-
- if (type == TYPE_MAX) {
- ret = TB_ERR; // No match
- return ret;
- }
-
- size_t buf_shift = 0;
-
- switch (type) {
- case TYPE_VT200:
- if (in->len >= 6) {
- int b = in->buf[3] - 0x20;
- int fail = 0;
-
- switch (b & 3) {
- case 0:
- event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
- : TB_KEY_MOUSE_LEFT;
- break;
- case 1:
- event->key = ((b & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
- : TB_KEY_MOUSE_MIDDLE;
- break;
- case 2:
- event->key = TB_KEY_MOUSE_RIGHT;
- break;
- case 3:
- event->key = TB_KEY_MOUSE_RELEASE;
- break;
- default:
- ret = TB_ERR;
- fail = 1;
- break;
- }
-
- if (!fail) {
- if ((b & 32) != 0) {
- event->mod |= TB_MOD_MOTION;
- }
-
- // the coord is 1,1 for upper left
- event->x = ((uint8_t)in->buf[4]) - 0x21;
- event->y = ((uint8_t)in->buf[5]) - 0x21;
-
- ret = TB_OK;
- }
-
- buf_shift = 6;
- }
- break;
- case TYPE_1006:
- // fallthrough
- case TYPE_1015: {
- size_t index_fail = (size_t)-1;
-
- enum {
- FIRST_M = 0,
- FIRST_SEMICOLON,
- LAST_SEMICOLON,
- FIRST_LAST_MAX
- };
-
- size_t indices[FIRST_LAST_MAX] = {index_fail, index_fail,
- index_fail};
- int m_is_capital = 0;
-
- for (size_t i = 0; i < in->len; i++) {
- if (in->buf[i] == ';') {
- if (indices[FIRST_SEMICOLON] == index_fail) {
- indices[FIRST_SEMICOLON] = i;
- } else {
- indices[LAST_SEMICOLON] = i;
- }
- } else if (indices[FIRST_M] == index_fail) {
- if (in->buf[i] == 'm' || in->buf[i] == 'M') {
- m_is_capital = (in->buf[i] == 'M');
- indices[FIRST_M] = i;
- }
- }
- }
-
- if (indices[FIRST_M] == index_fail ||
- indices[FIRST_SEMICOLON] == index_fail ||
- indices[LAST_SEMICOLON] == index_fail)
- {
- ret = TB_ERR;
- } else {
- int start = (type == TYPE_1015 ? 2 : 3);
-
- unsigned n1 = strtoul(&in->buf[start], NULL, 10);
- unsigned n2 =
- strtoul(&in->buf[indices[FIRST_SEMICOLON] + 1], NULL, 10);
- unsigned n3 =
- strtoul(&in->buf[indices[LAST_SEMICOLON] + 1], NULL, 10);
-
- if (type == TYPE_1015) {
- n1 -= 0x20;
- }
-
- int fail = 0;
-
- switch (n1 & 3) {
- case 0:
- event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_UP
- : TB_KEY_MOUSE_LEFT;
- break;
- case 1:
- event->key = ((n1 & 64) != 0) ? TB_KEY_MOUSE_WHEEL_DOWN
- : TB_KEY_MOUSE_MIDDLE;
- break;
- case 2:
- event->key = TB_KEY_MOUSE_RIGHT;
- break;
- case 3:
- event->key = TB_KEY_MOUSE_RELEASE;
- break;
- default:
- ret = TB_ERR;
- fail = 1;
- break;
- }
-
- buf_shift = in->len;
-
- if (!fail) {
- if (!m_is_capital) {
- // on xterm mouse release is signaled by lowercase m
- event->key = TB_KEY_MOUSE_RELEASE;
- }
-
- if ((n1 & 32) != 0) {
- event->mod |= TB_MOD_MOTION;
- }
-
- event->x = ((uint8_t)n2) - 1;
- event->y = ((uint8_t)n3) - 1;
-
- ret = TB_OK;
- }
- }
- } break;
- case TYPE_MAX:
- ret = TB_ERR;
- }
-
- if (buf_shift > 0) {
- bytebuf_shift(in, buf_shift);
- }
-
- if (ret == TB_OK) {
- event->type = TB_EVENT_MOUSE;
- }
-
- return ret;
-}
-
-static int resize_cellbufs(void) {
- int rv;
- if_err_return(rv,
- cellbuf_resize(&global.back, global.width, global.height));
- if_err_return(rv,
- cellbuf_resize(&global.front, global.width, global.height));
- if_err_return(rv, cellbuf_clear(&global.front));
- if_err_return(rv, send_clear());
- return TB_OK;
-}
-
-static void handle_resize(int sig) {
- int errno_copy = errno;
- write(global.resize_pipefd[1], &sig, sizeof(sig));
- errno = errno_copy;
-}
-
-static int send_attr(uintattr_t fg, uintattr_t bg) {
- int rv;
-
- if (fg == global.last_fg && bg == global.last_bg) {
- return TB_OK;
- }
-
- if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_SGR0]));
-
- uint32_t cfg, cbg;
- switch (global.output_mode) {
- default:
- case TB_OUTPUT_NORMAL:
- // The minus 1 below is because our colors are 1-indexed starting
- // from black. Black is represented by a 30, 40, 90, or 100 for fg,
- // bg, bright fg, or bright bg respectively. Red is 31, 41, 91,
- // 101, etc.
- cfg = (fg & TB_BRIGHT ? 90 : 30) + (fg & 0x0f) - 1;
- cbg = (bg & TB_BRIGHT ? 100 : 40) + (bg & 0x0f) - 1;
- break;
-
- case TB_OUTPUT_256:
- cfg = fg & 0xff;
- cbg = bg & 0xff;
- if (fg & TB_HI_BLACK) cfg = 0;
- if (bg & TB_HI_BLACK) cbg = 0;
- break;
-
- case TB_OUTPUT_216:
- cfg = fg & 0xff;
- cbg = bg & 0xff;
- if (cfg > 216) cfg = 216;
- if (cbg > 216) cbg = 216;
- cfg += 0x0f;
- cbg += 0x0f;
- break;
-
- case TB_OUTPUT_GRAYSCALE:
- cfg = fg & 0xff;
- cbg = bg & 0xff;
- if (cfg > 24) cfg = 24;
- if (cbg > 24) cbg = 24;
- cfg += 0xe7;
- cbg += 0xe7;
- break;
-
-#if TB_OPT_ATTR_W >= 32
- case TB_OUTPUT_TRUECOLOR:
- cfg = fg & 0xffffff;
- cbg = bg & 0xffffff;
- if (fg & TB_HI_BLACK) cfg = 0;
- if (bg & TB_HI_BLACK) cbg = 0;
- break;
-#endif
- }
-
- if (fg & TB_BOLD)
- if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BOLD]));
-
- if (fg & TB_BLINK)
- if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_BLINK]));
-
- if (fg & TB_UNDERLINE)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_UNDERLINE]));
-
- if (fg & TB_ITALIC)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_ITALIC]));
-
- if (fg & TB_DIM)
- if_err_return(rv, bytebuf_puts(&global.out, global.caps[TB_CAP_DIM]));
-
-#if TB_OPT_ATTR_W == 64
- if (fg & TB_STRIKEOUT)
- if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_STRIKEOUT));
-
- if (fg & TB_UNDERLINE_2)
- if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_UNDERLINE_2));
-
- if (fg & TB_OVERLINE)
- if_err_return(rv, bytebuf_puts(&global.out, TB_HARDCAP_OVERLINE));
-
- if (fg & TB_INVISIBLE)
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_INVISIBLE]));
-#endif
-
- if ((fg & TB_REVERSE) || (bg & TB_REVERSE))
- if_err_return(rv,
- bytebuf_puts(&global.out, global.caps[TB_CAP_REVERSE]));
-
- int fg_is_default = (fg & 0xff) == 0;
- int bg_is_default = (bg & 0xff) == 0;
- if (global.output_mode == TB_OUTPUT_256) {
- if (fg & TB_HI_BLACK) fg_is_default = 0;
- if (bg & TB_HI_BLACK) bg_is_default = 0;
- }
-#if TB_OPT_ATTR_W >= 32
- if (global.output_mode == TB_OUTPUT_TRUECOLOR) {
- fg_is_default = ((fg & 0xffffff) == 0) && ((fg & TB_HI_BLACK) == 0);
- bg_is_default = ((bg & 0xffffff) == 0) && ((bg & TB_HI_BLACK) == 0);
- }
-#endif
-
- if_err_return(rv, send_sgr(cfg, cbg, fg_is_default, bg_is_default));
-
- global.last_fg = fg;
- global.last_bg = bg;
-
- return TB_OK;
-}
-
-static int send_sgr(uint32_t cfg, uint32_t cbg, int fg_is_default,
- int bg_is_default) {
- int rv;
- char nbuf[32];
-
- if (fg_is_default && bg_is_default) {
- return TB_OK;
- }
-
- switch (global.output_mode) {
- default:
- case TB_OUTPUT_NORMAL:
- send_literal(rv, "\x1b[");
- if (!fg_is_default) {
- send_num(rv, nbuf, cfg);
- if (!bg_is_default) {
- send_literal(rv, ";");
- }
- }
- if (!bg_is_default) {
- send_num(rv, nbuf, cbg);
- }
- send_literal(rv, "m");
- break;
-
- case TB_OUTPUT_256:
- case TB_OUTPUT_216:
- case TB_OUTPUT_GRAYSCALE:
- send_literal(rv, "\x1b[");
- if (!fg_is_default) {
- send_literal(rv, "38;5;");
- send_num(rv, nbuf, cfg);
- if (!bg_is_default) {
- send_literal(rv, ";");
- }
- }
- if (!bg_is_default) {
- send_literal(rv, "48;5;");
- send_num(rv, nbuf, cbg);
- }
- send_literal(rv, "m");
- break;
-
-#if TB_OPT_ATTR_W >= 32
- case TB_OUTPUT_TRUECOLOR:
- send_literal(rv, "\x1b[");
- if (!fg_is_default) {
- send_literal(rv, "38;2;");
- send_num(rv, nbuf, (cfg >> 16) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, (cfg >> 8) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, cfg & 0xff);
- if (!bg_is_default) {
- send_literal(rv, ";");
- }
- }
- if (!bg_is_default) {
- send_literal(rv, "48;2;");
- send_num(rv, nbuf, (cbg >> 16) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, (cbg >> 8) & 0xff);
- send_literal(rv, ";");
- send_num(rv, nbuf, cbg & 0xff);
- }
- send_literal(rv, "m");
- break;
-#endif
- }
- return TB_OK;
-}
-
-static int send_cursor_if(int x, int y) {
- int rv;
- char nbuf[32];
- if (x < 0 || y < 0) {
- return TB_OK;
- }
- send_literal(rv, "\x1b[");
- send_num(rv, nbuf, y + 1);
- send_literal(rv, ";");
- send_num(rv, nbuf, x + 1);
- send_literal(rv, "H");
- return TB_OK;
-}
-
-static int send_char(int x, int y, uint32_t ch) {
- return send_cluster(x, y, &ch, 1);
-}
-
-static int send_cluster(int x, int y, uint32_t *ch, size_t nch) {
- int rv;
- char chu8[8];
-
- if (global.last_x != x - 1 || global.last_y != y) {
- if_err_return(rv, send_cursor_if(x, y));
- }
- global.last_x = x;
- global.last_y = y;
-
- int i;
- for (i = 0; i < (int)nch; i++) {
- uint32_t ch32 = *(ch + i);
- if (!iswprint((wint_t)ch32)) {
- ch32 = 0xfffd; // replace non-printable codepoints with U+FFFD
- }
- int chu8_len = tb_utf8_unicode_to_char(chu8, ch32);
- if_err_return(rv, bytebuf_nputs(&global.out, chu8, (size_t)chu8_len));
- }
-
- return TB_OK;
-}
-
-static int convert_num(uint32_t num, char *buf) {
- int i, l = 0;
- char ch;
- do {
- buf[l++] = (char)('0' + (num % 10));
- num /= 10;
- } while (num);
- for (i = 0; i < l / 2; i++) {
- ch = buf[i];
- buf[i] = buf[l - 1 - i];
- buf[l - 1 - i] = ch;
- }
- return l;
-}
-
-static int cell_cmp(struct tb_cell *a, struct tb_cell *b) {
- if (a->ch != b->ch || a->fg != b->fg || a->bg != b->bg) {
- return 1;
- }
-#ifdef TB_OPT_EGC
- if (a->nech != b->nech) {
- return 1;
- } else if (a->nech > 0) { // a->nech == b->nech
- return memcmp(a->ech, b->ech, a->nech);
- }
-#endif
- return 0;
-}
-
-static int cell_copy(struct tb_cell *dst, struct tb_cell *src) {
-#ifdef TB_OPT_EGC
- if (src->nech > 0) {
- return cell_set(dst, src->ech, src->nech, src->fg, src->bg);
- }
-#endif
- return cell_set(dst, &src->ch, 1, src->fg, src->bg);
-}
-
-static int cell_set(struct tb_cell *cell, uint32_t *ch, size_t nch,
- uintattr_t fg, uintattr_t bg) {
- cell->ch = ch ? *ch : 0;
- cell->fg = fg;
- cell->bg = bg;
-#ifdef TB_OPT_EGC
- if (nch <= 1) {
- cell->nech = 0;
- } else {
- int rv;
- if_err_return(rv, cell_reserve_ech(cell, nch + 1));
- memcpy(cell->ech, ch, sizeof(ch) * nch);
- cell->ech[nch] = '\0';
- cell->nech = nch;
- }
-#else
- (void)nch;
- (void)cell_reserve_ech;
-#endif
- return TB_OK;
-}
-
-static int cell_reserve_ech(struct tb_cell *cell, size_t n) {
-#ifdef TB_OPT_EGC
- if (cell->cech >= n) {
- return TB_OK;
- }
- if (!(cell->ech = tb_realloc(cell->ech, n * sizeof(cell->ch)))) {
- return TB_ERR_MEM;
- }
- cell->cech = n;
- return TB_OK;
-#else
- (void)cell;
- (void)n;
- return TB_ERR;
-#endif
-}
-
-static int cell_free(struct tb_cell *cell) {
-#ifdef TB_OPT_EGC
- if (cell->ech) {
- tb_free(cell->ech);
- }
-#endif
- memset(cell, 0, sizeof(*cell));
- return TB_OK;
-}
-
-static int cellbuf_init(struct cellbuf_t *c, int w, int h) {
- c->cells = (struct tb_cell *)tb_malloc(sizeof(struct tb_cell) * w * h);
- if (!c->cells) {
- return TB_ERR_MEM;
- }
- memset(c->cells, 0, sizeof(struct tb_cell) * w * h);
- c->width = w;
- c->height = h;
- return TB_OK;
-}
-
-static int cellbuf_free(struct cellbuf_t *c) {
- if (c->cells) {
- int i;
- for (i = 0; i < c->width * c->height; i++) {
- cell_free(&c->cells[i]);
- }
- tb_free(c->cells);
- }
- memset(c, 0, sizeof(*c));
- return TB_OK;
-}
-
-static int cellbuf_clear(struct cellbuf_t *c) {
- int rv, i;
- uint32_t space = (uint32_t)' ';
- for (i = 0; i < c->width * c->height; i++) {
- if_err_return(rv,
- cell_set(&c->cells[i], &space, 1, global.fg, global.bg));
- }
- return TB_OK;
-}
-
-static int cellbuf_get(struct cellbuf_t *c, int x, int y,
- struct tb_cell **out) {
- if (!cellbuf_in_bounds(c, x, y)) {
- *out = NULL;
- return TB_ERR_OUT_OF_BOUNDS;
- }
- *out = &c->cells[(y * c->width) + x];
- return TB_OK;
-}
-
-static int cellbuf_in_bounds(struct cellbuf_t *c, int x, int y) {
- if (x < 0 || x >= c->width || y < 0 || y >= c->height) {
- return 0;
- }
- return 1;
-}
-
-static int cellbuf_resize(struct cellbuf_t *c, int w, int h) {
- int rv;
-
- int ow = c->width;
- int oh = c->height;
-
- if (ow == w && oh == h) {
- return TB_OK;
- }
-
- w = w < 1 ? 1 : w;
- h = h < 1 ? 1 : h;
-
- int minw = (w < ow) ? w : ow;
- int minh = (h < oh) ? h : oh;
-
- struct tb_cell *prev = c->cells;
-
- if_err_return(rv, cellbuf_init(c, w, h));
- if_err_return(rv, cellbuf_clear(c));
-
- int x, y;
- for (x = 0; x < minw; x++) {
- for (y = 0; y < minh; y++) {
- struct tb_cell *src, *dst;
- src = &prev[(y * ow) + x];
- if_err_return(rv, cellbuf_get(c, x, y, &dst));
- if_err_return(rv, cell_copy(dst, src));
- }
- }
-
- tb_free(prev);
-
- return TB_OK;
-}
-
-static int bytebuf_puts(struct bytebuf_t *b, const char *str) {
- if (!str || strlen(str) <= 0) return TB_OK; // Nothing to do for empty caps
- return bytebuf_nputs(b, str, (size_t)strlen(str));
-}
-
-static int bytebuf_nputs(struct bytebuf_t *b, const char *str, size_t nstr) {
- int rv;
- if_err_return(rv, bytebuf_reserve(b, b->len + nstr + 1));
- memcpy(b->buf + b->len, str, nstr);
- b->len += nstr;
- b->buf[b->len] = '\0';
- return TB_OK;
-}
-
-static int bytebuf_shift(struct bytebuf_t *b, size_t n) {
- if (n > b->len) {
- n = b->len;
- }
- size_t nmove = b->len - n;
- memmove(b->buf, b->buf + n, nmove);
- b->len -= n;
- return TB_OK;
-}
-
-static int bytebuf_flush(struct bytebuf_t *b, int fd) {
- if (b->len <= 0) {
- return TB_OK;
- }
- ssize_t write_rv = write(fd, b->buf, b->len);
- if (write_rv < 0 || (size_t)write_rv != b->len) {
- // Note, errno will be 0 on partial write
- global.last_errno = errno;
- return TB_ERR;
- }
- b->len = 0;
- return TB_OK;
-}
-
-static int bytebuf_reserve(struct bytebuf_t *b, size_t sz) {
- if (b->cap >= sz) {
- return TB_OK;
- }
- size_t newcap = b->cap > 0 ? b->cap : 1;
- while (newcap < sz) {
- newcap *= 2;
- }
- char *newbuf;
- if (b->buf) {
- newbuf = (char *)tb_realloc(b->buf, newcap);
- } else {
- newbuf = (char *)tb_malloc(newcap);
- }
- if (!newbuf) {
- return TB_ERR_MEM;
- }
- b->buf = newbuf;
- b->cap = newcap;
- return TB_OK;
-}
-
-static int bytebuf_free(struct bytebuf_t *b) {
- if (b->buf) {
- tb_free(b->buf);
- }
- memset(b, 0, sizeof(*b));
- return TB_OK;
-}
-
-#endif // TB_IMPL
diff --git a/archived/wrap.c b/archived/wrap.c
deleted file mode 100644
index 2317c6e..0000000
--- a/archived/wrap.c
+++ /dev/null
@@ -1,52 +0,0 @@
-// 1. Search backwards for whitespace
-// - found?
-// y) wrap
-// n) break at limit
-// - end?
-// y) terminate
-// n) goto 1. with offset += limit
-void
-wrap(u8* Text, u32 Len, u32 XLimit, u32 YLimit)
-{
- u32 SearchingOffset = XLimit;
- u32 X = SearchingOffset;
- u32 Y = 0;
- u8 t;
- u32 PrevX = 0;
-
- while (X < Len)
- {
- // Search for whitespace to break on
- while (1)
- {
- if (is_whitespace(Text[X])) break;
-
- X--;
-
- // if we got back to the previous position break on Text[SearchingOffset]
- if (X == PrevX)
- {
- X = XLimit;
- break;
- }
- }
-
- // break
- t = Text[X];
- Text[X] = '\0';
- tb_printf(0, Y, 0, 0, "%s", Text + PrevX);
- Text[X] = t;
- Y++;
- if (Y >= YLimit) break;
-
- // consume leading whitespace
- while (is_whitespace(Text[X])) X++;
-
- PrevX = X;
- X += XLimit;
- }
-
- tb_printf(0, Y, 0, 0, "%s", Text + PrevX);
-
- return;
-}