From e835289d291bb8d0a9f0b56130412226c4cbec7c Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Fri, 4 Mar 2022 23:08:57 +0100 Subject: [PATCH] Add partial support for keyboard input --- src/main.c | 231 +++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 188 insertions(+), 43 deletions(-) diff --git a/src/main.c b/src/main.c index de566ac..2bce9f7 100644 --- a/src/main.c +++ b/src/main.c @@ -1,3 +1,4 @@ +#include #include #include #include @@ -5,6 +6,8 @@ #include #include +#include + #include "shorthand.h" #include "ppu.c" #include "uxn-fast.c" @@ -30,6 +33,142 @@ time_elapsed(Time since){ return (now.tv_sec - since.tv_sec) * 1e9 + (now.tv_nsec - since.tv_nsec); } +typedef struct Keyboard { + int fd; + char map[KEY_MAX / 8 + 1]; +} Keyboard; + +static Keyboard keyboard; + +void +init_input(void) { + const char *dev = "/dev/input/by-id/usb-Apple_Inc._Magic_Keyboard_with_Numeric_Keypad_F0T0167000DHTHKAD-if01-event-kbd"; + keyboard.fd = open(dev, O_RDONLY); + if (keyboard.fd == -1) { + // TODO: should this be a warning and still work for applications that + // don't require a keyboard? + fprintf(stderr, "error: no couldn't open keyboard %s: %s.\n", dev, strerror(errno)); + exit(EXIT_FAILURE); + } + + memset(keyboard.map, 0, sizeof(keyboard.map)); +} + +void +poll_keyboard(void) { + char map[KEY_MAX / 8 + 1]; + memset(map, 0, sizeof(map)); + ioctl(keyboard.fd, EVIOCGKEY(sizeof(map)), map); + for (size_t i = 0; i < sizeof(map); i++) { + keyboard.map[i] |= map[i]; + } +} + +void +handle_keyboard(void) { + // Find mod keys. + bool shift_mod = false; + // bool ctrl_mod = false; + // bool alt_mod = false; + // bool meta_mod = false; + for (size_t i = 0; i < sizeof(keyboard.map); i++) { + for (size_t j = 0; j < 8; j++) { + char key = keyboard.map[i] & (1 << j); + if (key) { + char key_code = i * 8 + j; + switch (key_code) { + case KEY_LEFTSHIFT: + case KEY_RIGHTSHIFT: { shift_mod = true; } break; + // case KEY_LEFTCTRL: + // case KEY_RIGHTCTRL: { ctrl_mod = true; } break; + // case KEY_LEFTALT: + // case KEY_RIGHTALT: { alt_mod = true; } break; + // case KEY_LEFTMETA: + // case KEY_RIGHTMETA: { meta_mod = true; } break; + default: break; + } + } + } + } + + // Handle normal keys. + for (size_t i = 0; i < sizeof(keyboard.map); i++) { + for (size_t j = 0; j < 8; j++) { + char key = keyboard.map[i] & (1 << j); + if (key) { + char key_code = i * 8 + j; + // Special keys. + // TODO: ... + + // Normal keys. + char rune = '\0'; + switch (key_code) { + case KEY_1: { rune = shift_mod ? '!' : '1'; } break; + case KEY_2: { rune = shift_mod ? '@' : '2'; } break; + case KEY_3: { rune = shift_mod ? '#' : '3'; } break; + case KEY_4: { rune = shift_mod ? '$' : '4'; } break; + case KEY_5: { rune = shift_mod ? '%' : '5'; } break; + case KEY_6: { rune = shift_mod ? '^' : '6'; } break; + case KEY_7: { rune = shift_mod ? '&' : '7'; } break; + case KEY_8: { rune = shift_mod ? '*' : '8'; } break; + case KEY_9: { rune = shift_mod ? '(' : '9'; } break; + case KEY_0: { rune = shift_mod ? ')' : '0'; } break; + case KEY_MINUS: { rune = shift_mod ? '_' : '-'; } break; + case KEY_EQUAL: { rune = shift_mod ? '+' : '+'; } break; + case KEY_Q: { rune = shift_mod ? 'Q' : 'q'; } break; + case KEY_W: { rune = shift_mod ? 'W' : 'w'; } break; + case KEY_E: { rune = shift_mod ? 'E' : 'e'; } break; + case KEY_R: { rune = shift_mod ? 'T' : 't'; } break; + case KEY_T: { rune = shift_mod ? 'T' : 't'; } break; + case KEY_Y: { rune = shift_mod ? 'Y' : 'y'; } break; + case KEY_U: { rune = shift_mod ? 'U' : 'u'; } break; + case KEY_I: { rune = shift_mod ? 'I' : 'i'; } break; + case KEY_O: { rune = shift_mod ? 'O' : 'o'; } break; + case KEY_P: { rune = shift_mod ? 'P' : 'p'; } break; + case KEY_LEFTBRACE: { rune = shift_mod ? '{' : '['; } break; + case KEY_RIGHTBRACE: { rune = shift_mod ? '}' : ']'; } break; + case KEY_A: { rune = shift_mod ? 'A' : 'a'; } break; + case KEY_S: { rune = shift_mod ? 'S' : 's'; } break; + case KEY_D: { rune = shift_mod ? 'D' : 'd'; } break; + case KEY_F: { rune = shift_mod ? 'F' : 'f'; } break; + case KEY_G: { rune = shift_mod ? 'G' : 'g'; } break; + case KEY_H: { rune = shift_mod ? 'H' : 'h'; } break; + case KEY_J: { rune = shift_mod ? 'J' : 'j'; } break; + case KEY_K: { rune = shift_mod ? 'K' : 'k'; } break; + case KEY_L: { rune = shift_mod ? 'L' : 'l'; } break; + case KEY_SEMICOLON: { rune = shift_mod ? ':' : ';'; } break; + case KEY_APOSTROPHE: { rune = shift_mod ? '"' : '\''; } break; + case KEY_GRAVE: { rune = shift_mod ? '~' : '`'; } break; + case KEY_BACKSLASH: { rune = shift_mod ? '|' : '\\'; } break; + case KEY_Z: { rune = shift_mod ? 'Z' : 'z'; } break; + case KEY_X: { rune = shift_mod ? 'X' : 'x'; } break; + case KEY_C: { rune = shift_mod ? 'C' : 'c'; } break; + case KEY_V: { rune = shift_mod ? 'V' : 'v'; } break; + case KEY_B: { rune = shift_mod ? 'B' : 'b'; } break; + case KEY_N: { rune = shift_mod ? 'N' : 'n'; } break; + case KEY_M: { rune = shift_mod ? 'M' : 'm'; } break; + case KEY_COMMA: { rune = shift_mod ? '<' : ','; } break; + case KEY_DOT: { rune = shift_mod ? '>' : '.'; } break; + case KEY_SLASH: { rune = shift_mod ? '?' : '/'; } break; + case KEY_SPACE: { rune = ' '; } break; + case KEY_TAB: { rune = '\t'; } break; + case KEY_BACKSPACE: { rune = 0x08; } break; + case KEY_ENTER: + case KEY_KPENTER: { rune = 0x0d; } break; + default: break; + } + if (rune) { + devctrl->dat[3] = rune; + uxn_eval(&u, mempeek16(devctrl->dat, 0)); + devctrl->dat[3] = 0; + continue; + } + } + } + } + memset(keyboard.map, 0, sizeof(keyboard.map)); +} + void nil_talk(Device *d, u8 b0, u8 w) { (void)d; @@ -103,51 +242,51 @@ screen_talk(Device *d, u8 b0, u8 w) { if (w) { switch (b0) { case 0x1: { - d->vector = mempeek16(d->dat, 0x0); - } break; + d->vector = mempeek16(d->dat, 0x0); + } break; case 0xe: { - u16 x = mempeek16(d->dat, 0x8); - u16 y = mempeek16(d->dat, 0xa); - u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; - u8 *layer = d->dat[0xe] >> 4 & 0x1 ? pixels_fg : pixels_bg; - u8 mode = d->dat[0xe] >> 5; - u8 color = d->dat[0xf] & 0xf; - if(!mode) { - ppu_pixel(layer, x, y, d->dat[0xe] & 0x3); - } else if(mode-- & 0x1) { - u8 flipx = mode & 0x2; - u8 flipy = mode & 0x4; - ppu_1bpp(layer, x, y, addr, color, flipx, flipy); - } else { - u8 flipx = mode & 0x2; - u8 flipy = mode & 0x4; - ppu_2bpp(layer, x, y, addr, color, flipx, flipy); - } - if(d->dat[0x6] & 0x01) { mempoke16(d->dat, 0x8, x + 1); } - if(d->dat[0x6] & 0x02) { mempoke16(d->dat, 0xa, y + 1); } - } break; + u16 x = mempeek16(d->dat, 0x8); + u16 y = mempeek16(d->dat, 0xa); + u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; + u8 *layer = d->dat[0xe] >> 4 & 0x1 ? pixels_fg : pixels_bg; + u8 mode = d->dat[0xe] >> 5; + u8 color = d->dat[0xf] & 0xf; + if(!mode) { + ppu_pixel(layer, x, y, d->dat[0xe] & 0x3); + } else if(mode-- & 0x1) { + u8 flipx = mode & 0x2; + u8 flipy = mode & 0x4; + ppu_1bpp(layer, x, y, addr, color, flipx, flipy); + } else { + u8 flipx = mode & 0x2; + u8 flipy = mode & 0x4; + ppu_2bpp(layer, x, y, addr, color, flipx, flipy); + } + if(d->dat[0x6] & 0x01) { mempoke16(d->dat, 0x8, x + 1); } + if(d->dat[0x6] & 0x02) { mempoke16(d->dat, 0xa, y + 1); } + } break; case 0xf: { - u16 x = mempeek16(d->dat, 0x8); - u16 y = mempeek16(d->dat, 0xa); - u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; - u8 *layer = d->dat[0xf] >> 6 & 0x1 ? pixels_fg : pixels_bg; - u8 color = d->dat[0xf] & 0xf; - u8 flipx = (d->dat[0xf] >> 0x4) & 0x1; - u8 flipy = (d->dat[0xf] >> 0x5) & 0x1; - if(d->dat[0xf] >> 0x7 & 0x1) { - ppu_2bpp(layer, x, y, addr, color, flipx, flipy); - if(d->dat[0x6] & 0x04) { - mempoke16(d->dat, 0xc, mempeek16(d->dat, 0xc) + 16); - } - } else { - ppu_1bpp(layer, x, y, addr, color, flipx, flipy); - if(d->dat[0x6] & 0x04) { - mempoke16(d->dat, 0xc, mempeek16(d->dat, 0xc) + 8); - } - } - if(d->dat[0x6] & 0x01) { mempoke16(d->dat, 0x8, x + 8); } - if(d->dat[0x6] & 0x02) { mempoke16(d->dat, 0xa, y + 8); } - } break; + u16 x = mempeek16(d->dat, 0x8); + u16 y = mempeek16(d->dat, 0xa); + u8 *addr = &d->mem[mempeek16(d->dat, 0xc)]; + u8 *layer = d->dat[0xf] >> 6 & 0x1 ? pixels_fg : pixels_bg; + u8 color = d->dat[0xf] & 0xf; + u8 flipx = (d->dat[0xf] >> 0x4) & 0x1; + u8 flipy = (d->dat[0xf] >> 0x5) & 0x1; + if(d->dat[0xf] >> 0x7 & 0x1) { + ppu_2bpp(layer, x, y, addr, color, flipx, flipy); + if(d->dat[0x6] & 0x04) { + mempoke16(d->dat, 0xc, mempeek16(d->dat, 0xc) + 16); + } + } else { + ppu_1bpp(layer, x, y, addr, color, flipx, flipy); + if(d->dat[0x6] & 0x04) { + mempoke16(d->dat, 0xc, mempeek16(d->dat, 0xc) + 8); + } + } + if(d->dat[0x6] & 0x01) { mempoke16(d->dat, 0x8, x + 8); } + if(d->dat[0x6] & 0x02) { mempoke16(d->dat, 0xa, y + 8); } + } break; default: break; } } @@ -208,6 +347,9 @@ init_uxn(char *file_name) { // Initialize framebuffer. ppu_init(); + // Initialize keybord. + init_input(); + // Prepare devices. uxn_port(&u, 0x0, "system", system_talk); uxn_port(&u, 0x1, "console", console_talk); @@ -242,8 +384,11 @@ main(int argc, char *argv[]) { uxn_eval(&u, 0x0100); Time frame_time = time_now(); while (true) { + poll_keyboard(); size_t elapsed = time_elapsed(frame_time); if (elapsed >= 16666666) { + handle_keyboard(); + // Echo input to standard output. uxn_eval(&u, mempeek16(devscreen->dat, 0));