-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathinterface.c
227 lines (178 loc) · 6.94 KB
/
interface.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#include <unistd.h>
#include <stdlib.h>
#include <ncurses.h>
#include <sys/ioctl.h>
#include <signal.h>
#include "interface.h"
#include "color.h"
#define FIRST_MEM_COL 6
#define FIRST_MEM_ROW 1
static uint16_t memWidth;
static uint16_t memHeight;
static uint16_t codeWidth;
static uint16_t codeHeight;
static WINDOW *codeWin;
static WINDOW *outputWin;
static WINDOW *memWin;
static WINDOW *debugWin;
static WINDOW *inputWin;
static void InitMemoryWindow(void);
static void PrintNewMemoryRow(void);
static uint16_t CalculateMemoryWidth(uint16_t terminalWidth);
void InitInterface(uint16_t outputHeight, char* code) {
char* brainfuckCode;
InitColorPairs();
brainfuckCode = code;
// Get terminal size.
// Resizing is not supported because I prefer not having headaches.
struct winsize terminal;
ioctl(STDOUT_FILENO, TIOCGWINSZ, &terminal);
//* Window configuration:
// (This looks very good with ligatures)
// _________
// | c | m | c = Code.
// |----| | m = Memory.
// | o |----| o = Output.
// |____|_i__| i = Input (not always there).
// |____d____| d = Debug info.
// Define the heights of the windows, which are defined as the height of the terminal
// minus the height of the other windows, minus the height of the seperation lines.
codeHeight = terminal.ws_row - outputHeight - DEBUG_WINDOW_HEIGHT - 1;
memHeight = terminal.ws_row - INPUT_WINDOW_HEIGHT - DEBUG_WINDOW_HEIGHT - 1;
// Define the width of the windows.
// Make sure to leave room for a line between the left and right windows.
memWidth = CalculateMemoryWidth(terminal.ws_col);
codeWidth = terminal.ws_col - memWidth - 1;
// Define the windows.
codeWin = newwin(codeHeight, codeWidth, 0, 0);
outputWin = newwin(outputHeight, codeWidth, codeHeight + 1, 0);
memWin = newwin(memHeight, memWidth, 0, codeWidth + 1);
inputWin = newwin(INPUT_WINDOW_HEIGHT, memWidth, memHeight + 1, codeWidth + 1);
debugWin = newwin(DEBUG_WINDOW_HEIGHT, terminal.ws_col, terminal.ws_row - DEBUG_WINDOW_HEIGHT, 0);
// Print vertical window border.
for(uint16_t i = 0; i < terminal.ws_row; i++) mvaddch(i, codeWidth, ACS_VLINE);
// Print horizontal window borders.
for(uint16_t i = 0; i < codeWidth; i++) mvaddch(codeHeight, i, ACS_HLINE);
for(uint16_t i = codeWidth + 1; i < terminal.ws_col; i++) mvaddch(memHeight, i, ACS_HLINE);
// Print cool connection characters (├ and ┤).
mvaddch(codeHeight, codeWidth, ACS_RTEE);
mvaddch(memHeight, codeWidth, ACS_LTEE);
refresh();
#ifdef TEST_WINDOWS
// Code that fills the windows up with characters so you know which one is where.
for(uint16_t i = 0; i < 10000; i++) wprintw(debugWin, "DEBUG");
for(uint16_t i = 0; i < 10000; i++) wprintw(codeWin, "CODE");
for(uint16_t i = 0; i < 10000; i++) wprintw(outputWin, "OUTPUT");
for(uint16_t i = 0; i < 10000; i++) wprintw(memWin, "MEMORY");
for(uint16_t i = 0; i < 10000; i++) wprintw(inputWin, "INPUT");
#endif
// Refresh all of the windows at the beginning.
wrefresh(codeWin);
wrefresh(memWin);
wrefresh(outputWin);
wrefresh(debugWin);
wrefresh(inputWin);
// The output window needs to be able to scroll.
scrollok(outputWin, 1);
// Print code into the code window.
InitCodeWindow(codeWin, brainfuckCode);
InitDebugWindow(debugWin);
InitMemoryWindow();
}
void EndInterface(void) {
delwin(codeWin);
delwin(memWin);
delwin(outputWin);
delwin(debugWin);
delwin(inputWin);
endwin();
}
static uint16_t memByteCols;
static uint16_t memByteRows = 0;
void UpdateMemory(uint8_t* memory, uint16_t memIndex) {
uint16_t row = memIndex / memByteCols;
uint16_t col = memIndex % memByteCols;
if(row == memByteRows) PrintNewMemoryRow();
// Turn byte cols/rows into text cols/rows.
// Rows are 2 long, top margin is 1 long.
row = row * 2 + FIRST_MEM_ROW;
// Columns are 3 wide, the index is 6 wide.
col = col * 3 + FIRST_MEM_COL;
wattrset(memWin, COLOR_PAIR(MEM_CURSOR_PAIR));
mvwprintw(memWin, row, col, "%02x", memory[memIndex]);
// Overwrite the previous byte if the cursor has moved.
static uint16_t prevRow = FIRST_MEM_ROW;
static uint16_t prevCol = FIRST_MEM_COL;
static uint16_t prevIndex = 0;
if(memIndex != prevIndex) {
wattrset(memWin, COLOR_PAIR(MEM_USED_PAIR));
mvwprintw(memWin, prevRow, prevCol, "%02x", memory[prevIndex]);
prevRow = row;
prevCol = col;
prevIndex = memIndex;
}
wattrset(memWin, 0);
wrefresh(memWin);
}
void InitMemoryWindow(void) {
// I want one column of margin around the memory bytes.
// Each memory byte takes up 3 columns (two hex digits and a space).
// The right margin is automatically added because the space.
// The first byte column (not text column) is dedicated to indeces.
memByteCols = (memWidth - 1 - 5) / 3;
// Print the horizontal index header.
wattrset(memWin, COLOR_PAIR(MEM_INDEX_PAIR) | A_ITALIC);
wmove(memWin, 0, FIRST_MEM_COL);
for(uint16_t i = 0; i < memByteCols; i++) {
wprintw(memWin, "%2x ", (uint8_t)i);
}
PrintNewMemoryRow();
}
void PrintNewMemoryRow(void) {
// Print index
wattrset(memWin, COLOR_PAIR(MEM_INDEX_PAIR) | A_ITALIC);
mvwprintw(memWin, FIRST_MEM_ROW + memByteRows * 2, 1, "%4x ", memByteCols * memByteRows);
wattrset(memWin, 0);
// Print zeroes, which always come in packets of at least 4 bytes.
wattrset(memWin, COLOR_PAIR(MEM_IDLE_PAIR));
for(uint16_t col = 0; col < memByteCols / 4; col++) {
wprintw(memWin, "00 00 00 00 ");
}
wattrset(memWin, 0);
memByteRows++;
wrefresh(memWin);
}
static uint16_t CalculateMemoryWidth(uint16_t terminalWidth) {
uint16_t width = 4;
// Compare the width of the memory to half the width of the terminal,
// and see how many bytes we can fit.
// Every byte takes up 3 columns, one of which is a space, I want a margin of 1 column at the front.
// I want one row of bytes to be indeces, which take up 5 text columns.
while((width * 3 + 1 + 5) < (terminalWidth / 2)) {
width <<= 1;
}
width >>= 1;
return width * 3 + 1 + 5;
}
// ######## The output window ########
// This should probably go into a seperate file, but that is a lot of work, and git blame stops working.
// Output 1 character to the output window.
void OutputChar(char outChar) {
wprintw(outputWin, "%c", outChar);
wrefresh(outputWin);
}
void OutputBackspaceChar(void) {
// wprintw(outputWin, "heh");
if(getcurx(outputWin) == 0) {
mvwaddch(outputWin, getcury(outputWin) - 1, getmaxx(outputWin) - 1, ' ');
wmove(outputWin, getcury(outputWin) - 1, getmaxx(outputWin) - 1);
}
else {
wprintw(outputWin, "\b \b");
}
wrefresh(outputWin);
}
void PrintInfoMessage(char* message, int attribute) {
wattrset(outputWin, attribute);
wprintw(outputWin, "\n%s\n", message);
}