Skip to content

Commit b05da87

Browse files
Quick addition of generalized keyboard commands (#59)
* More generalized keyboard commands and their respective tests * Remove macro and move all previous usages to use to get clang-format to shut up * Implement changes suggested by Trevor
1 parent 9618ca6 commit b05da87

File tree

3 files changed

+389
-0
lines changed

3 files changed

+389
-0
lines changed

tests/expected/test_kb_cmd.expect

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
loop test starting
2+
loop test completed
3+
nesting test starting
4+
nesting test completed
5+
char table test starting
6+
char table test completed
7+
word test starting
8+
word test completed
9+
complex word test starting
10+
complex word test completed
11+
test_kb_cmd done

tests/src/kb_cmd.h

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
#ifndef KB_CMD_H
2+
#define KB_CMD_H
3+
4+
#include "device/keyboard.h"
5+
#include "device/ps2.h"
6+
#include "test_helper.h"
7+
8+
// Returns 0 if executed successfully, non-zero otherwise
9+
typedef int (*CmdFunc)(void);
10+
11+
struct LoopNode {
12+
int idx;
13+
int remaining;
14+
};
15+
16+
// if you have more than 8 nested loops then what the fuck
17+
static struct LoopNode loopStack[8];
18+
static int current = -1;
19+
20+
typedef enum {
21+
// A singular key press
22+
CMD_KEY_PRESS,
23+
24+
// Types the entire provided word
25+
CMD_TYPE_WORD,
26+
27+
// Signifies the start of a command loop
28+
CMD_LOOP_START,
29+
30+
// Signifies the end of the most recent loop command
31+
CMD_LOOP_END,
32+
33+
// Runs a function
34+
CMD_FUNC,
35+
36+
// End of command list
37+
CMD_END
38+
} CMD;
39+
40+
struct KbCmd {
41+
CMD cmd;
42+
union {
43+
// key to press
44+
struct PS2Buf_t kb;
45+
46+
// null terminated string
47+
char *w;
48+
49+
// loop count
50+
int loops;
51+
52+
// function to execute
53+
CmdFunc func;
54+
} data;
55+
};
56+
57+
struct KeyData {
58+
enum KeyCode c;
59+
uint8_t mods;
60+
};
61+
62+
#define keyDataStruct(code, modifiers) \
63+
(struct KeyData) { \
64+
code, modifiers \
65+
}
66+
67+
#define keyAndShifted(unshiftedChar, shiftedChar, key) \
68+
[unshiftedChar] = keyDataStruct(key, 0), [shiftedChar] = keyDataStruct( \
69+
key, KEY_MOD_SHIFT)
70+
71+
// clang-format off
72+
static const struct KeyData charToPressCMD[] = {
73+
// Row 1
74+
keyAndShifted('`', '~', Key_grave),
75+
keyAndShifted('1', '!', Key_1),
76+
keyAndShifted('2', '@', Key_2),
77+
keyAndShifted('3', '#', Key_3),
78+
keyAndShifted('4', '$', Key_4),
79+
keyAndShifted('5', '%', Key_5),
80+
keyAndShifted('6', '^', Key_6),
81+
keyAndShifted('7', '&', Key_7),
82+
keyAndShifted('8', '*', Key_8),
83+
keyAndShifted('9', '(', Key_9),
84+
keyAndShifted('0', ')', Key_0),
85+
keyAndShifted('-', '_', Key_minus),
86+
keyAndShifted('=', '+', Key_equal),
87+
88+
// Row 2
89+
keyAndShifted('q', 'Q', Key_q),
90+
keyAndShifted('w', 'W', Key_w),
91+
keyAndShifted('e', 'E', Key_e),
92+
keyAndShifted('r', 'R', Key_r),
93+
keyAndShifted('t', 'T', Key_t),
94+
keyAndShifted('y', 'Y', Key_y),
95+
keyAndShifted('u', 'U', Key_u),
96+
keyAndShifted('i', 'I', Key_i),
97+
keyAndShifted('o', 'O', Key_o),
98+
keyAndShifted('p', 'P', Key_p),
99+
keyAndShifted('[', '{', Key_openSquare),
100+
keyAndShifted(']', '}', Key_closeSquare),
101+
keyAndShifted('\\', '|', Key_backSlash),
102+
103+
// Row 3
104+
keyAndShifted('a', 'A', Key_a),
105+
keyAndShifted('s', 'S', Key_s),
106+
keyAndShifted('d', 'D', Key_d),
107+
keyAndShifted('f', 'F', Key_f),
108+
keyAndShifted('g', 'G', Key_g),
109+
keyAndShifted('h', 'H', Key_h),
110+
keyAndShifted('j', 'J', Key_j),
111+
keyAndShifted('k', 'K', Key_k),
112+
keyAndShifted('l', 'L', Key_l),
113+
keyAndShifted(';', ':', Key_semicolon),
114+
keyAndShifted('\'', '"', Key_apostrophe),
115+
116+
// Row 4
117+
keyAndShifted('z', 'Z', Key_z),
118+
keyAndShifted('x', 'X', Key_x),
119+
keyAndShifted('c', 'C', Key_c),
120+
keyAndShifted('v', 'V', Key_v),
121+
keyAndShifted('b', 'B', Key_b),
122+
keyAndShifted('n', 'N', Key_n),
123+
keyAndShifted('m', 'M', Key_m),
124+
keyAndShifted(',', '<', Key_comma),
125+
keyAndShifted('.', '>', Key_period),
126+
keyAndShifted('/', '?', Key_slash),
127+
128+
// Row 5
129+
[' '] = keyDataStruct(Key_space, 0)
130+
};
131+
// clang-format on
132+
// clang-format can you please not butcher the readability of the code?
133+
134+
#undef keyPressStruct
135+
#undef keyAndShifted
136+
#undef keyAndCapital
137+
138+
static struct KbCmd keyPressCMD(enum KeyCode code, enum KeyState event,
139+
uint8_t modifiers) {
140+
KeyPress kp = (KeyPress){0, code, event, modifiers};
141+
struct PS2Buf_t b = (struct PS2Buf_t){PS2_KEY_EVENT, {.keyEvent = kp}};
142+
return (struct KbCmd){CMD_KEY_PRESS, {.kb = b}};
143+
}
144+
145+
static struct KbCmd keyPressCMDFromData(struct KeyData data) {
146+
return (keyPressCMD(data.c, KeyPressed, data.mods));
147+
}
148+
149+
static struct KbCmd typeWordCMD(char *word) {
150+
return (struct KbCmd){CMD_TYPE_WORD, {.w = word}};
151+
}
152+
153+
// `loops` must be greater than 0
154+
static struct KbCmd loopStartCMD(int loops) {
155+
return (struct KbCmd){CMD_LOOP_START, {.loops = loops}};
156+
}
157+
158+
static struct KbCmd loopEndCMD() {
159+
return (struct KbCmd){CMD_LOOP_END, {}};
160+
}
161+
162+
static struct KbCmd funcCMD(CmdFunc func) {
163+
return (struct KbCmd){CMD_FUNC, {.func = func}};
164+
}
165+
166+
static struct KbCmd endCMD() {
167+
return (struct KbCmd){CMD_END, {}};
168+
}
169+
170+
// Returns 0 when exiting normally, and anything else when things go wrong
171+
typedef void (*KeyPressHandler)(struct PS2Buf_t);
172+
typedef int (*ExecFunc)(struct KbCmd, int *, KeyPressHandler);
173+
174+
static void baseKeyHandler(struct PS2Buf_t kb) {
175+
vgaEditor(kb);
176+
}
177+
178+
static int baseExec(struct KbCmd cmd, int *idx, KeyPressHandler kp) {
179+
switch (cmd.cmd) {
180+
case CMD_KEY_PRESS:
181+
kp(cmd.data.kb);
182+
break;
183+
case CMD_TYPE_WORD:
184+
for (char *c = cmd.data.w; *c != 0; c++) {
185+
kp(keyPressCMDFromData(charToPressCMD[(int)*c]).data.kb);
186+
}
187+
break;
188+
case CMD_LOOP_START:
189+
if (cmd.data.loops < 1) {
190+
FAIL_M("Loop count must be greater than 0, got %i", cmd.data.loops);
191+
return 1;
192+
}
193+
if (current == -1 || loopStack[current].idx != *idx) {
194+
current++;
195+
loopStack[current] = (struct LoopNode){*idx, cmd.data.loops};
196+
}
197+
loopStack[current].remaining--;
198+
break;
199+
case CMD_LOOP_END:
200+
if (loopStack[current].remaining) {
201+
// subtract 1 because idx is incremented after command completes
202+
// this was a surprisingly annoying issue to track down
203+
*idx = loopStack[current].idx - 1;
204+
} else {
205+
current--;
206+
}
207+
break;
208+
case CMD_FUNC:
209+
return cmd.data.func();
210+
case CMD_END:
211+
break;
212+
default:
213+
FAIL_M("Unexpected command type: %i", cmd.cmd);
214+
return 1;
215+
}
216+
return 0;
217+
}
218+
219+
static int execList(struct KbCmd *cmds, ExecFunc f, KeyPressHandler kp) {
220+
for (int i = 0; cmds[i].cmd != CMD_END; i++) {
221+
if (f(cmds[i], &i, kp)) {
222+
char buff[32];
223+
int len = snprintf(buff, sizeof(buff), "Command %i failed", i);
224+
serialWrite(COM1, (uint8_t *)buff, len);
225+
return 1;
226+
}
227+
}
228+
return 0;
229+
}
230+
231+
#endif

tests/src/test_kb_cmd.c

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#include "kb_cmd.h"
2+
#include "string.h"
3+
4+
#define testStart(name, print) \
5+
int name##Start() { \
6+
char text[] = print " starting\n"; \
7+
serialWrite(COM1, (uint8_t *)(text), sizeof(text) - 1); \
8+
return 0; \
9+
}
10+
11+
#define testEnd(name, print) \
12+
int name##End() { \
13+
char text[] = print " completed\n"; \
14+
serialWrite(COM1, (uint8_t *)(text), sizeof(text) - 1); \
15+
return 0; \
16+
}
17+
18+
char buff[64];
19+
int buffIdx = 0;
20+
21+
int resetBuff() {
22+
memset(buff, 0, sizeof(buff));
23+
buffIdx = 0;
24+
return 0;
25+
}
26+
27+
void testKeyHandler(struct PS2Buf_t buf) {
28+
buff[buffIdx++] = keyPressToASCII(buf.keyEvent);
29+
}
30+
31+
// clang-format off
32+
33+
// Loop prints
34+
testStart(loopTest, "loop test")
35+
testEnd(loopTest, "loop test")
36+
37+
// Nesting prints
38+
testStart(nestingTest, "nesting test")
39+
testEnd(nestingTest, "nesting test")
40+
41+
// Char table prints
42+
testStart(charTableTest, "char table test")
43+
testEnd(charTableTest, "char table test")
44+
45+
// Word prints
46+
testStart(wordTest, "word test")
47+
testEnd(wordTest, "word test")
48+
49+
// Complex word prints
50+
testStart(complexWordTest, "complex word test")
51+
testEnd(complexWordTest, "complex word test")
52+
53+
// Loop testing
54+
int v = 0;
55+
56+
// clang-format on
57+
58+
int vReset() {
59+
v = 0;
60+
return 0;
61+
}
62+
63+
int vInc() {
64+
v++;
65+
return 0;
66+
}
67+
68+
int testSingleLoop() {
69+
ASSERT_M(v == 4, "Expected to loop 4 times, looped %i time(s) instead", v);
70+
return 0;
71+
}
72+
73+
int testNestedLoop() {
74+
ASSERT_M(v == 6, "Expected to loop 6 times, looped %i time(s) instead", v);
75+
return 0;
76+
}
77+
78+
// Char table testing
79+
int capitalShiftTest() {
80+
ASSERT(Key_a == charToPressCMD['a'].c);
81+
ASSERT(0 == charToPressCMD['a'].mods);
82+
ASSERT(Key_a == charToPressCMD['A'].c);
83+
ASSERT(KEY_MOD_SHIFT == charToPressCMD['A'].mods)
84+
return 0;
85+
}
86+
87+
int shiftTest() {
88+
ASSERT(Key_grave == charToPressCMD['`'].c)
89+
ASSERT(0 == charToPressCMD['`'].mods)
90+
ASSERT(Key_grave == charToPressCMD['~'].c)
91+
ASSERT(KEY_MOD_SHIFT == charToPressCMD['~'].mods)
92+
return 0;
93+
}
94+
95+
// Word type testing
96+
int wordTest() {
97+
ASSERT(strncmp("test", buff, 5))
98+
return 0;
99+
}
100+
101+
int complexWordTest() {
102+
ASSERT(strncmp("This is a very Loong word$%@^@\\", buff, 32))
103+
return 0;
104+
}
105+
106+
void test_main() {
107+
struct KbCmd list[] = {
108+
// Single loop test
109+
funcCMD(loopTestStart),
110+
loopStartCMD(4),
111+
funcCMD(vInc),
112+
loopEndCMD(),
113+
funcCMD(testSingleLoop),
114+
funcCMD(loopTestEnd),
115+
116+
funcCMD(vReset),
117+
// Nested loop test
118+
funcCMD(nestingTestStart),
119+
loopStartCMD(2),
120+
loopStartCMD(3),
121+
funcCMD(vInc),
122+
loopEndCMD(),
123+
loopEndCMD(),
124+
funcCMD(testNestedLoop),
125+
funcCMD(nestingTestEnd),
126+
127+
// Char table values test
128+
funcCMD(charTableTestStart),
129+
funcCMD(capitalShiftTest),
130+
funcCMD(shiftTest),
131+
funcCMD(charTableTestEnd),
132+
133+
// Word type test
134+
funcCMD(wordTestStart),
135+
typeWordCMD("test"),
136+
funcCMD(wordTestEnd),
137+
138+
funcCMD(complexWordTestStart),
139+
typeWordCMD("This is a very Loong word$%@^@\\"),
140+
funcCMD(complexWordTestEnd),
141+
142+
endCMD(),
143+
};
144+
execList(list, baseExec, testKeyHandler);
145+
char done[] = "test_kb_cmd done\n";
146+
serialWrite(COM1, (uint8_t *)(done), sizeof(done) - 1);
147+
}

0 commit comments

Comments
 (0)