From 85ee9eb4b40d505f5298ff4759d973ab741422d9 Mon Sep 17 00:00:00 2001 From: Bad Diode Date: Sat, 5 Mar 2022 19:23:24 +0100 Subject: [PATCH] Add initial mouse support --- Makefile | 26 +++++++------ src/main.c | 112 +++++++++++++++++++++++++++++++++++++++++++---------- 2 files changed, 105 insertions(+), 33 deletions(-) diff --git a/Makefile b/Makefile index 8548dbc..10dc4fb 100644 --- a/Makefile +++ b/Makefile @@ -1,14 +1,16 @@ -BASE_UXN := src/uxn -SRC_DIR ?= src -BUILD_DIR ?= build -SRC_MAIN ?= $(SRC_DIR)/main.c -EXE_NAME ?= uxnfb -BIN := $(BUILD_DIR)/$(EXE_NAME) -UXN_HEAD := $(BASE_UXN)/src/uxn.h -TAL_SRC ?= $(BASE_UXN)/projects/examples/devices/screen.tal -UXN_ROM ?= $(BUILD_DIR)/rom.rom -UXN_ASM ?= $(BUILD_DIR)/uxnasm -KBD_PATH ?= /dev/input/event1 +BASE_UXN := src/uxn +SRC_DIR ?= src +BUILD_DIR ?= build +SRC_MAIN ?= $(SRC_DIR)/main.c +EXE_NAME ?= uxnfb +BIN := $(BUILD_DIR)/$(EXE_NAME) +UXN_HEAD := $(BASE_UXN)/src/uxn.h +TAL_SRC ?= $(BASE_UXN)/projects/examples/devices/screen.tal +UXN_ROM ?= $(BUILD_DIR)/rom.rom +UXN_ASM ?= $(BUILD_DIR)/uxnasm +KBD_PATH ?= /dev/input/event1 +MOUSE_PATH ?= /dev/input/mice +C_DEFINES := -DKBD_PATH=\"$(KBD_PATH)\" -DMOUSE_PATH=\"$(MOUSE_PATH)\" CC ?= cc CFLAGS := -Wall -Wextra -pedantic @@ -30,7 +32,7 @@ endif main: $(BIN) $(BIN): $(SRC_MAIN) $(BUILD_DIR) $(UXN_HEAD) - $(CC) $(CFLAGS) -o $(BIN) $(SRC_MAIN) -DKBD_PATH=\"$(KBD_PATH)\" + $(CC) $(CFLAGS) -o $(BIN) $(SRC_MAIN) $(C_DEFINES) $(BUILD_DIR): mkdir -p $(BUILD_DIR) diff --git a/src/main.c b/src/main.c index aac3211..a431029 100644 --- a/src/main.c +++ b/src/main.c @@ -12,6 +12,8 @@ #include "ppu.c" #include "uxn-fast.c" +#define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) + static Uxn u; static Device *devscreen; static Device *devctrl; @@ -33,42 +35,93 @@ time_elapsed(Time since){ return (now.tv_sec - since.tv_sec) * 1e9 + (now.tv_nsec - since.tv_nsec); } +typedef struct Mouse { + s32 x; + s32 y; + bool left; + bool mid; + bool right; + bool update; +} Mouse; + typedef struct Input { - int kb_fd; + int kbd_fd; + int mouse_fd; char map[KEY_MAX / 8 + 1]; u8 controller; + Mouse mouse; } Input; -static Input input; +// NOTE: For event codes and input documentation: +// - https://www.kernel.org/doc/Documentation/input/event-codes.txt +// - /usr/include/linux/input.h +static Input in; void init_input(void) { - memset(&input, 0, sizeof(input)); - input.kb_fd = -1; + memset(&in, 0, sizeof(in)); + in.kbd_fd = -1; + in.mouse_fd = -1; - const char *dev = KBD_PATH; - input.kb_fd = open(dev, O_RDONLY); - if (input.kb_fd == -1) { + in.kbd_fd = open(KBD_PATH, O_RDONLY | O_NONBLOCK); + if (in.kbd_fd == -1) { // NOTE: Some applications may not require a keyboard so this is - // optional, but we are still displaying an error. - fprintf(stderr, "error: no couldn't open keyboard %s: %s.\n", dev, strerror(errno)); + // optional, but we are still displaying an error. + fprintf(stderr, "error: no couldn't open keyboard %s: %s.\n", KBD_PATH, strerror(errno)); + } + + in.mouse_fd = open(MOUSE_PATH, O_RDONLY | O_NONBLOCK); + if (in.mouse_fd == -1) { + // NOTE: Some applications may not require a mouse so this is + // optional, but we are still displaying an error. + fprintf(stderr, "error: no couldn't open mouse %s: %s.\n", MOUSE_PATH, strerror(errno)); } } void poll_keyboard(void) { - if (input.kb_fd == -1) { + if (in.kbd_fd == -1) { return; } + // TODO: use read() instead to avoid updating the keyboard if no events have + // occurred. Similar to the mouse implementation. char map[KEY_MAX / 8 + 1]; memset(map, 0, sizeof(map)); - ioctl(input.kb_fd, EVIOCGKEY(sizeof(map)), map); + ioctl(in.kbd_fd, EVIOCGKEY(sizeof(map)), map); for (size_t i = 0; i < sizeof(map); i++) { - input.map[i] |= map[i]; + in.map[i] |= map[i]; } } +void +poll_mouse(void) { + if (in.mouse_fd == -1) { + return; + } + + struct input_event mouse_event; + if (read(in.mouse_fd, &mouse_event, sizeof(mouse_event)) != -1) { + if (mouse_event.type == EV_REL) { + if (mouse_event.code == REL_X) { + in.mouse.x = CLAMP( + in.mouse.x + (s32)mouse_event.value, 0, (s32)screen_width); + } else if (mouse_event.code == REL_Y) { + in.mouse.y = CLAMP( + in.mouse.y + (s32)mouse_event.value, 0, (s32)screen_height); + } + } + in.mouse.update = true; + // TODO: Handle mouse keys + } +} + +void +poll_input(void) { + poll_keyboard(); + poll_mouse(); +} + void handle_keyboard(void) { // Find mod keys. @@ -76,9 +129,9 @@ handle_keyboard(void) { // bool ctrl_mod = false; // bool alt_mod = false; // bool meta_mod = false; - for (size_t i = 0; i < sizeof(input.map); i++) { + for (size_t i = 0; i < sizeof(in.map); i++) { for (size_t j = 0; j < 8; j++) { - char key = input.map[i] & (1 << j); + char key = in.map[i] & (1 << j); if (key) { char key_code = i * 8 + j; switch (key_code) { @@ -98,9 +151,9 @@ handle_keyboard(void) { // Handle normal keys. u8 controller_now = 0; - for (size_t i = 0; i < sizeof(input.map); i++) { + for (size_t i = 0; i < sizeof(in.map); i++) { for (size_t j = 0; j < 8; j++) { - char key = input.map[i] & (1 << j); + char key = in.map[i] & (1 << j); if (key) { char key_code = i * 8 + j; // Normal keys. @@ -204,15 +257,32 @@ handle_keyboard(void) { } } - if (controller_now != input.controller) { + if (controller_now != in.controller) { devctrl->dat[2] = controller_now; uxn_eval(&u, mempeek16(devctrl->dat, 0)); - input.controller = controller_now; + in.controller = controller_now; } // Reset input state. devctrl->dat[3] = 0; - memset(input.map, 0, sizeof(input.map)); + memset(in.map, 0, sizeof(in.map)); +} + +void +handle_mouse(void) { + if (in.mouse.update) { + // TODO: Handle mouse keys + mempoke16(devmouse->dat, 0x2, in.mouse.x); + mempoke16(devmouse->dat, 0x4, in.mouse.y); + uxn_eval(&u, mempeek16(devmouse->dat, 0)); + in.mouse.update = false; + } +} + +void +handle_input(void) { + handle_keyboard(); + handle_mouse(); } void @@ -430,10 +500,10 @@ main(int argc, char *argv[]) { uxn_eval(&u, 0x0100); Time frame_time = time_now(); while (true) { - poll_keyboard(); + poll_input(); size_t elapsed = time_elapsed(frame_time); if (elapsed >= 16666666) { - handle_keyboard(); + handle_input(); // Echo input to standard output. uxn_eval(&u, mempeek16(devscreen->dat, 0));