|
| 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 |
0 commit comments