aboutsummaryrefslogtreecommitdiff
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
parent0574f5a7c5159a2ae1d7d2182cec982509947db9 (diff)
First pass at preparing for Github
-rw-r--r--.gitignore10
-rw-r--r--LICENSE7
-rw-r--r--Makefile11
-rw-r--r--README.md120
-rw-r--r--archived/v1/.gitignore7
-rw-r--r--archived/wrap.c52
-rwxr-xr-xbuild.sh7
-rwxr-xr-xbuild/chattybin0 -> 139016 bytes
-rwxr-xr-xbuild/input_boxbin0 -> 117432 bytes
-rwxr-xr-xbuild/serverbin0 -> 44688 bytes
-rw-r--r--chatty2.c90
-rw-r--r--compile_flags.txt5
-rw-r--r--external/keyboard.c777
-rw-r--r--gdb_scripts12
-rw-r--r--notes/archived.md (renamed from archived.md)0
-rw-r--r--notes/chatty.md76
-rw-r--r--notes/insert.md (renamed from insert.md)0
-rw-r--r--notes/scroll.md (renamed from scroll.md)0
-rw-r--r--source/archived/array.h (renamed from archived/array.h)0
-rw-r--r--source/archived/input_box.c (renamed from archived/input_box.c)11
-rw-r--r--source/archived/network_compression.c (renamed from archived/network_compression.c)0
-rw-r--r--source/archived/scrollandwrapped.c (renamed from scrollandwrapped.c)0
-rw-r--r--source/archived/send.c (renamed from send.c)0
-rw-r--r--source/archived/ui_checkmark.c (renamed from archived/ui_checkmark.c)0
-rwxr-xr-xsource/archived/ui_meterbin0 -> 101936 bytes
-rw-r--r--source/archived/ui_meter.c (renamed from archived/ui_meter.c)2
-rw-r--r--source/archived/ui_selection.c (renamed from archived/ui_selection.c)0
-rw-r--r--source/archived/ui_wrapped.c (renamed from ui_wrapped.c)0
-rw-r--r--source/archived/utf8toASCII.c (renamed from archived/utf8toASCII.c)0
-rw-r--r--source/archived/v1/README.md (renamed from archived/v1/README.md)0
-rw-r--r--source/archived/v1/arena.h (renamed from archived/v1/arena.h)0
-rwxr-xr-xsource/archived/v1/build.sh (renamed from archived/v1/build.sh)0
-rw-r--r--source/archived/v1/client.c (renamed from archived/v1/client.c)0
-rw-r--r--source/archived/v1/common.h (renamed from archived/v1/common.h)0
-rw-r--r--source/archived/v1/compile_flags.txt (renamed from archived/v1/compile_flags.txt)0
-rw-r--r--source/archived/v1/recv.c (renamed from archived/v1/recv.c)0
-rw-r--r--source/archived/v1/send.c (renamed from archived/v1/send.c)0
-rw-r--r--source/archived/v1/server.c (renamed from archived/v1/server.c)0
-rw-r--r--source/archived/v1/termbox2.h (renamed from archived/v1/termbox2.h)0
-rw-r--r--source/archived/wrap.c (renamed from wrap.c)0
-rw-r--r--source/arena.h (renamed from arena.h)28
-rwxr-xr-xsource/build.sh14
-rw-r--r--source/chatty.c (renamed from chatty.c)15
-rw-r--r--source/chatty.h (renamed from chatty.h)12
-rw-r--r--source/protocol.h (renamed from protocol.h)2
-rw-r--r--source/server.c (renamed from server.c)0
-rw-r--r--source/termbox2.h (renamed from external/termbox2.h)1
-rw-r--r--source/ui.h (renamed from ui.h)23
-rwxr-xr-xtests/a.outbin0 -> 78728 bytes
-rwxr-xr-xtests/build.sh12
-rw-r--r--tests/test.h (renamed from test.h)2
-rwxr-xr-xtests/testsbin0 -> 107832 bytes
-rw-r--r--tests/tests.c (renamed from tests.c)7
-rw-r--r--types.h15
-rw-r--r--ui_box.h472
55 files changed, 227 insertions, 1563 deletions
diff --git a/.gitignore b/.gitignore
index dbd414c..f2e627a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,8 +1,2 @@
-build/*
-external/keyboard
-
-_id
-_clients
-*.log
-
-tags
+.gitignore
+chatty.log
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..666de47
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,7 @@
+Copyright (c) 2025 Luca Raymaekers
+
+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. \ No newline at end of file
diff --git a/Makefile b/Makefile
deleted file mode 100644
index aa3eea1..0000000
--- a/Makefile
+++ /dev/null
@@ -1,11 +0,0 @@
-all: chatty server send
-
-clean:
- rm -f server chatty send tags *.log _*
-
-chatty:
- gcc -ggdb -Wall -pedantic -std=c99 -o chatty chatty.c
-server:
- gcc -ggdb -Wall -pedantic -std=c99 -o server server.c
-send:
- gcc -ggdb -Wall -pedantic -std=c99 -o send send.c
diff --git a/README.md b/README.md
index 565fc2e..c69eb45 100644
--- a/README.md
+++ b/README.md
@@ -1,95 +1,53 @@
-# Chatty
-The idea is the following:
-- tcp server that you can send messages to
-- history upon connecting
-- date of messages sent
-- client for reading the messages and sending them at the same time
-- rooms
-- encryption
-- authentication
-
-## client
-- [ ] BUG: text is not appearing after typing
-- [ ] BUG: when connecting two clients of the same account
-- [ ] BUG: wrapping does not work and displays nothing if there is no screen space
-- [ ] BUG: reconnect does not work when server does not know id
-- [ ] TODO: Convert tabs to spaces
-- [ ] BUG: when using lots of markup characters
-- [ ] TODO: Newline support
- - [ ] resizable box
-
-## server
-- [ ] check that fds arena does not overflow
- - free clients which disconnected and use free list to give them space
-- [ ] check if when sending and the client is offline (due to connection loss) what happens
-- [ ] timeout on recv?
-- [ ] use threads to handle clients/ timeout when receiving because a client could theoretically
- stall the entire server.
-- [ ] do not crash on errors from clients
- - implement error message?
- - timeout on recv with setsockopt
-- [ ] theoretically two clients can connect at the same time. The uni/bi connections should be
- negotiated.
-
-## common
-- [ ] use IP address / domain
-- [ ] chat history
-- [ ] rooms
-- [ ] compression
-
-## Protocol
-- see `protocol.h` for more info
-
-- The null terminator must be sent with the string.
-- The text can be arbitrary length
-
-## Keybinds
+# chatty: The terminal chat application
+
+## Overview
+`chatty` is a terminal chat application.
+Included is also a server.
+
+### Client features
+- users are saved
+- you can send messages
+- you can pause and resume with `Ctrl-Z` and the `fg` command
+- messages can have basic markdown formatting
+- basic shortcuts for editing the message
+- reconnecting on
+#### Shortcuts
- `Ctrl+C` | `Ctrl+D`: quits
- `Ctrl+U`: Erase input line
- `Ctrl+W`: Erase word behind cursor
- `Ctrl+Y`: Paste clipboard into input field
-## Resources I used for building this
+### Server features
+- multiple users
+- recovering on invalid messages
+- send "connected"/"disconnected" messages to other clients
+
+## Build
+Run the build script.
+```sh
+./source/build.sh
+```
+
+## Try it out
+Run the server with
+```sh
+./build/server
+```
+> You can stop it with `Ctrl-D`
+
+In another prompt, start a client with
+```sh
+./build/chatty Poulbi
+```
+
+# Resources
+- terminal library: [Termbox2](https://github.com/termbox/termbox2)
- source code I looked at:
- https://github.com/git-bruh/matrix-tui
- https://github.com/NikitaIvanovV/ictree
- - https://github.com/termbox/termbox2
- *mmap & gdb*: [Tsoding - "Why linux has this syscall?" ](https://youtu.be/sFYFuBzu9Ow?si=CX32IzFVA8OPDZvS)
- *pthreads*: [C for dummies](https://c-for-dummies.com/blog/?p=5365)
- *unicode and wide characters*: [C for dummies](https://c-for-dummies.com/blog/?p=2578)
- *sockets*: [Nir Lichtman - Making Minimalist Chat Server in C on Linux](https://www.youtube.com/watch?v=gGfTjKwLQxY)
- syscall manpages `man`
- UTF8 Comprssion: [Casey Muratori - Simple RLE Compressor](https://www.youtube.com/watch?v=kikLEdc3C1c&t=6312s)
-
-### To Read
-#### C Programming
-- https://www.youtube.com/watch?v=wvtFGa6XJDU
-- https://nullprogram.com/blog/2023/02/11/
-- https://nullprogram.com/blog/2023/02/13/
-- https://nullprogram.com/blog/2023/10/08/
-#### Encryption w/ Compression
-- https://en.wikipedia.org/wiki/BREACH
-- https://en.wikipedia.org/wiki/CRIME
-- https://crypto.stackexchange.com/questions/2283/crypto-compression-algorithms
-- openpgp https://www.rfc-editor.org/rfc/rfc4880
-- https://security.stackexchange.com/questions/19911/crime-how-to-beat-the-beast-successor
-- https://blog.qualys.com/product-tech/2012/09/14/crime-information-leakage-attack-against-ssltls
-- Algorithms:
- *Symmetric*
- - AESI
- - Blowfish
- - Twofish
- - Rivest Cipher (RC4)
- *Assymetric*
- - Data Encryption Standard (DES)
- - ECDSA
- - RSA
- - Diffie-Hellman
- - PGP
- _Hash_
- - Deflate
- - Huffman Coding
- - LZ77
- Other
- - ChaCha20-Poly1305
- - AES(-GCM)
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/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;
-}
diff --git a/build.sh b/build.sh
deleted file mode 100755
index 50572d0..0000000
--- a/build.sh
+++ /dev/null
@@ -1,7 +0,0 @@
-#!/bin/sh
-set -x
-# gcc external/keyboard.c
-gcc -DDEBUG -ggdb -Wall -pedantic -std=c11 -I external -o build/chatty chatty.c
-gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o build/server server.c
-# gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o build/send send.c
-gcc -DDEBUG -ggdb -Wall -pedantic -std=c11 -I external -I . -o build/input_box archived/input_box.c
diff --git a/build/chatty b/build/chatty
new file mode 100755
index 0000000..06f4379
--- /dev/null
+++ b/build/chatty
Binary files differ
diff --git a/build/input_box b/build/input_box
new file mode 100755
index 0000000..466a38c
--- /dev/null
+++ b/build/input_box
Binary files differ
diff --git a/build/server b/build/server
new file mode 100755
index 0000000..90d2d33
--- /dev/null
+++ b/build/server
Binary files differ
diff --git a/chatty2.c b/chatty2.c
deleted file mode 100644
index 5a00fee..0000000
--- a/chatty2.c
+++ /dev/null
@@ -1,90 +0,0 @@
-/* Macro's */
-
-#define TB_IMPL
-#include "external/termbox2.h"
-#undef TB_IMPL
-
-#define DEBUG
-#define MAX_INPUT_LEN 255
-
-#define CHATTY_IMPL
-#include "ui.h"
-#undef CHATTY_IMPL
-#include "protocol.h"
-
-#include <locale.h>
-
-#ifdef DEBUG
-#define Assert(expr) \
- if (!(expr)) \
- { \
- tb_shutdown(); \
- raise(SIGTRAP); \
- }
-#else
-#define Assert(expr) ;
-#endif
-
-int
-main(int Argc, char *Argv[])
-{
- struct tb_event ev;
- rect TextBox = {0, 0, 24, 4};
- rect TextR = {
- TextBox.X + TEXTBOX_BORDER_WIDTH + TEXTBOX_PADDING_X,
- TextBox.Y + TEXTBOX_BORDER_WIDTH,
- TextBox.W - TEXTBOX_BORDER_WIDTH * 2 - TEXTBOX_PADDING_X * 2,
- TextBox.H - TEXTBOX_BORDER_WIDTH * 2
- };
- wchar_t Input[MAX_INPUT_LEN] = {0};
- u32 InputLen = 0;
- u32 InputOffset = 0;
- u32 InputPos = 0;
- u32 DidParseKey = 0;
-
- Assert(setlocale(LC_ALL, ""));
- tb_init();
- global.cursor_x = TextR.X;
- global.cursor_y = TextR.Y;
- bytebuf_puts(&global.out, global.caps[TB_CAP_SHOW_CURSOR]);
-
- while (ev.key != TB_KEY_CTRL_C)
- {
- tb_clear();
-
- DrawBox(TextBox, 0);
- TextBoxDraw(TextR, Input + InputOffset, InputLen);
-
- InputPos = InputOffset + (global.cursor_x - TextR.X) + (global.cursor_y - TextR.Y) * TextR.W;
- Assert(InputPos <= InputLen);
-
- tb_present();
-
- tb_poll_event(&ev);
-
- // TODO: Handle resize event
-
- // Intercept keys
- if (ev.key == TB_KEY_CTRL_M)
- {
- tb_printf(26, 0, 0, 0, "sent.");
- continue;
- }
- else
- {
- DidParseKey = TextBoxKeypress(ev, TextR,
- Input, &InputLen, InputPos, &InputOffset);
- }
-
- u32 ShouldInsert = (!DidParseKey) && (ev.ch && InputLen < MAX_INPUT_LEN);
- if (ShouldInsert)
- {
- TextBoxInsert(Input, InputPos, InputLen++, ev.ch);
- ScrollRight(TextR, &InputOffset);
- }
-
- // tb_clear();
- }
-
- tb_shutdown();
-}
diff --git a/compile_flags.txt b/compile_flags.txt
deleted file mode 100644
index eb526c6..0000000
--- a/compile_flags.txt
+++ /dev/null
@@ -1,5 +0,0 @@
--Wall
--Wextra
--pedantic
--std=c99
--O3
diff --git a/external/keyboard.c b/external/keyboard.c
deleted file mode 100644
index e53fac5..0000000
--- a/external/keyboard.c
+++ /dev/null
@@ -1,777 +0,0 @@
-#define TB_IMPL
-#include "termbox2.h"
-
-#include <assert.h>
-#include <stdint.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <locale.h>
-
-struct key {
- unsigned char x;
- unsigned char y;
- uint32_t ch;
-};
-
-#define STOP {0,0,0}
-struct key K_ESC[] = {{1,1,'E'},{2,1,'S'},{3,1,'C'},STOP};
-struct key K_F1[] = {{6,1,'F'},{7,1,'1'},STOP};
-struct key K_F2[] = {{9,1,'F'},{10,1,'2'},STOP};
-struct key K_F3[] = {{12,1,'F'},{13,1,'3'},STOP};
-struct key K_F4[] = {{15,1,'F'},{16,1,'4'},STOP};
-struct key K_F5[] = {{19,1,'F'},{20,1,'5'},STOP};
-struct key K_F6[] = {{22,1,'F'},{23,1,'6'},STOP};
-struct key K_F7[] = {{25,1,'F'},{26,1,'7'},STOP};
-struct key K_F8[] = {{28,1,'F'},{29,1,'8'},STOP};
-struct key K_F9[] = {{33,1,'F'},{34,1,'9'},STOP};
-struct key K_F10[] = {{36,1,'F'},{37,1,'1'},{38,1,'0'},STOP};
-struct key K_F11[] = {{40,1,'F'},{41,1,'1'},{42,1,'1'},STOP};
-struct key K_F12[] = {{44,1,'F'},{45,1,'1'},{46,1,'2'},STOP};
-struct key K_PRN[] = {{50,1,'P'},{51,1,'R'},{52,1,'N'},STOP};
-struct key K_SCR[] = {{54,1,'S'},{55,1,'C'},{56,1,'R'},STOP};
-struct key K_BRK[] = {{58,1,'B'},{59,1,'R'},{60,1,'K'},STOP};
-struct key K_LED1[] = {{66,1,'-'},STOP};
-struct key K_LED2[] = {{70,1,'-'},STOP};
-struct key K_LED3[] = {{74,1,'-'},STOP};
-
-struct key K_TILDE[] = {{1,4,'`'},STOP};
-struct key K_TILDE_SHIFT[] = {{1,4,'~'},STOP};
-struct key K_1[] = {{4,4,'1'},STOP};
-struct key K_1_SHIFT[] = {{4,4,'!'},STOP};
-struct key K_2[] = {{7,4,'2'},STOP};
-struct key K_2_SHIFT[] = {{7,4,'@'},STOP};
-struct key K_3[] = {{10,4,'3'},STOP};
-struct key K_3_SHIFT[] = {{10,4,'#'},STOP};
-struct key K_4[] = {{13,4,'4'},STOP};
-struct key K_4_SHIFT[] = {{13,4,'$'},STOP};
-struct key K_5[] = {{16,4,'5'},STOP};
-struct key K_5_SHIFT[] = {{16,4,'%'},STOP};
-struct key K_6[] = {{19,4,'6'},STOP};
-struct key K_6_SHIFT[] = {{19,4,'^'},STOP};
-struct key K_7[] = {{22,4,'7'},STOP};
-struct key K_7_SHIFT[] = {{22,4,'&'},STOP};
-struct key K_8[] = {{25,4,'8'},STOP};
-struct key K_8_SHIFT[] = {{25,4,'*'},STOP};
-struct key K_9[] = {{28,4,'9'},STOP};
-struct key K_9_SHIFT[] = {{28,4,'('},STOP};
-struct key K_0[] = {{31,4,'0'},STOP};
-struct key K_0_SHIFT[] = {{31,4,')'},STOP};
-struct key K_MINUS[] = {{34,4,'-'},STOP};
-struct key K_MINUS_SHIFT[] = {{34,4,'_'},STOP};
-struct key K_EQUALS[] = {{37,4,'='},STOP};
-struct key K_EQUALS_SHIFT[] = {{37,4,'+'},STOP};
-struct key K_BACKSLASH[] = {{40,4,'\\'},STOP};
-struct key K_BACKSLASH_SHIFT[] = {{40,4,'|'},STOP};
-struct key K_BACKSPACE[] = {{44,4,0x2190},{45,4,0x2500},{46,4,0x2500},STOP};
-struct key K_INS[] = {{50,4,'I'},{51,4,'N'},{52,4,'S'},STOP};
-struct key K_HOM[] = {{54,4,'H'},{55,4,'O'},{56,4,'M'},STOP};
-struct key K_PGU[] = {{58,4,'P'},{59,4,'G'},{60,4,'U'},STOP};
-struct key K_K_NUMLOCK[] = {{65,4,'N'},STOP};
-struct key K_K_SLASH[] = {{68,4,'/'},STOP};
-struct key K_K_STAR[] = {{71,4,'*'},STOP};
-struct key K_K_MINUS[] = {{74,4,'-'},STOP};
-
-struct key K_TAB[] = {{1,6,'T'},{2,6,'A'},{3,6,'B'},STOP};
-struct key K_q[] = {{6,6,'q'},STOP};
-struct key K_Q[] = {{6,6,'Q'},STOP};
-struct key K_w[] = {{9,6,'w'},STOP};
-struct key K_W[] = {{9,6,'W'},STOP};
-struct key K_e[] = {{12,6,'e'},STOP};
-struct key K_E[] = {{12,6,'E'},STOP};
-struct key K_r[] = {{15,6,'r'},STOP};
-struct key K_R[] = {{15,6,'R'},STOP};
-struct key K_t[] = {{18,6,'t'},STOP};
-struct key K_T[] = {{18,6,'T'},STOP};
-struct key K_y[] = {{21,6,'y'},STOP};
-struct key K_Y[] = {{21,6,'Y'},STOP};
-struct key K_u[] = {{24,6,'u'},STOP};
-struct key K_U[] = {{24,6,'U'},STOP};
-struct key K_i[] = {{27,6,'i'},STOP};
-struct key K_I[] = {{27,6,'I'},STOP};
-struct key K_o[] = {{30,6,'o'},STOP};
-struct key K_O[] = {{30,6,'O'},STOP};
-struct key K_p[] = {{33,6,'p'},STOP};
-struct key K_P[] = {{33,6,'P'},STOP};
-struct key K_LSQB[] = {{36,6,'['},STOP};
-struct key K_LCUB[] = {{36,6,'{'},STOP};
-struct key K_RSQB[] = {{39,6,']'},STOP};
-struct key K_RCUB[] = {{39,6,'}'},STOP};
-struct key K_ENTER[] = {
- {43,6,0x2591},{44,6,0x2591},{45,6,0x2591},{46,6,0x2591},
- {43,7,0x2591},{44,7,0x2591},{45,7,0x21B5},{46,7,0x2591},
- {41,8,0x2591},{42,8,0x2591},{43,8,0x2591},{44,8,0x2591},
- {45,8,0x2591},{46,8,0x2591},STOP
-};
-struct key K_DEL[] = {{50,6,'D'},{51,6,'E'},{52,6,'L'},STOP};
-struct key K_END[] = {{54,6,'E'},{55,6,'N'},{56,6,'D'},STOP};
-struct key K_PGD[] = {{58,6,'P'},{59,6,'G'},{60,6,'D'},STOP};
-struct key K_K_7[] = {{65,6,'7'},STOP};
-struct key K_K_8[] = {{68,6,'8'},STOP};
-struct key K_K_9[] = {{71,6,'9'},STOP};
-struct key K_K_PLUS[] = {{74,6,' '},{74,7,'+'},{74,8,' '},STOP};
-
-struct key K_CAPS[] = {{1,8,'C'},{2,8,'A'},{3,8,'P'},{4,8,'S'},STOP};
-struct key K_a[] = {{7,8,'a'},STOP};
-struct key K_A[] = {{7,8,'A'},STOP};
-struct key K_s[] = {{10,8,'s'},STOP};
-struct key K_S[] = {{10,8,'S'},STOP};
-struct key K_d[] = {{13,8,'d'},STOP};
-struct key K_D[] = {{13,8,'D'},STOP};
-struct key K_f[] = {{16,8,'f'},STOP};
-struct key K_F[] = {{16,8,'F'},STOP};
-struct key K_g[] = {{19,8,'g'},STOP};
-struct key K_G[] = {{19,8,'G'},STOP};
-struct key K_h[] = {{22,8,'h'},STOP};
-struct key K_H[] = {{22,8,'H'},STOP};
-struct key K_j[] = {{25,8,'j'},STOP};
-struct key K_J[] = {{25,8,'J'},STOP};
-struct key K_k[] = {{28,8,'k'},STOP};
-struct key K_K[] = {{28,8,'K'},STOP};
-struct key K_l[] = {{31,8,'l'},STOP};
-struct key K_L[] = {{31,8,'L'},STOP};
-struct key K_SEMICOLON[] = {{34,8,';'},STOP};
-struct key K_PARENTHESIS[] = {{34,8,':'},STOP};
-struct key K_QUOTE[] = {{37,8,'\''},STOP};
-struct key K_DOUBLEQUOTE[] = {{37,8,'"'},STOP};
-struct key K_K_4[] = {{65,8,'4'},STOP};
-struct key K_K_5[] = {{68,8,'5'},STOP};
-struct key K_K_6[] = {{71,8,'6'},STOP};
-
-struct key K_LSHIFT[] = {{1,10,'S'},{2,10,'H'},{3,10,'I'},{4,10,'F'},{5,10,'T'},STOP};
-struct key K_z[] = {{9,10,'z'},STOP};
-struct key K_Z[] = {{9,10,'Z'},STOP};
-struct key K_x[] = {{12,10,'x'},STOP};
-struct key K_X[] = {{12,10,'X'},STOP};
-struct key K_c[] = {{15,10,'c'},STOP};
-struct key K_C[] = {{15,10,'C'},STOP};
-struct key K_v[] = {{18,10,'v'},STOP};
-struct key K_V[] = {{18,10,'V'},STOP};
-struct key K_b[] = {{21,10,'b'},STOP};
-struct key K_B[] = {{21,10,'B'},STOP};
-struct key K_n[] = {{24,10,'n'},STOP};
-struct key K_N[] = {{24,10,'N'},STOP};
-struct key K_m[] = {{27,10,'m'},STOP};
-struct key K_M[] = {{27,10,'M'},STOP};
-struct key K_COMMA[] = {{30,10,','},STOP};
-struct key K_LANB[] = {{30,10,'<'},STOP};
-struct key K_PERIOD[] = {{33,10,'.'},STOP};
-struct key K_RANB[] = {{33,10,'>'},STOP};
-struct key K_SLASH[] = {{36,10,'/'},STOP};
-struct key K_QUESTION[] = {{36,10,'?'},STOP};
-struct key K_RSHIFT[] = {{42,10,'S'},{43,10,'H'},{44,10,'I'},{45,10,'F'},{46,10,'T'},STOP};
-struct key K_ARROW_UP[] = {{54,10,'('},{55,10,0x2191},{56,10,')'},STOP};
-struct key K_K_1[] = {{65,10,'1'},STOP};
-struct key K_K_2[] = {{68,10,'2'},STOP};
-struct key K_K_3[] = {{71,10,'3'},STOP};
-struct key K_K_ENTER[] = {{74,10,0x2591},{74,11,0x2591},{74,12,0x2591},STOP};
-
-struct key K_LCTRL[] = {{1,12,'C'},{2,12,'T'},{3,12,'R'},{4,12,'L'},STOP};
-struct key K_LWIN[] = {{6,12,'W'},{7,12,'I'},{8,12,'N'},STOP};
-struct key K_LALT[] = {{10,12,'A'},{11,12,'L'},{12,12,'T'},STOP};
-struct key K_SPACE[] = {
- {14,12,' '},{15,12,' '},{16,12,' '},{17,12,' '},{18,12,' '},
- {19,12,'S'},{20,12,'P'},{21,12,'A'},{22,12,'C'},{23,12,'E'},
- {24,12,' '},{25,12,' '},{26,12,' '},{27,12,' '},{28,12,' '},
- STOP
-};
-struct key K_RALT[] = {{30,12,'A'},{31,12,'L'},{32,12,'T'},STOP};
-struct key K_RWIN[] = {{34,12,'W'},{35,12,'I'},{36,12,'N'},STOP};
-struct key K_RPROP[] = {{38,12,'P'},{39,12,'R'},{40,12,'O'},{41,12,'P'},STOP};
-struct key K_RCTRL[] = {{43,12,'C'},{44,12,'T'},{45,12,'R'},{46,12,'L'},STOP};
-struct key K_ARROW_LEFT[] = {{50,12,'('},{51,12,0x2190},{52,12,')'},STOP};
-struct key K_ARROW_DOWN[] = {{54,12,'('},{55,12,0x2193},{56,12,')'},STOP};
-struct key K_ARROW_RIGHT[] = {{58,12,'('},{59,12,0x2192},{60,12,')'},STOP};
-struct key K_K_0[] = {{65,12,' '},{66,12,'0'},{67,12,' '},{68,12,' '},STOP};
-struct key K_K_PERIOD[] = {{71,12,'.'},STOP};
-
-struct combo {
- struct key *keys[6];
-};
-
-struct combo combos[] = {
- {{K_TILDE, K_2, K_LCTRL, K_RCTRL, 0}},
- {{K_A, K_LCTRL, K_RCTRL, 0}},
- {{K_B, K_LCTRL, K_RCTRL, 0}},
- {{K_C, K_LCTRL, K_RCTRL, 0}},
- {{K_D, K_LCTRL, K_RCTRL, 0}},
- {{K_E, K_LCTRL, K_RCTRL, 0}},
- {{K_F, K_LCTRL, K_RCTRL, 0}},
- {{K_G, K_LCTRL, K_RCTRL, 0}},
- {{K_H, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}},
- {{K_I, K_TAB, K_LCTRL, K_RCTRL, 0}},
- {{K_J, K_LCTRL, K_RCTRL, 0}},
- {{K_K, K_LCTRL, K_RCTRL, 0}},
- {{K_L, K_LCTRL, K_RCTRL, 0}},
- {{K_M, K_ENTER, K_K_ENTER, K_LCTRL, K_RCTRL, 0}},
- {{K_N, K_LCTRL, K_RCTRL, 0}},
- {{K_O, K_LCTRL, K_RCTRL, 0}},
- {{K_P, K_LCTRL, K_RCTRL, 0}},
- {{K_Q, K_LCTRL, K_RCTRL, 0}},
- {{K_R, K_LCTRL, K_RCTRL, 0}},
- {{K_S, K_LCTRL, K_RCTRL, 0}},
- {{K_T, K_LCTRL, K_RCTRL, 0}},
- {{K_U, K_LCTRL, K_RCTRL, 0}},
- {{K_V, K_LCTRL, K_RCTRL, 0}},
- {{K_W, K_LCTRL, K_RCTRL, 0}},
- {{K_X, K_LCTRL, K_RCTRL, 0}},
- {{K_Y, K_LCTRL, K_RCTRL, 0}},
- {{K_Z, K_LCTRL, K_RCTRL, 0}},
- {{K_LSQB, K_ESC, K_3, K_LCTRL, K_RCTRL, 0}},
- {{K_4, K_BACKSLASH, K_LCTRL, K_RCTRL, 0}},
- {{K_RSQB, K_5, K_LCTRL, K_RCTRL, 0}},
- {{K_6, K_LCTRL, K_RCTRL, 0}},
- {{K_7, K_SLASH, K_MINUS_SHIFT, K_LCTRL, K_RCTRL, 0}},
- {{K_SPACE,0}},
- {{K_1_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_DOUBLEQUOTE,K_LSHIFT,K_RSHIFT,0}},
- {{K_3_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_4_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_5_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_7_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_QUOTE,0}},
- {{K_9_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_0_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_8_SHIFT,K_K_STAR,K_LSHIFT,K_RSHIFT,0}},
- {{K_EQUALS_SHIFT,K_K_PLUS,K_LSHIFT,K_RSHIFT,0}},
- {{K_COMMA,0}},
- {{K_MINUS,K_K_MINUS,0}},
- {{K_PERIOD,K_K_PERIOD,0}},
- {{K_SLASH,K_K_SLASH,0}},
- {{K_0,K_K_0,0}},
- {{K_1,K_K_1,0}},
- {{K_2,K_K_2,0}},
- {{K_3,K_K_3,0}},
- {{K_4,K_K_4,0}},
- {{K_5,K_K_5,0}},
- {{K_6,K_K_6,0}},
- {{K_7,K_K_7,0}},
- {{K_8,K_K_8,0}},
- {{K_9,K_K_9,0}},
- {{K_PARENTHESIS,K_LSHIFT,K_RSHIFT,0}},
- {{K_SEMICOLON,0}},
- {{K_LANB,K_LSHIFT,K_RSHIFT,0}},
- {{K_EQUALS,0}},
- {{K_RANB,K_LSHIFT,K_RSHIFT,0}},
- {{K_QUESTION,K_LSHIFT,K_RSHIFT,0}},
- {{K_2_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_A,K_LSHIFT,K_RSHIFT,0}},
- {{K_B,K_LSHIFT,K_RSHIFT,0}},
- {{K_C,K_LSHIFT,K_RSHIFT,0}},
- {{K_D,K_LSHIFT,K_RSHIFT,0}},
- {{K_E,K_LSHIFT,K_RSHIFT,0}},
- {{K_F,K_LSHIFT,K_RSHIFT,0}},
- {{K_G,K_LSHIFT,K_RSHIFT,0}},
- {{K_H,K_LSHIFT,K_RSHIFT,0}},
- {{K_I,K_LSHIFT,K_RSHIFT,0}},
- {{K_J,K_LSHIFT,K_RSHIFT,0}},
- {{K_K,K_LSHIFT,K_RSHIFT,0}},
- {{K_L,K_LSHIFT,K_RSHIFT,0}},
- {{K_M,K_LSHIFT,K_RSHIFT,0}},
- {{K_N,K_LSHIFT,K_RSHIFT,0}},
- {{K_O,K_LSHIFT,K_RSHIFT,0}},
- {{K_P,K_LSHIFT,K_RSHIFT,0}},
- {{K_Q,K_LSHIFT,K_RSHIFT,0}},
- {{K_R,K_LSHIFT,K_RSHIFT,0}},
- {{K_S,K_LSHIFT,K_RSHIFT,0}},
- {{K_T,K_LSHIFT,K_RSHIFT,0}},
- {{K_U,K_LSHIFT,K_RSHIFT,0}},
- {{K_V,K_LSHIFT,K_RSHIFT,0}},
- {{K_W,K_LSHIFT,K_RSHIFT,0}},
- {{K_X,K_LSHIFT,K_RSHIFT,0}},
- {{K_Y,K_LSHIFT,K_RSHIFT,0}},
- {{K_Z,K_LSHIFT,K_RSHIFT,0}},
- {{K_LSQB,0}},
- {{K_BACKSLASH,0}},
- {{K_RSQB,0}},
- {{K_6_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_MINUS_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_TILDE,0}},
- {{K_a,0}},
- {{K_b,0}},
- {{K_c,0}},
- {{K_d,0}},
- {{K_e,0}},
- {{K_f,0}},
- {{K_g,0}},
- {{K_h,0}},
- {{K_i,0}},
- {{K_j,0}},
- {{K_k,0}},
- {{K_l,0}},
- {{K_m,0}},
- {{K_n,0}},
- {{K_o,0}},
- {{K_p,0}},
- {{K_q,0}},
- {{K_r,0}},
- {{K_s,0}},
- {{K_t,0}},
- {{K_u,0}},
- {{K_v,0}},
- {{K_w,0}},
- {{K_x,0}},
- {{K_y,0}},
- {{K_z,0}},
- {{K_LCUB,K_LSHIFT,K_RSHIFT,0}},
- {{K_BACKSLASH_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_RCUB,K_LSHIFT,K_RSHIFT,0}},
- {{K_TILDE_SHIFT,K_LSHIFT,K_RSHIFT,0}},
- {{K_8, K_BACKSPACE, K_LCTRL, K_RCTRL, 0}}
-};
-
-struct combo func_combos[] = {
- {{K_F1,0}},
- {{K_F2,0}},
- {{K_F3,0}},
- {{K_F4,0}},
- {{K_F5,0}},
- {{K_F6,0}},
- {{K_F7,0}},
- {{K_F8,0}},
- {{K_F9,0}},
- {{K_F10,0}},
- {{K_F11,0}},
- {{K_F12,0}},
- {{K_INS,0}},
- {{K_DEL,0}},
- {{K_HOM,0}},
- {{K_END,0}},
- {{K_PGU,0}},
- {{K_PGD,0}},
- {{K_ARROW_UP,0}},
- {{K_ARROW_DOWN,0}},
- {{K_ARROW_LEFT,0}},
- {{K_ARROW_RIGHT,0}}
-};
-
-void print_tb(const char *str, int x, int y, uint16_t fg, uint16_t bg)
-{
- while (*str) {
- uint32_t uni;
- str += tb_utf8_char_to_unicode(&uni, str);
- tb_set_cell(x, y, uni, fg, bg);
- x++;
- }
-}
-
-void printf_tb(int x, int y, uint16_t fg, uint16_t bg, const char *fmt, ...)
-{
- char buf[4096];
- va_list vl;
- va_start(vl, fmt);
- vsnprintf(buf, sizeof(buf), fmt, vl);
- va_end(vl);
- print_tb(buf, x, y, fg, bg);
-}
-
-void draw_key(struct key *k, uint16_t fg, uint16_t bg)
-{
- while (k->x) {
- tb_set_cell(k->x+2, k->y+4, k->ch, fg, bg);
- k++;
- }
-}
-
-void draw_keyboard(void)
-{
- int i;
- tb_set_cell(0, 0, 0x250C, TB_WHITE, TB_DEFAULT);
- tb_set_cell(79, 0, 0x2510, TB_WHITE, TB_DEFAULT);
- tb_set_cell(0, 23, 0x2514, TB_WHITE, TB_DEFAULT);
- tb_set_cell(79, 23, 0x2518, TB_WHITE, TB_DEFAULT);
-
- for (i = 1; i < 79; ++i) {
- tb_set_cell(i, 0, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_set_cell(i, 23, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_set_cell(i, 17, 0x2500, TB_WHITE, TB_DEFAULT);
- tb_set_cell(i, 4, 0x2500, TB_WHITE, TB_DEFAULT);
- }
- for (i = 1; i < 23; ++i) {
- tb_set_cell(0, i, 0x2502, TB_WHITE, TB_DEFAULT);
- tb_set_cell(79, i, 0x2502, TB_WHITE, TB_DEFAULT);
- }
- tb_set_cell(0, 17, 0x251C, TB_WHITE, TB_DEFAULT);
- tb_set_cell(79, 17, 0x2524, TB_WHITE, TB_DEFAULT);
- tb_set_cell(0, 4, 0x251C, TB_WHITE, TB_DEFAULT);
- tb_set_cell(79, 4, 0x2524, TB_WHITE, TB_DEFAULT);
- for (i = 5; i < 17; ++i) {
- tb_set_cell(1, i, 0x2588, TB_YELLOW, TB_YELLOW);
- tb_set_cell(78, i, 0x2588, TB_YELLOW, TB_YELLOW);
- }
-
- draw_key(K_ESC, TB_WHITE, TB_BLUE);
- draw_key(K_F1, TB_WHITE, TB_BLUE);
- draw_key(K_F2, TB_WHITE, TB_BLUE);
- draw_key(K_F3, TB_WHITE, TB_BLUE);
- draw_key(K_F4, TB_WHITE, TB_BLUE);
- draw_key(K_F5, TB_WHITE, TB_BLUE);
- draw_key(K_F6, TB_WHITE, TB_BLUE);
- draw_key(K_F7, TB_WHITE, TB_BLUE);
- draw_key(K_F8, TB_WHITE, TB_BLUE);
- draw_key(K_F9, TB_WHITE, TB_BLUE);
- draw_key(K_F10, TB_WHITE, TB_BLUE);
- draw_key(K_F11, TB_WHITE, TB_BLUE);
- draw_key(K_F12, TB_WHITE, TB_BLUE);
- draw_key(K_PRN, TB_WHITE, TB_BLUE);
- draw_key(K_SCR, TB_WHITE, TB_BLUE);
- draw_key(K_BRK, TB_WHITE, TB_BLUE);
- draw_key(K_LED1, TB_WHITE, TB_BLUE);
- draw_key(K_LED2, TB_WHITE, TB_BLUE);
- draw_key(K_LED3, TB_WHITE, TB_BLUE);
-
- draw_key(K_TILDE, TB_WHITE, TB_BLUE);
- draw_key(K_1, TB_WHITE, TB_BLUE);
- draw_key(K_2, TB_WHITE, TB_BLUE);
- draw_key(K_3, TB_WHITE, TB_BLUE);
- draw_key(K_4, TB_WHITE, TB_BLUE);
- draw_key(K_5, TB_WHITE, TB_BLUE);
- draw_key(K_6, TB_WHITE, TB_BLUE);
- draw_key(K_7, TB_WHITE, TB_BLUE);
- draw_key(K_8, TB_WHITE, TB_BLUE);
- draw_key(K_9, TB_WHITE, TB_BLUE);
- draw_key(K_0, TB_WHITE, TB_BLUE);
- draw_key(K_MINUS, TB_WHITE, TB_BLUE);
- draw_key(K_EQUALS, TB_WHITE, TB_BLUE);
- draw_key(K_BACKSLASH, TB_WHITE, TB_BLUE);
- draw_key(K_BACKSPACE, TB_WHITE, TB_BLUE);
- draw_key(K_INS, TB_WHITE, TB_BLUE);
- draw_key(K_HOM, TB_WHITE, TB_BLUE);
- draw_key(K_PGU, TB_WHITE, TB_BLUE);
- draw_key(K_K_NUMLOCK, TB_WHITE, TB_BLUE);
- draw_key(K_K_SLASH, TB_WHITE, TB_BLUE);
- draw_key(K_K_STAR, TB_WHITE, TB_BLUE);
- draw_key(K_K_MINUS, TB_WHITE, TB_BLUE);
-
- draw_key(K_TAB, TB_WHITE, TB_BLUE);
- draw_key(K_q, TB_WHITE, TB_BLUE);
- draw_key(K_w, TB_WHITE, TB_BLUE);
- draw_key(K_e, TB_WHITE, TB_BLUE);
- draw_key(K_r, TB_WHITE, TB_BLUE);
- draw_key(K_t, TB_WHITE, TB_BLUE);
- draw_key(K_y, TB_WHITE, TB_BLUE);
- draw_key(K_u, TB_WHITE, TB_BLUE);
- draw_key(K_i, TB_WHITE, TB_BLUE);
- draw_key(K_o, TB_WHITE, TB_BLUE);
- draw_key(K_p, TB_WHITE, TB_BLUE);
- draw_key(K_LSQB, TB_WHITE, TB_BLUE);
- draw_key(K_RSQB, TB_WHITE, TB_BLUE);
- draw_key(K_ENTER, TB_WHITE, TB_BLUE);
- draw_key(K_DEL, TB_WHITE, TB_BLUE);
- draw_key(K_END, TB_WHITE, TB_BLUE);
- draw_key(K_PGD, TB_WHITE, TB_BLUE);
- draw_key(K_K_7, TB_WHITE, TB_BLUE);
- draw_key(K_K_8, TB_WHITE, TB_BLUE);
- draw_key(K_K_9, TB_WHITE, TB_BLUE);
- draw_key(K_K_PLUS, TB_WHITE, TB_BLUE);
-
- draw_key(K_CAPS, TB_WHITE, TB_BLUE);
- draw_key(K_a, TB_WHITE, TB_BLUE);
- draw_key(K_s, TB_WHITE, TB_BLUE);
- draw_key(K_d, TB_WHITE, TB_BLUE);
- draw_key(K_f, TB_WHITE, TB_BLUE);
- draw_key(K_g, TB_WHITE, TB_BLUE);
- draw_key(K_h, TB_WHITE, TB_BLUE);
- draw_key(K_j, TB_WHITE, TB_BLUE);
- draw_key(K_k, TB_WHITE, TB_BLUE);
- draw_key(K_l, TB_WHITE, TB_BLUE);
- draw_key(K_SEMICOLON, TB_WHITE, TB_BLUE);
- draw_key(K_QUOTE, TB_WHITE, TB_BLUE);
- draw_key(K_K_4, TB_WHITE, TB_BLUE);
- draw_key(K_K_5, TB_WHITE, TB_BLUE);
- draw_key(K_K_6, TB_WHITE, TB_BLUE);
-
- draw_key(K_LSHIFT, TB_WHITE, TB_BLUE);
- draw_key(K_z, TB_WHITE, TB_BLUE);
- draw_key(K_x, TB_WHITE, TB_BLUE);
- draw_key(K_c, TB_WHITE, TB_BLUE);
- draw_key(K_v, TB_WHITE, TB_BLUE);
- draw_key(K_b, TB_WHITE, TB_BLUE);
- draw_key(K_n, TB_WHITE, TB_BLUE);
- draw_key(K_m, TB_WHITE, TB_BLUE);
- draw_key(K_COMMA, TB_WHITE, TB_BLUE);
- draw_key(K_PERIOD, TB_WHITE, TB_BLUE);
- draw_key(K_SLASH, TB_WHITE, TB_BLUE);
- draw_key(K_RSHIFT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_UP, TB_WHITE, TB_BLUE);
- draw_key(K_K_1, TB_WHITE, TB_BLUE);
- draw_key(K_K_2, TB_WHITE, TB_BLUE);
- draw_key(K_K_3, TB_WHITE, TB_BLUE);
- draw_key(K_K_ENTER, TB_WHITE, TB_BLUE);
-
- draw_key(K_LCTRL, TB_WHITE, TB_BLUE);
- draw_key(K_LWIN, TB_WHITE, TB_BLUE);
- draw_key(K_LALT, TB_WHITE, TB_BLUE);
- draw_key(K_SPACE, TB_WHITE, TB_BLUE);
- draw_key(K_RCTRL, TB_WHITE, TB_BLUE);
- draw_key(K_RPROP, TB_WHITE, TB_BLUE);
- draw_key(K_RWIN, TB_WHITE, TB_BLUE);
- draw_key(K_RALT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_LEFT, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_DOWN, TB_WHITE, TB_BLUE);
- draw_key(K_ARROW_RIGHT, TB_WHITE, TB_BLUE);
- draw_key(K_K_0, TB_WHITE, TB_BLUE);
- draw_key(K_K_PERIOD, TB_WHITE, TB_BLUE);
-
- printf_tb(33, 1, (uint16_t)(TB_MAGENTA | TB_BOLD), TB_DEFAULT, "Keyboard demo!");
- printf_tb(21, 2, TB_MAGENTA, TB_DEFAULT, "(press CTRL+X and then CTRL+Q to exit)");
- printf_tb(15, 3, TB_MAGENTA, TB_DEFAULT, "(press CTRL+X and then CTRL+C to change input mode)");
-
- int inputmode = tb_set_input_mode(0);
- char inputmode_str[64];
-
- if (inputmode & TB_INPUT_ESC)
- sprintf(inputmode_str, "TB_INPUT_ESC");
- if (inputmode & TB_INPUT_ALT)
- sprintf(inputmode_str, "TB_INPUT_ALT");
- if (inputmode & TB_INPUT_MOUSE)
- sprintf(inputmode_str + 12, " | TB_INPUT_MOUSE");
-
- printf_tb(3, 18, TB_WHITE, TB_DEFAULT, "Input mode: %s", inputmode_str);
-}
-
-const char *funckeymap(int k)
-{
- static const char *fcmap[] = {
- "CTRL+2, CTRL+~",
- "CTRL+A",
- "CTRL+B",
- "CTRL+C",
- "CTRL+D",
- "CTRL+E",
- "CTRL+F",
- "CTRL+G",
- "CTRL+H, BACKSPACE",
- "CTRL+I, TAB",
- "CTRL+J",
- "CTRL+K",
- "CTRL+L",
- "CTRL+M, ENTER",
- "CTRL+N",
- "CTRL+O",
- "CTRL+P",
- "CTRL+Q",
- "CTRL+R",
- "CTRL+S",
- "CTRL+T",
- "CTRL+U",
- "CTRL+V",
- "CTRL+W",
- "CTRL+X",
- "CTRL+Y",
- "CTRL+Z",
- "CTRL+3, ESC, CTRL+[",
- "CTRL+4, CTRL+\\",
- "CTRL+5, CTRL+]",
- "CTRL+6",
- "CTRL+7, CTRL+/, CTRL+_",
- "SPACE"
- };
- static const char *fkmap[] = {
- "F1",
- "F2",
- "F3",
- "F4",
- "F5",
- "F6",
- "F7",
- "F8",
- "F9",
- "F10",
- "F11",
- "F12",
- "INSERT",
- "DELETE",
- "HOME",
- "END",
- "PGUP",
- "PGDN",
- "ARROW UP",
- "ARROW DOWN",
- "ARROW LEFT",
- "ARROW RIGHT"
- };
-
- if (k == TB_KEY_CTRL_8)
- return "CTRL+8, BACKSPACE 2"; /* 0x7F */
- else if (k >= TB_KEY_ARROW_RIGHT && k <= 0xFFFF)
- return fkmap[0xFFFF-k];
- else if (k <= TB_KEY_SPACE)
- return fcmap[k];
- return "UNKNOWN";
-}
-
-void pretty_print_press(struct tb_event *ev)
-{
- char buf[7];
- buf[tb_utf8_unicode_to_char(buf, ev->ch)] = '\0';
- printf_tb(3, 19, TB_WHITE , TB_DEFAULT, "Key: ");
- printf_tb(8, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->key);
- printf_tb(8, 20, TB_GREEN , TB_DEFAULT, "hex: 0x%X", ev->key);
- printf_tb(8, 21, TB_CYAN , TB_DEFAULT, "octal: 0%o", ev->key);
- printf_tb(8, 22, TB_RED , TB_DEFAULT, "string: %s", funckeymap(ev->key));
-
- printf_tb(54, 19, TB_WHITE , TB_DEFAULT, "Char: ");
- printf_tb(60, 19, TB_YELLOW, TB_DEFAULT, "decimal: %d", ev->ch);
- printf_tb(60, 20, TB_GREEN , TB_DEFAULT, "hex: 0x%X", ev->ch);
- printf_tb(60, 21, TB_CYAN , TB_DEFAULT, "octal: 0%o", ev->ch);
- printf_tb(60, 22, TB_RED , TB_DEFAULT, "string: %s", buf);
-
- printf_tb(54, 18, TB_WHITE, TB_DEFAULT, "Modifier: %c%c%c%c",
- (ev->mod & TB_MOD_CTRL) ? 'C' : ' ',
- (ev->mod & TB_MOD_ALT) ? 'A' : ' ',
- (ev->mod & TB_MOD_SHIFT) ? 'S' : ' ',
- (ev->mod & TB_MOD_MOTION) ? 'M' : ' ');
-
-}
-
-void pretty_print_resize(struct tb_event *ev)
-{
- printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Resize event: %d x %d", ev->w, ev->h);
-}
-
-int counter = 0;
-
-void pretty_print_mouse(struct tb_event *ev) {
- printf_tb(3, 19, TB_WHITE, TB_DEFAULT, "Mouse event: %d x %d %c", ev->x, ev->y, (ev->mod & TB_MOD_MOTION) ? '*' : ' ');
- char *btn = "";
- switch (ev->key) {
- case TB_KEY_MOUSE_LEFT:
- btn = "MouseLeft: %d";
- break;
- case TB_KEY_MOUSE_MIDDLE:
- btn = "MouseMiddle: %d";
- break;
- case TB_KEY_MOUSE_RIGHT:
- btn = "MouseRight: %d";
- break;
- case TB_KEY_MOUSE_WHEEL_UP:
- btn = "MouseWheelUp: %d";
- break;
- case TB_KEY_MOUSE_WHEEL_DOWN:
- btn = "MouseWheelDown: %d";
- break;
- case TB_KEY_MOUSE_RELEASE:
- btn = "MouseRelease: %d";
- }
- counter++;
- printf_tb(43, 19, TB_WHITE, TB_DEFAULT, "Key: ");
- printf_tb(48, 19, TB_YELLOW, TB_DEFAULT, btn, counter);
-}
-
-void dispatch_press(struct tb_event *ev)
-{
- if (ev->mod & TB_MOD_ALT) {
- draw_key(K_LALT, TB_WHITE, TB_RED);
- draw_key(K_RALT, TB_WHITE, TB_RED);
- }
- if (ev->mod & TB_MOD_CTRL) {
- draw_key(K_LCTRL, TB_WHITE, TB_RED);
- draw_key(K_RCTRL, TB_WHITE, TB_RED);
- }
- if (ev->mod & TB_MOD_SHIFT) {
- draw_key(K_LSHIFT, TB_WHITE, TB_RED);
- draw_key(K_RSHIFT, TB_WHITE, TB_RED);
- }
-
- struct combo *k = 0;
- if (ev->key >= TB_KEY_ARROW_RIGHT)
- k = &func_combos[0xFFFF-ev->key];
- else if (ev->ch < 128) {
- if (ev->ch == 0 && ev->key < 128)
- k = &combos[ev->key];
- else
- k = &combos[ev->ch];
- }
- if (!k)
- return;
-
- struct key **keys = k->keys;
- while (*keys) {
- draw_key(*keys, TB_WHITE, TB_RED);
- keys++;
- }
-}
-
-int main(int argc, char **argv)
-{
- (void) argc; (void) argv;
- int ret;
-
- setlocale(LC_ALL, "");
-
- ret = tb_init();
- if (ret) {
- fprintf(stderr, "tb_init() failed with error code %d\n", ret);
- return 1;
- }
-
- tb_set_input_mode(TB_INPUT_ESC | TB_INPUT_MOUSE);
- struct tb_event ev;
-
- tb_clear();
- draw_keyboard();
- tb_present();
- int inputmode = 0;
- int ctrlxpressed = 0;
-
- while (1) {
- ret = tb_poll_event(&ev);
-
- if (ret != TB_OK) {
- if (ret == TB_ERR_POLL && tb_last_errno() == EINTR) {
- /* poll was interrupted, maybe by a SIGWINCH; try again */
- continue;
- }
- /* some other error occurred; bail */
- break;
- }
-
- switch (ev.type) {
- case TB_EVENT_KEY:
- if (ev.key == TB_KEY_CTRL_Q && ctrlxpressed) {
- tb_shutdown();
- return 0;
- }
- if (ev.key == TB_KEY_CTRL_C && ctrlxpressed) {
- static int chmap[] = {
- TB_INPUT_ESC | TB_INPUT_MOUSE, /* 101 */
- TB_INPUT_ALT | TB_INPUT_MOUSE, /* 110 */
- TB_INPUT_ESC, /* 001 */
- TB_INPUT_ALT, /* 010 */
- };
- inputmode++;
- if (inputmode >= 4) {
- inputmode = 0;
- }
- tb_set_input_mode(chmap[inputmode]);
- }
- if (ev.key == TB_KEY_CTRL_X)
- ctrlxpressed = 1;
- else
- ctrlxpressed = 0;
-
- tb_clear();
- draw_keyboard();
- dispatch_press(&ev);
- pretty_print_press(&ev);
- tb_present();
- break;
- case TB_EVENT_RESIZE:
- tb_clear();
- draw_keyboard();
- pretty_print_resize(&ev);
- tb_present();
- break;
- case TB_EVENT_MOUSE:
- tb_clear();
- draw_keyboard();
- pretty_print_mouse(&ev);
- tb_present();
- break;
- default:
- break;
- }
- }
- tb_shutdown();
- return 0;
-}
diff --git a/gdb_scripts b/gdb_scripts
deleted file mode 100644
index 97acf23..0000000
--- a/gdb_scripts
+++ /dev/null
@@ -1,12 +0,0 @@
-define tbx
- tui ena
- b 126
- continue
- disp SearchIndex
- disp PrevIndex
- disp Text[TextOffset + SearchIndex]
- disp Text + TextOffset + PrevIndex
- disp BreakOffset
- disp YOffset
-end
-
diff --git a/archived.md b/notes/archived.md
index 62def2d..62def2d 100644
--- a/archived.md
+++ b/notes/archived.md
diff --git a/notes/chatty.md b/notes/chatty.md
new file mode 100644
index 0000000..77a3886
--- /dev/null
+++ b/notes/chatty.md
@@ -0,0 +1,76 @@
+# Chatty
+The idea is the following:
+- tcp server that you can send messages to
+- history upon connecting
+- date of messages sent
+- client for reading the messages and sending them at the same time
+- rooms
+- encryption
+- authentication
+
+## client
+- [ ] BUG: text is not appearing after typing
+- [ ] BUG: when connecting two clients of the same account
+- [ ] BUG: wrapping does not work and displays nothing if there is no screen space
+- [ ] BUG: reconnect does not work when server does not know id
+- [ ] TODO: Convert tabs to spaces
+- [ ] BUG: when using lots of markup characters
+- [ ] TODO: Newline support
+ - [ ] resizable box
+
+## server
+- [ ] check that fds arena does not overflow
+ - free clients which disconnected and use free list to give them space
+- [ ] check if when sending and the client is offline (due to connection loss) what happens
+- [ ] timeout on recv?
+- [ ] use threads to handle clients/ timeout when receiving because a client could theoretically
+ stall the entire server.
+- [ ] do not crash on errors from clients
+ - implement error message?
+ - timeout on recv with setsockopt
+- [ ] theoretically two clients can connect at the same time. The uni/bi connections should be
+ negotiated.
+
+## common
+- [ ] use IP address / domain
+- [ ] chat history
+- [ ] rooms
+- [ ] compression
+
+## Protocol
+- see `protocol.h` for more info
+- The null terminator must be sent with the string.
+- The text can be arbitrary length
+
+### To Read
+#### C Programming
+- https://www.youtube.com/watch?v=wvtFGa6XJDU
+- https://nullprogram.com/blog/2023/02/11/
+- https://nullprogram.com/blog/2023/02/13/
+- https://nullprogram.com/blog/2023/10/08/
+#### Encryption w/ Compression
+- https://en.wikipedia.org/wiki/BREACH
+- https://en.wikipedia.org/wiki/CRIME
+- https://crypto.stackexchange.com/questions/2283/crypto-compression-algorithms
+- openpgp https://www.rfc-editor.org/rfc/rfc4880
+- https://security.stackexchange.com/questions/19911/crime-how-to-beat-the-beast-successor
+- https://blog.qualys.com/product-tech/2012/09/14/crime-information-leakage-attack-against-ssltls
+- Algorithms:
+ *Symmetric*
+ - AESI
+ - Blowfish
+ - Twofish
+ - Rivest Cipher (RC4)
+ *Assymetric*
+ - Data Encryption Standard (DES)
+ - ECDSA
+ - RSA
+ - Diffie-Hellman
+ - PGP
+ _Hash_
+ - Deflate
+ - Huffman Coding
+ - LZ77
+ Other
+ - ChaCha20-Poly1305
+ - AES(-GCM)
diff --git a/insert.md b/notes/insert.md
index cb00d1c..cb00d1c 100644
--- a/insert.md
+++ b/notes/insert.md
diff --git a/scroll.md b/notes/scroll.md
index f36e774..f36e774 100644
--- a/scroll.md
+++ b/notes/scroll.md
diff --git a/archived/array.h b/source/archived/array.h
index 04d2f38..04d2f38 100644
--- a/archived/array.h
+++ b/source/archived/array.h
diff --git a/archived/input_box.c b/source/archived/input_box.c
index 466c03d..729af02 100644
--- a/archived/input_box.c
+++ b/source/archived/input_box.c
@@ -17,6 +17,17 @@
#include <locale.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;
+typedef u32 b32;
+
int
main(void)
{
diff --git a/archived/network_compression.c b/source/archived/network_compression.c
index eef9e3c..eef9e3c 100644
--- a/archived/network_compression.c
+++ b/source/archived/network_compression.c
diff --git a/scrollandwrapped.c b/source/archived/scrollandwrapped.c
index 3cd3a42..3cd3a42 100644
--- a/scrollandwrapped.c
+++ b/source/archived/scrollandwrapped.c
diff --git a/send.c b/source/archived/send.c
index 396b337..396b337 100644
--- a/send.c
+++ b/source/archived/send.c
diff --git a/archived/ui_checkmark.c b/source/archived/ui_checkmark.c
index df7e507..df7e507 100644
--- a/archived/ui_checkmark.c
+++ b/source/archived/ui_checkmark.c
diff --git a/source/archived/ui_meter b/source/archived/ui_meter
new file mode 100755
index 0000000..c12b0fe
--- /dev/null
+++ b/source/archived/ui_meter
Binary files differ
diff --git a/archived/ui_meter.c b/source/archived/ui_meter.c
index 8636ba4..b0ee5a0 100644
--- a/archived/ui_meter.c
+++ b/source/archived/ui_meter.c
@@ -1,5 +1,5 @@
#define TB_IMPL
-#include "../chatty/external/termbox2.h"
+#include <termbox2.h>
#include <locale.h>
#include <signal.h>
diff --git a/archived/ui_selection.c b/source/archived/ui_selection.c
index 3e45ee2..3e45ee2 100644
--- a/archived/ui_selection.c
+++ b/source/archived/ui_selection.c
diff --git a/ui_wrapped.c b/source/archived/ui_wrapped.c
index 67ddffe..67ddffe 100644
--- a/ui_wrapped.c
+++ b/source/archived/ui_wrapped.c
diff --git a/archived/utf8toASCII.c b/source/archived/utf8toASCII.c
index 988a69b..988a69b 100644
--- a/archived/utf8toASCII.c
+++ b/source/archived/utf8toASCII.c
diff --git a/archived/v1/README.md b/source/archived/v1/README.md
index 2cf0aee..2cf0aee 100644
--- a/archived/v1/README.md
+++ b/source/archived/v1/README.md
diff --git a/archived/v1/arena.h b/source/archived/v1/arena.h
index 6f371f4..6f371f4 100644
--- a/archived/v1/arena.h
+++ b/source/archived/v1/arena.h
diff --git a/archived/v1/build.sh b/source/archived/v1/build.sh
index a80b193..a80b193 100755
--- a/archived/v1/build.sh
+++ b/source/archived/v1/build.sh
diff --git a/archived/v1/client.c b/source/archived/v1/client.c
index 878b678..878b678 100644
--- a/archived/v1/client.c
+++ b/source/archived/v1/client.c
diff --git a/archived/v1/common.h b/source/archived/v1/common.h
index c7bb0dd..c7bb0dd 100644
--- a/archived/v1/common.h
+++ b/source/archived/v1/common.h
diff --git a/archived/v1/compile_flags.txt b/source/archived/v1/compile_flags.txt
index 1a62790..1a62790 100644
--- a/archived/v1/compile_flags.txt
+++ b/source/archived/v1/compile_flags.txt
diff --git a/archived/v1/recv.c b/source/archived/v1/recv.c
index 042d8a9..042d8a9 100644
--- a/archived/v1/recv.c
+++ b/source/archived/v1/recv.c
diff --git a/archived/v1/send.c b/source/archived/v1/send.c
index 27e9793..27e9793 100644
--- a/archived/v1/send.c
+++ b/source/archived/v1/send.c
diff --git a/archived/v1/server.c b/source/archived/v1/server.c
index 7edf558..7edf558 100644
--- a/archived/v1/server.c
+++ b/source/archived/v1/server.c
diff --git a/archived/v1/termbox2.h b/source/archived/v1/termbox2.h
index 265cdab..265cdab 100644
--- a/archived/v1/termbox2.h
+++ b/source/archived/v1/termbox2.h
diff --git a/wrap.c b/source/archived/wrap.c
index 57506b8..57506b8 100644
--- a/wrap.c
+++ b/source/archived/wrap.c
diff --git a/arena.h b/source/arena.h
index f3d21d7..8571e6c 100644
--- a/arena.h
+++ b/source/arena.h
@@ -2,23 +2,32 @@
#define ARENA_H
#include <sys/mman.h>
+
#include <stdint.h>
-#include "types.h"
+typedef uint8_t A_u8;
+typedef uint16_t A_u16;
+typedef uint32_t A_u32;
+typedef uint64_t A_u64;
+typedef int8_t A_s8;
+typedef int16_t A_s16;
+typedef int32_t A_s32;
+typedef int64_t A_s64;
+typedef A_u32 A_b32;
// Arena Allocator
typedef struct {
void* addr;
- u64 size;
- u64 pos;
+ A_u64 size;
+ A_u64 pos;
} Arena;
#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)
-void ArenaAlloc(Arena* arena, u64 size);
+void ArenaAlloc(Arena* arena, A_u64 size);
void ArenaRelease(Arena* arena);
-void* ArenaPush(Arena* arena, u64 size);
+void* ArenaPush(Arena* arena, A_u64 size);
#endif // ARENA_H
@@ -26,7 +35,7 @@ void* ArenaPush(Arena* arena, u64 size);
// Returns arena in case of success, or 0 if it failed to alllocate the memory
void
-ArenaAlloc(Arena* arena, u64 size)
+ArenaAlloc(Arena* arena, A_u64 size)
{
arena->addr = mmap(0, size, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
Assert(arena->addr != MAP_FAILED);
@@ -41,13 +50,14 @@ ArenaRelease(Arena* arena)
}
void*
-ArenaPush(Arena* arena, u64 size)
+ArenaPush(Arena* arena, A_u64 size)
{
- u8* mem;
- mem = (u8*)arena->addr + arena->pos;
+ A_u8* mem;
+ mem = (A_u8*)arena->addr + arena->pos;
arena->pos += size;
Assert(arena->pos <= arena->size);
return mem;
}
+#undef ARENA_IMPL
#endif // ARENA_IMPL
diff --git a/source/build.sh b/source/build.sh
new file mode 100755
index 0000000..317e9a4
--- /dev/null
+++ b/source/build.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ScriptDir="$(dirname "$(readlink -f "$0")")"
+cd "$ScriptDir"
+BuildDir="$ScriptDir"/../build
+
+mkdir -p "$BuildDir"
+printf 'chatty.c\n'
+gcc -DDEBUG -ggdb -Wall -pedantic -std=c11 -I external -o "$BuildDir"/chatty chatty.c
+printf 'server.c\n'
+gcc -DDEBUG -ggdb -Wall -pedantic -std=c99 -o "$BuildDir"/server server.c
+
+# printf 'archived/input_box.c\n'
+# gcc -DDEBUG -ggdb -Wall -pedantic -std=c11 -I external -I . -o "$BuildDir"/input_box archived/input_box.c
diff --git a/chatty.c b/source/chatty.c
index 3300ce0..32a4431 100644
--- a/chatty.c
+++ b/source/chatty.c
@@ -1,7 +1,8 @@
#define TB_IMPL
-#include "external/termbox2.h"
+#include "termbox2.h"
#undef TB_IMPL
+
#include <arpa/inet.h>
#include <locale.h>
#include <poll.h>
@@ -34,16 +35,19 @@
#endif // DEBUG
#endif // Assert
+
+#define ARENA_IMPL
+#include "arena.h"
+
#define CHATTY_IMPL
#include "chatty.h"
+
#include "protocol.h"
#define TEXTBOX_MAX_INPUT MAX_INPUT_LEN
+#define UI_IMPL
#include "ui.h"
-#define ARENA_IMPL
-#include "arena.h"
-
enum { FDS_BI = 0, // for one-way communication with the server (eg. TextMessage)
FDS_UNI, // For two-way communication with the server (eg. IDMessage)
FDS_TTY,
@@ -334,8 +338,7 @@ DisplayChat(Arena* ScratchArena,
global.cursor_x = TextR.X;
global.cursor_y = TextR.Y;
DrawBox(TextBox, 0);
-
- // InputBox(TextBox, Input, InputLen, True);
+ DrawTextBox(TextR, Input, InputLen);
// Print vertical bar
s32 VerticalBarOffset = TIMESTAMP_LEN + AUTHOR_LEN + 2;
diff --git a/chatty.h b/source/chatty.h
index 5891b94..f7525fa 100644
--- a/chatty.h
+++ b/source/chatty.h
@@ -34,7 +34,15 @@
#define global_variable
#define internal static
-#include "types.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;
+typedef u32 b32;
void Loggingf(char* format, ...);
@@ -66,5 +74,5 @@ LoggingF(char* format, ...)
write(LogFD, buf, n);
}
-
+#undef CHATTY_IMPL
#endif // CHATTY_IMPL
diff --git a/protocol.h b/source/protocol.h
index 0c5b04a..f863f0a 100644
--- a/protocol.h
+++ b/source/protocol.h
@@ -2,7 +2,7 @@
#define PROTOCOL_H
#include "arena.h"
-#include "types.h"
+#include "chatty.h"
/// Protocol
// - every message has format Header + Message
diff --git a/server.c b/source/server.c
index a6613d6..a6613d6 100644
--- a/server.c
+++ b/source/server.c
diff --git a/external/termbox2.h b/source/termbox2.h
index 265cdab..30f9ad3 100644
--- a/external/termbox2.h
+++ b/source/termbox2.h
@@ -3514,4 +3514,5 @@ static int bytebuf_free(struct bytebuf_t *b) {
return TB_OK;
}
+#undef TB_IMPL
#endif // TB_IMPL
diff --git a/ui.h b/source/ui.h
index 078a456..83a1ab4 100644
--- a/ui.h
+++ b/source/ui.h
@@ -7,6 +7,17 @@
#include <stdbool.h>
#include "termbox2.h"
#include "arena.h"
+#include "chatty.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;
+typedef u32 b32;
/* Types */
@@ -450,14 +461,21 @@ DrawTextBox(rect TextR, wchar_t *Text, u32 TextLen)
if (At < TextLen)
{
tb_printf(AtX++, AtY, 0, 0, "%lc", Text[At++]);
+ global.cursor_x = AtX;
}
else
{
tb_printf(AtX++, AtY, 0, 0, " ");
}
- if (AtX == TextR.X + TextR.W) { AtY++; AtX = TextR.X; }
+ if (AtX == TextR.X + TextR.W)
+ {
+ AtY++;
+ AtX = TextR.X;
+ global.cursor_y = AtY - 1;
+ }
}
+
}
// NOTE: To ensure that the text looks the same even when scrolling it you must provide the whole text,
@@ -530,6 +548,7 @@ DrawTextBoxWrapped(rect TextR, wchar_t *Text, u32 TextLen)
#endif // UI_H
+#ifdef UI_IMPL
// Check if coordinate (X,Y) is in rect boundaries
bool
IsInRect(rect Rect, s32 X, s32 Y)
@@ -817,3 +836,5 @@ preprocess_markdown(Arena* ScratchArena, wchar_t* Text, u32 Len)
return Result;
}
+
+#endif
diff --git a/tests/a.out b/tests/a.out
new file mode 100755
index 0000000..4e8f3c3
--- /dev/null
+++ b/tests/a.out
Binary files differ
diff --git a/tests/build.sh b/tests/build.sh
new file mode 100755
index 0000000..2bab03e
--- /dev/null
+++ b/tests/build.sh
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+ScriptDir="$(dirname "$(readlink -f "$0")")"
+cd "$ScriptDir"
+WarningFlags="-Wno-unused-variable"
+
+printf 'tests.c\n'
+gcc -ggdb -Wall $WarningFlags -o tests tests.c
+./tests
+
+# printf 'archived/input_box.c\n'
+# gcc -DDEBUG -ggdb -Wall -pedantic -std=c11 -I external -I . -o "$BuildDir"/input_box archived/input_box.c
diff --git a/test.h b/tests/test.h
index febdabd..23fb5cd 100644
--- a/test.h
+++ b/tests/test.h
@@ -43,7 +43,7 @@
#include <stdio.h>
#include <stdbool.h>
-#include "chatty.h"
+#include "../source/chatty.h"
/* Macro's */
#define Expect(expr) if (!(expr)) \
diff --git a/tests/tests b/tests/tests
new file mode 100755
index 0000000..1ee83c7
--- /dev/null
+++ b/tests/tests
Binary files differ
diff --git a/tests.c b/tests/tests.c
index a9a73a1..93ba4cc 100644
--- a/tests.c
+++ b/tests/tests.c
@@ -1,9 +1,8 @@
#define MAX_INPUT_LEN 255
#define TB_IMPL
-#include "external/termbox2.h"
-
-#include "chatty.h"
+#include "../source/termbox2.h"
+#include "../source/chatty.h"
#define TEST_IMPL
#include "test.h"
@@ -15,8 +14,6 @@ DrawingTest(void)
{
struct tb_event ev = {0};
-
-
return true;
}
diff --git a/types.h b/types.h
deleted file mode 100644
index 7bd04c5..0000000
--- a/types.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#ifndef TYPES_H
-#define TYPES_H
-
-#include <stdbool.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;
-
-#endif // TYPES_H
diff --git a/ui_box.h b/ui_box.h
deleted file mode 100644
index c19b9dd..0000000
--- a/ui_box.h
+++ /dev/null
@@ -1,472 +0,0 @@
-#ifndef BOX_H
-#define BOX_H
-
-// assumes MAX_INPUT_LEN to be set
-//
-// #define MAX_INPUT_LEN 128
-
-#define TEXTBOX_PADDING_X 1
-#define TEXTBOX_BORDER_WIDTH 1
-#define TEXTBOX_MIN_WIDTH TEXTBOX_PADDING_X * 2 + TEXTBOX_BORDER_WIDTH * 2 + 1;
-#define TEXTBOXFROMBOX(Box) \
- { \
- .X = Box.X + TEXTBOX_BORDER_WIDTH + TEXTBOX_PADDING_X, \
- .Y = Box.Y + TEXTBOX_BORDER_WIDTH, \
- .W = Box.W - TEXTBOX_BORDER_WIDTH * 2 - TEXTBOX_PADDING_X * 2, \
- .H = Box.H - TEXTBOX_BORDER_WIDTH * 2 \
- }
-
-void DrawBox(rect Rect, box_characters *Chars);
-void ScrollLeft(rect Text, u32 *TextOffset);
-void ScrollRight(rect Text, u32 *TextOffset);
-void TextBoxDelete(wchar_t* Text, u64 Pos);
-void TextBoxInsert(wchar_t *Input, u32 InputPos, u32 InputLen, wchar_t ch);
-u32 TextBoxKeypress(struct tb_event ev, rect TextR, wchar_t *Text, u32 *TextLenPtr, u32 TextPos, u32 *TextOffsetPtr);
-void TextBoxDrawText(rect TextR, wchar_t *Input, u32 InputLen, u32 TextOffset);
-
-// Draw box along boundaries in Rect with optional Chars.
-void
-DrawBox(rect Rect, box_characters *Chars)
-{
- wchar_t ur, ru, rd, dr, lr, ud;
- if (!Chars)
- {
- ur = L'╭';
- ru = L'╯';
- rd = L'╮';
- dr = L'╰';
- lr = L'─';
- ud = L'│';
- }
- else
- {
- ur = Chars->ur;
- ru = Chars->ru;
- rd = Chars->rd;
- dr = Chars->dr;
- lr = Chars->lr;
- ud = Chars->ud;
- }
-
- Rect.H--;
- Rect.W--;
-
- tb_printf(Rect.X, Rect.Y, 0, 0, "%lc", ur);
- for (s32 X = 1; X < Rect.W; X++)
- {
- tb_printf(Rect.X + X, Rect.Y, 0, 0, "%lc", lr);
- }
- tb_printf(Rect.X + Rect.W, Rect.Y, 0, 0, "%lc", rd);
-
- // Draw vertical bars
- for (s32 Y = 1; Y < Rect.H; Y++)
- {
- tb_printf(Rect.X, Rect.Y + Y, 0, 0, "%lc", ud);
- tb_printf(Rect.X + Rect.W, Rect.Y + Y, 0, 0, "%lc", ud);
- }
-
- tb_printf(Rect.X, Rect.Y + Rect.H, 0, 0, "%lc", dr);
- for (s32 X = 1; X < Rect.W; X++)
- {
- tb_printf(Rect.X + X, Rect.Y + Rect.H, 0, 0, "%lc", lr);
- }
- tb_printf(Rect.X + Rect.W, Rect.Y + Rect.H, 0, 0, "%lc", ru);
-}
-
-// SCROLLING
-// ╭──────────╮ ╭──────────╮ Going Left on the first character scrolls up.
-// │ █3 4 │ => │ 1 2█ │ Cursor on end of the top line.
-// │ 5 6 │ │ 3 4 │
-// ╰──────────╯ ╰──────────╯
-//
-// ╭──────────╮ ╭──────────╮ Going Right on the last character scrolls down.
-// │ 1 3 │ => │ 2 4 │ Puts cursor on start of the bottom line.
-// │ 2 4█ │ │ █ │
-// ╰──────────╯ ╰──────────╯
-//
-// ╭──────────╮ ╭──────────╮ Going Down on bottom line scrolls down.
-// │ 1 3 │ => │ 2 4 │ Cursor stays on bottom line.
-// │ 2 █ 4 │ │ █ │
-// ╰──────────╯ ╰──────────╯
-//
-// ╭──────────╮ ╭──────────╮ Going Up on top line scrolls up.
-// │ 3 █ 4 │ => │ 1 █ 2 │ Cursor stays on top line.
-// │ 5 6 │ │ 3 5 │
-// ╰──────────╯ ╰──────────╯
-//
-// In code this translates to changing global.cursor_{x,y} and TextOffset accordingly.
-
-// Scroll one character to the left
-void
-TextBoxScrollLeft(rect Text, u32 *TextOffset)
-{
- // If text is on the first character of the box scroll up
- if (global.cursor_x == Text.X &&
- global.cursor_y == Text.Y)
- {
- global.cursor_x = Text.X + Text.W - 1;
- global.cursor_y = Text.Y;
-
- *TextOffset -= Text.W;
- }
- else
- {
- if (global.cursor_x == Text.X)
- {
- // Got to previous line
- global.cursor_x = Text.X + Text.W - 1;
- global.cursor_y--;
- }
- else
- {
- global.cursor_x--;
- }
- }
-}
-
-// Scroll one character to the right
-void
-TextBoxScrollRight(rect Text, u32 *TextOffset)
-{
- // If cursor is on the last character scroll forwards
- if (global.cursor_x == Text.X + Text.W - 1 &&
- global.cursor_y == Text.Y + Text.H - 1)
- {
- global.cursor_x = Text.X;
- global.cursor_y = Text.Y + Text.H - 1;
-
- *TextOffset += Text.W;
- }
- else
- {
- global.cursor_x++;
- if (global.cursor_x == Text.X + Text.W)
- {
- global.cursor_x = Text.X;
- global.cursor_y++;
- }
- }
-}
-
-
-// Delete a character in Text at Pos
-void
-TextBoxDelete(wchar_t* Text, u64 Pos)
-{
- memmove(Text + Pos,
- Text + Pos + 1,
- (MAX_INPUT_LEN - Pos - 1) * sizeof(*Text));
-}
-
-// Insert a ev.ch in Input at InputPos
-void
-TextBoxInsert(wchar_t *Input, u32 InputPos, u32 InputLen, wchar_t ch)
-{
- if (InputPos < InputLen)
- {
- memmove(Input + InputPos,
- Input + InputPos - 1,
- (InputLen - InputPos + 1) * sizeof(*Input));
- }
- Input[InputPos] = ch;
-}
-
-// Handle the key event and edit Input and updates TextLenPtr and TextOffsetPtr accordingly.
-// InputPos is the position in the Input relating to the cursor position.
-// TextR is the bounding box for the text.
-// Returns 0 no key event was handled
-u32
-TextBoxKeypress(struct tb_event ev, rect TextR,
- wchar_t *Text, u32 *TextLenPtr, u32 TextPos, u32 *TextOffsetPtr)
-{
- u32 Result = 1;
-
- u32 TextLen = *TextLenPtr;
- u32 TextOffset = *TextOffsetPtr;
-
- switch (ev.key)
- {
-
- // Delete character backwards
- case TB_KEY_CTRL_8:
- // case TB_KEY_BACKSPACE2:
- {
- if (TextPos == 0) break;
-
- TextBoxDelete(Text, TextPos - 1);
- TextLen--;
-
- TextBoxScrollLeft(TextR, &TextOffset);
-
- } break;
-
- // Delete character forwards
- case TB_KEY_CTRL_D:
- {
- if (TextPos == TextLen) break;
- TextBoxDelete(Text, TextPos);
- TextLen--;
- // Delete(Text, Position)
- } break;
-
- // Delete word backwards
- case TB_KEY_CTRL_W:
- {
- u32 At = TextPos;
- // Find character to stop on
- while (At && is_whitespace(Text[At - 1])) At--;
- while (At && !is_whitespace(Text[At - 1])) At--;
-
- s32 NDelete = TextPos - At;
- memmove(Text + At, Text + TextPos, (TextLen - TextPos) * sizeof(Text[At]));
- TextLen -= NDelete;
-#ifdef DEBUG
- Text[TextLen] = 0;
-#endif
- // NOTE: this could be calculated at once instead
- while(NDelete--) TextBoxScrollLeft(TextR, &TextOffset);
-
- Assert(IsInRect(TextR, global.cursor_x, global.cursor_y));
-
- } break;
-
- // Delete until start of Text
- case TB_KEY_CTRL_U:
- {
- memmove(Text, Text + TextPos, (TextLen - TextPos) * sizeof(*Text));
- TextLen -= TextPos;
-#ifdef DEBUG
- Text[TextLen] = 0;
-#endif
- global.cursor_x = TextR.X;
- global.cursor_y = TextR.Y;
- TextOffset = 0;
- } break;
-
- // Delete until end of Text
- case TB_KEY_CTRL_K:
- {
- TextLen = TextPos;
- Text[TextPos] = 0;
- } break;
-
- // Move to start of line
- case TB_KEY_CTRL_A: global.cursor_x = TextR.X; break;
-
- // Move to end of line
- case TB_KEY_CTRL_E:
- {
- if (global.cursor_x == TextR.X + TextR.W - 1) break;
-
- if (TextPos + TextR.W > TextLen)
- {
- // Put the cursor on the last character
- global.cursor_x = TextR.X + (TextLen - TextOffset) % TextR.W;
- }
- else
- {
- global.cursor_x = TextR.X + TextR.W - 1;
- }
- } break;
-
- // Move backwards
- case TB_KEY_CTRL_B:
- case TB_KEY_ARROW_LEFT:
- {
- // Move forward by word
- if (ev.mod == TB_MOD_CTRL)
- {
- u32 At = TextPos;
- while(At && is_whitespace(Text[At])) At--;
- while(At && !is_whitespace(Text[At])) At--;
- while(TextPos - At++) TextBoxScrollLeft(TextR, &TextOffset);
- }
- // Move forward by character
- else
- {
- if (TextPos == 0) break;
- TextBoxScrollLeft(TextR, &TextOffset);
- }
- } break;
-
- // Move forwards
- case TB_KEY_CTRL_F:
- case TB_KEY_ARROW_RIGHT:
- {
- // Move forward by word
- if (ev.mod == TB_MOD_CTRL)
- {
- u32 At = TextPos;
- while(At < TextLen && is_whitespace(Text[At])) At++;
- while(At < TextLen && !is_whitespace(Text[At])) At++;
- while(At-- - TextPos) TextBoxScrollRight(TextR, &TextOffset);
- }
- // Move forward by character
- else
- {
- if (TextPos == TextLen) break;
- TextBoxScrollRight(TextR, &TextOffset);
- }
- } break;
-
- // Move up
- case TB_KEY_CTRL_P:
- case TB_KEY_ARROW_UP:
- {
- if (global.cursor_y == TextR.Y)
- {
- if (TextOffset == 0)
- {
- global.cursor_x = TextR.X;
-
- break;
- }
-
- TextOffset -= TextR.W;
- global.cursor_y = TextR.Y;
- }
- else
- {
- global.cursor_y--;
- }
- } break;
-
- // Move down
- case TB_KEY_CTRL_N:
- case TB_KEY_ARROW_DOWN:
- {
- if (TextPos + TextR.W > TextLen)
- {
- // Put the cursor on the last character
- global.cursor_x = TextR.X + (TextLen - TextOffset) % (TextR.W);
- global.cursor_y = TextR.Y + (TextLen - TextOffset) / TextR.W;
-
- // If cursor ended 1 line under the bottom line this means that the text
- // needs to be scrolled.
- if (global.cursor_y == TextR.Y + TextR.H)
- {
- TextOffset += TextR.W;
- global.cursor_y--;
- }
-
- break;
- }
-
- if (global.cursor_y == TextR.Y + TextR.H - 1)
- {
- TextOffset += TextR.W;
- }
- else
- {
- global.cursor_y++;
- }
- } break;
- default:
- {
- Result = 0;
- }
- }
-
- *TextLenPtr = TextLen;
- *TextOffsetPtr = TextOffset;
-
- return Result;
-}
-
-// Draws characters in Input in the TextR rectangle.
-// Skip printing TextOffset amount of characters.
-// InputLen is the amount of characters in Input.
-//
-// NOTE: TextR is always filled, when not enough characters in Input it will uses spaces instead.
-// This makes it easy to update the textbox by recalling this function.
-void
-TextBoxDraw(rect TextR, wchar_t *Text, u32 TextLen)
-{
- // Draw the text right of the cursor
- // NOTE: the cursor is assumed to be in the box
- Assert(IsInRect(TextR, global.cursor_x, global.cursor_y));
- s32 AtX = TextR.X, AtY = TextR.Y;
- u32 At = 0;
- while (AtY < TextR.Y + TextR.H)
- {
- if (At < TextLen)
- {
- tb_printf(AtX++, AtY, 0, 0, "%lc", Text[At++]);
- }
- else
- {
- tb_printf(AtX++, AtY, 0, 0, " ");
- }
-
- if (AtX == TextR.X + TextR.W) { AtY++; AtX = TextR.X; }
- }
-}
-
-// NOTE: To ensure that the text looks the same even when scrolling it you must provide the whole text,
-// wrap the whole text and the only show the portion that can fit in the Text Rectangle.
-
-// When line will exceed width break the word on the next line. This is done by looking backwards
-// for whitespace from TextR.W width. When a whitespace is found text is wrapped on the next line.
-void
-TextBoxDrawWrapped(rect TextR, wchar_t *Text, u32 TextLen)
-{
- if (TextLen <= TextR.W)
- {
- tb_printf(TextR.X, TextR.Y, 0, 0, "%ls", Text);
- tb_present();
- global.cursor_x = TextR.X + TextLen;
- global.cursor_y = TextR.Y;
- return;
- }
-
- u32 SearchIndex = TextR.W;
- u32 PrevIndex = 0;
- u32 Y = TextR.Y;
-
- while (SearchIndex < TextLen)
- {
- while (Text[SearchIndex] != ' ')
- {
- SearchIndex--;
- if (SearchIndex == PrevIndex)
- {
- SearchIndex += TextR.W;
- break;
- }
- }
-
- // Wrap
- wchar_t BreakChar = Text[SearchIndex];
- Text[SearchIndex] = 0;
- tb_printf(TextR.X, Y, 0, 0, "%ls", Text + PrevIndex);
- tb_present();
- Text[SearchIndex] = BreakChar;
-
- if (Y + 1 == TextR.Y + TextR.H)
- {
- global.cursor_y = Y;
- global.cursor_x = TextR.X + (SearchIndex - PrevIndex);
- return;
- }
- Y++;
-
- if (BreakChar == L' ')
- {
- SearchIndex++;
- }
-
- PrevIndex = SearchIndex;
- SearchIndex += TextR.W;
- }
-
- // This happens when SearchIndex exceeds TextLen but there is still some
- // text left to print. We can assume that the text will fit because otherwise it would have
- // been wrapped a second time and the loop would have returned.
- tb_printf(TextR.X, Y, 0, 0, "%ls", Text + PrevIndex);
- // NOTE: this sets the cursor position correctly
-
- global.cursor_y = Y;
- global.cursor_x = TextR.X + TextLen - PrevIndex;
-}
-
-#endif // BOX_H