]> gitweb.ps.run Git - iftint/blob - main3.c
main3: basic functionality, array and string
[iftint] / main3.c
1 #include<stdint.h>
2 #include<stdbool.h>
3 #include<stdio.h>
4
5 // Global defines
6
7 #define STR_SIZE 128
8 #define BUF_SIZE 1024*1024
9
10 // Memory
11
12 static char g_memory[1024*1024];
13 static int g_memory_index = 0;
14
15 void * alloc(int num, int size) {
16     void * result = g_memory + g_memory_index;
17     for (int i = 0; i < num*size; i++)
18         g_memory[g_memory_index+i] = 0;
19     g_memory_index += num*size;
20     return result;
21 }
22
23 #define NEW(TYPE) ((TYPE *)alloc(1, sizeof(TYPE)))
24 #define NEWARR(TYPE, NUM) ((TYPE *)alloc(NUM, sizeof(TYPE)))
25
26 // getch
27
28 #ifdef _WIN32
29 #include <windows.h>
30 #include <conio.h>
31 #else
32 #include <sys/ioctl.h>
33 #include <termios.h>
34 #include <unistd.h>
35 #include <stdio.h>
36
37 /* reads from keypress, doesn't echo */
38 int getch(void)
39 {
40     struct termios oldattr, newattr;
41     int ch;
42     tcgetattr( STDIN_FILENO, &oldattr );
43     newattr = oldattr;
44     newattr.c_lflag &= ~( ICANON | ECHO ); // no ECHO for echo(?)
45     tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
46     ch = getchar();
47     tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
48     return ch;
49 }
50
51 /* ungets keypress */
52 void ungetch(int ch)
53 {
54     struct termios oldattr, newattr;
55     tcgetattr( STDIN_FILENO, &oldattr );
56     newattr = oldattr;
57     newattr.c_lflag &= ~( ICANON | ECHO );
58     tcsetattr( STDIN_FILENO, TCSANOW, &newattr );
59     ungetc(ch, stdin);
60     tcsetattr( STDIN_FILENO, TCSANOW, &oldattr );
61 }
62 #endif
63
64 int
65 peekch() {
66     int c = getch();
67     ungetch(c);
68     return c;
69 }
70
71 // VT100
72
73 #define ASCII_ESC 27
74
75 void vt100Escape(const char * str, ...) {
76     va_list args;
77     va_start(args, str);
78
79     printf("%c", ASCII_ESC);
80     vprintf(str, args);
81 }
82
83 void vt100ClearScreen() { vt100Escape("[2J"); }
84 void vt100CursorHome() { vt100Escape("[H"); }
85 void vt100CursorPos(int v, int h) { vt100Escape("[%d;%dH", v, h); }
86 void vt100SaveCursor() { vt100Escape("7"); }
87 void vt100RestoreCursor() { vt100Escape("8"); }
88 void vt100EnableAlternateBuffer() { vt100Escape("[?1049h"); }
89 void vt100DisableAlternateBuffer() { vt100Escape("[?1049l"); }
90 void vt100EnableNegative() { vt100Escape("[7m"); }
91 void vt100DisableNegative() { vt100Escape("[27m"); }
92 void vt100ShowCursor() { vt100Escape("[?25h"); }
93 void vt100HideCursor() { vt100Escape("[?25l"); }
94 void vt100GetScreenSize(int * v, int * h) {
95 #ifdef _WIN32
96     CONSOLE_SCREEN_BUFFER_INFO csbi;
97     GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &csbi);
98     *h = csbi.srWindow.Right - csbi.srWindow.Left + 1;
99     *v = csbi.srWindow.Bottom - csbi.srWindow.Top + 1;
100 #else
101     struct winsize w;
102     ioctl(STDOUT_FILENO, TIOCGWINSZ, &w);
103     *h = w.ws_row;
104     *v = w.ws_col;
105 #endif
106 }
107
108 // Node
109
110 typedef enum {
111     NK_Nul,
112     NK_Arr,
113     NK_Str,
114
115     NK_COUNT
116 } NodeKind;
117
118 typedef struct Node Node;
119 struct Node {
120     NodeKind kind;
121     void * data;
122
123     int childCount;
124     Node *next, *prev, *parent, *child;
125 };
126
127 Node *NodeGetChild(Node *n, unsigned int index) {
128     if (index >= n->childCount)
129         return NULL;
130     
131     Node *result = n->child;
132     for (int i = 0; i < index; i++)
133         result = result->next;
134     return result;
135 }
136
137 void NodeAppend(Node *n1, Node *n2) {
138     if (n1->childCount == 0) {
139         n1->child = n2;
140     }
141     else {
142         Node *lastChild = NodeGetChild(n1, n1->childCount-1);
143
144         lastChild->next = n2;
145         n2->prev = lastChild;
146         n2->next = NULL;
147     }
148     n2->parent = n1;
149     n1->childCount += 1;
150 }
151
152 void NodeInsert(Node *n1, Node *n2, unsigned int index) {
153     if (index >= n1->childCount-1) {
154         NodeAppend(n1, n2);
155         return;
156     }
157
158     Node *insertAfter = NodeGetChild(n1, index);
159     Node *insertBefore = NodeGetChild(n1, index+1);
160
161     insertAfter->next = n2;
162     insertBefore->prev = n2;
163     n2->prev = insertAfter;
164     n2->next = insertBefore;
165
166     n1->childCount += 1;
167 }
168
169 Node *NodeRemove(Node *n1, Node *n2) {
170     if (n1 == NULL)
171         return n2;
172     
173     Node *prev = n2->prev;
174     Node *next = n2->next;
175
176     if (prev != NULL)
177         prev->next = next;
178     if (next != NULL)
179         next->prev = prev;
180     
181     n2->prev = n2->next = n2->parent = NULL;
182
183     if (n2 == n1->child)
184         n1->child = next;
185
186     n1->childCount -= 1;
187     
188     if (prev == NULL)
189         return n1;
190     else
191         return prev;
192 }
193
194 void NodeDraw(Node *n, Node *selected) {
195     static int indent = 0;
196     for (int i = 0; i < indent; i++)
197         printf("  ");
198     
199     if (n == selected) vt100EnableNegative();
200     
201     switch (n->kind) {
202     case NK_Nul: printf("null\n"); break;
203     case NK_Arr: printf("(%d) [\n", n->childCount); for (int i = 0; i < n->childCount; i++) NodeDraw(NodeGetChild(n, i), selected); printf("]\n"); break;
204     case NK_Str: printf("'%.16s'\n", (char *)n->data); break;
205     }
206
207     if (n == selected) vt100DisableNegative();
208 }
209
210 // Input
211
212 #define KEY_CTRL_C 3
213 #define KEY_BACKSPACE1 8
214 #define KEY_BACKSPACE2 127
215
216 typedef enum Input {
217     IN_None,
218
219     IN_A,
220     IN_S,
221     IN_D,
222
223     IN_H,
224     IN_J,
225     IN_K,
226     IN_L,
227
228     IN_SPACE,
229
230     IN_Quit,
231
232     IN_COUNT
233 } Input;
234
235 Input InputGet() {
236     int c;
237
238     while (true) {
239         c = getch();
240         switch (c) {
241             case 'a': return IN_A;
242             case 's': return IN_S;
243             case 'd': return IN_D;
244             
245             case 'h': return IN_H;
246             case 'j': return IN_J;
247             case 'k': return IN_K;
248             case 'l': return IN_L;
249
250             case ' ': return IN_SPACE;
251
252             case 'q': return IN_Quit;
253         }
254     }
255 }
256
257 typedef enum InputAction {
258     IA_None,
259     IA_AppendArray,
260     IA_AppendString,
261     IA_StartEditing,
262     IA_Delete,
263
264     IA_MoveLeft,
265     IA_MoveDown,
266     IA_MoveUp,
267     IA_MoveRight,
268
269     IA_COUNT
270 } InputAction;
271
272 Node *GetNode(InputAction actions[NK_COUNT][IN_COUNT]) {
273     Node * result = NEW(Node);
274     result->kind = NK_Arr;
275
276     Node * n = result;
277
278     Input in;
279     InputAction action;
280
281     enum {
282         Mode_Normal,
283         Mode_Editing,
284     } mode = Mode_Normal;
285
286     while (true) {
287         vt100ClearScreen();
288         vt100CursorHome();
289         NodeDraw(result, n);
290
291         if (mode == Mode_Editing) {
292             int c = getch();
293             char *s = (char*)n->data;
294             int slen = strlen(s);
295             
296             if (c == KEY_BACKSPACE1 || c == KEY_BACKSPACE2) {
297                 s[slen-1] = '\0';
298             }
299             else if (c == 13) {
300                 mode = Mode_Normal;
301             }
302             else if (slen < STR_SIZE) {
303                 s[slen++] = (char)c;
304             }
305         }
306         else if (mode == Mode_Normal) {
307             in = InputGet();
308
309             if (in == IN_Quit)
310                 break;
311
312             action = actions[n->kind][in];
313
314             #define NODE_APPEND(KIND, DATA) Node *newNode = NEW(Node); newNode->kind = KIND; newNode->data = DATA; NodeAppend(n, newNode); n = newNode;
315
316             switch (action) {
317             case IA_AppendArray: { NODE_APPEND(NK_Arr, 0); break; }
318             case IA_AppendString: { NODE_APPEND(NK_Str, NEWARR(char, STR_SIZE)); mode = Mode_Editing; break; }
319             case IA_StartEditing: { mode = Mode_Editing; break; }
320             case IA_Delete: { n = NodeRemove(n->parent, n); break; }
321
322             case IA_MoveLeft: { if (n->prev != NULL) n = n->prev; break; }
323             case IA_MoveDown: { if (n->child != NULL) n = n->child; break; }
324             case IA_MoveUp: { if (n->parent != NULL) n = n->parent; break; }
325             case IA_MoveRight: { if (n->next != NULL) n = n->next; break; }
326             }
327
328             #undef NODE_APPEND
329         }
330     }
331
332     return result;
333 }
334
335
336 int main() {
337     // Setup
338     InputAction actions[NK_COUNT][IN_COUNT];
339
340     actions[NK_Arr][IN_A] = IA_AppendArray;
341     actions[NK_Arr][IN_S] = IA_AppendString;
342     actions[NK_Arr][IN_D] = IA_Delete;
343     actions[NK_Arr][IN_H] = IA_MoveLeft;
344     actions[NK_Arr][IN_J] = IA_MoveDown;
345     actions[NK_Arr][IN_K] = IA_MoveUp;
346     actions[NK_Arr][IN_L] = IA_MoveRight;
347
348     actions[NK_Str][IN_SPACE] = IA_StartEditing;
349     actions[NK_Str][IN_D] = IA_Delete;
350     actions[NK_Str][IN_H] = IA_MoveLeft;
351     actions[NK_Str][IN_J] = IA_MoveDown;
352     actions[NK_Str][IN_K] = IA_MoveUp;
353     actions[NK_Str][IN_L] = IA_MoveRight;
354
355     // Main
356     vt100EnableAlternateBuffer();
357     vt100HideCursor();
358
359     Node *n = GetNode(actions);
360
361     vt100DisableAlternateBuffer();
362     vt100ShowCursor();
363
364     int v, h;
365     vt100GetScreenSize(&v, &h);
366     printf("Screen Size: %d | %d\n", v, h);
367
368     NodeDraw(n, NULL);
369
370     return 0;
371 }