diff options
author | Bad Diode <bd@badd10de.dev> | 2022-03-05 19:23:24 +0100 |
---|---|---|
committer | Bad Diode <bd@badd10de.dev> | 2022-03-05 19:23:24 +0100 |
commit | 85ee9eb4b40d505f5298ff4759d973ab741422d9 (patch) | |
tree | 48d7924e0706eefcd724c4bb3997ad704d07beae | |
parent | d08a7dfac6ea835603a580668761aa27e70f669b (diff) | |
download | uxnfb-85ee9eb4b40d505f5298ff4759d973ab741422d9.tar.gz uxnfb-85ee9eb4b40d505f5298ff4759d973ab741422d9.zip |
Add initial mouse support
-rw-r--r-- | Makefile | 26 | ||||
-rw-r--r-- | src/main.c | 112 |
2 files changed, 105 insertions, 33 deletions
@@ -1,14 +1,16 @@ | |||
1 | BASE_UXN := src/uxn | 1 | BASE_UXN := src/uxn |
2 | SRC_DIR ?= src | 2 | SRC_DIR ?= src |
3 | BUILD_DIR ?= build | 3 | BUILD_DIR ?= build |
4 | SRC_MAIN ?= $(SRC_DIR)/main.c | 4 | SRC_MAIN ?= $(SRC_DIR)/main.c |
5 | EXE_NAME ?= uxnfb | 5 | EXE_NAME ?= uxnfb |
6 | BIN := $(BUILD_DIR)/$(EXE_NAME) | 6 | BIN := $(BUILD_DIR)/$(EXE_NAME) |
7 | UXN_HEAD := $(BASE_UXN)/src/uxn.h | 7 | UXN_HEAD := $(BASE_UXN)/src/uxn.h |
8 | TAL_SRC ?= $(BASE_UXN)/projects/examples/devices/screen.tal | 8 | TAL_SRC ?= $(BASE_UXN)/projects/examples/devices/screen.tal |
9 | UXN_ROM ?= $(BUILD_DIR)/rom.rom | 9 | UXN_ROM ?= $(BUILD_DIR)/rom.rom |
10 | UXN_ASM ?= $(BUILD_DIR)/uxnasm | 10 | UXN_ASM ?= $(BUILD_DIR)/uxnasm |
11 | KBD_PATH ?= /dev/input/event1 | 11 | KBD_PATH ?= /dev/input/event1 |
12 | MOUSE_PATH ?= /dev/input/mice | ||
13 | C_DEFINES := -DKBD_PATH=\"$(KBD_PATH)\" -DMOUSE_PATH=\"$(MOUSE_PATH)\" | ||
12 | 14 | ||
13 | CC ?= cc | 15 | CC ?= cc |
14 | CFLAGS := -Wall -Wextra -pedantic | 16 | CFLAGS := -Wall -Wextra -pedantic |
@@ -30,7 +32,7 @@ endif | |||
30 | main: $(BIN) | 32 | main: $(BIN) |
31 | 33 | ||
32 | $(BIN): $(SRC_MAIN) $(BUILD_DIR) $(UXN_HEAD) | 34 | $(BIN): $(SRC_MAIN) $(BUILD_DIR) $(UXN_HEAD) |
33 | $(CC) $(CFLAGS) -o $(BIN) $(SRC_MAIN) -DKBD_PATH=\"$(KBD_PATH)\" | 35 | $(CC) $(CFLAGS) -o $(BIN) $(SRC_MAIN) $(C_DEFINES) |
34 | 36 | ||
35 | $(BUILD_DIR): | 37 | $(BUILD_DIR): |
36 | mkdir -p $(BUILD_DIR) | 38 | mkdir -p $(BUILD_DIR) |
@@ -12,6 +12,8 @@ | |||
12 | #include "ppu.c" | 12 | #include "ppu.c" |
13 | #include "uxn-fast.c" | 13 | #include "uxn-fast.c" |
14 | 14 | ||
15 | #define CLAMP(X, MIN, MAX) ((X) <= (MIN) ? (MIN) : (X) > (MAX) ? (MAX): (X)) | ||
16 | |||
15 | static Uxn u; | 17 | static Uxn u; |
16 | static Device *devscreen; | 18 | static Device *devscreen; |
17 | static Device *devctrl; | 19 | static Device *devctrl; |
@@ -33,52 +35,103 @@ time_elapsed(Time since){ | |||
33 | return (now.tv_sec - since.tv_sec) * 1e9 + (now.tv_nsec - since.tv_nsec); | 35 | return (now.tv_sec - since.tv_sec) * 1e9 + (now.tv_nsec - since.tv_nsec); |
34 | } | 36 | } |
35 | 37 | ||
38 | typedef struct Mouse { | ||
39 | s32 x; | ||
40 | s32 y; | ||
41 | bool left; | ||
42 | bool mid; | ||
43 | bool right; | ||
44 | bool update; | ||
45 | } Mouse; | ||
46 | |||
36 | typedef struct Input { | 47 | typedef struct Input { |
37 | int kb_fd; | 48 | int kbd_fd; |
49 | int mouse_fd; | ||
38 | char map[KEY_MAX / 8 + 1]; | 50 | char map[KEY_MAX / 8 + 1]; |
39 | u8 controller; | 51 | u8 controller; |
52 | Mouse mouse; | ||
40 | } Input; | 53 | } Input; |
41 | 54 | ||
42 | static Input input; | 55 | // NOTE: For event codes and input documentation: |
56 | // - https://www.kernel.org/doc/Documentation/input/event-codes.txt | ||
57 | // - /usr/include/linux/input.h | ||
58 | static Input in; | ||
43 | 59 | ||
44 | void | 60 | void |
45 | init_input(void) { | 61 | init_input(void) { |
46 | memset(&input, 0, sizeof(input)); | 62 | memset(&in, 0, sizeof(in)); |
47 | input.kb_fd = -1; | 63 | in.kbd_fd = -1; |
64 | in.mouse_fd = -1; | ||
48 | 65 | ||
49 | const char *dev = KBD_PATH; | 66 | in.kbd_fd = open(KBD_PATH, O_RDONLY | O_NONBLOCK); |
50 | input.kb_fd = open(dev, O_RDONLY); | 67 | if (in.kbd_fd == -1) { |
51 | if (input.kb_fd == -1) { | ||
52 | // NOTE: Some applications may not require a keyboard so this is | 68 | // NOTE: Some applications may not require a keyboard so this is |
53 | // optional, but we are still displaying an error. | 69 | // optional, but we are still displaying an error. |
54 | fprintf(stderr, "error: no couldn't open keyboard %s: %s.\n", dev, strerror(errno)); | 70 | fprintf(stderr, "error: no couldn't open keyboard %s: %s.\n", KBD_PATH, strerror(errno)); |
71 | } | ||
72 | |||
73 | in.mouse_fd = open(MOUSE_PATH, O_RDONLY | O_NONBLOCK); | ||
74 | if (in.mouse_fd == -1) { | ||
75 | // NOTE: Some applications may not require a mouse so this is | ||
76 | // optional, but we are still displaying an error. | ||
77 | fprintf(stderr, "error: no couldn't open mouse %s: %s.\n", MOUSE_PATH, strerror(errno)); | ||
55 | } | 78 | } |
56 | } | 79 | } |
57 | 80 | ||
58 | void | 81 | void |
59 | poll_keyboard(void) { | 82 | poll_keyboard(void) { |
60 | if (input.kb_fd == -1) { | 83 | if (in.kbd_fd == -1) { |
61 | return; | 84 | return; |
62 | } | 85 | } |
63 | 86 | ||
87 | // TODO: use read() instead to avoid updating the keyboard if no events have | ||
88 | // occurred. Similar to the mouse implementation. | ||
64 | char map[KEY_MAX / 8 + 1]; | 89 | char map[KEY_MAX / 8 + 1]; |
65 | memset(map, 0, sizeof(map)); | 90 | memset(map, 0, sizeof(map)); |
66 | ioctl(input.kb_fd, EVIOCGKEY(sizeof(map)), map); | 91 | ioctl(in.kbd_fd, EVIOCGKEY(sizeof(map)), map); |
67 | for (size_t i = 0; i < sizeof(map); i++) { | 92 | for (size_t i = 0; i < sizeof(map); i++) { |
68 | input.map[i] |= map[i]; | 93 | in.map[i] |= map[i]; |
69 | } | 94 | } |
70 | } | 95 | } |
71 | 96 | ||
72 | void | 97 | void |
98 | poll_mouse(void) { | ||
99 | if (in.mouse_fd == -1) { | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | struct input_event mouse_event; | ||
104 | if (read(in.mouse_fd, &mouse_event, sizeof(mouse_event)) != -1) { | ||
105 | if (mouse_event.type == EV_REL) { | ||
106 | if (mouse_event.code == REL_X) { | ||
107 | in.mouse.x = CLAMP( | ||
108 | in.mouse.x + (s32)mouse_event.value, 0, (s32)screen_width); | ||
109 | } else if (mouse_event.code == REL_Y) { | ||
110 | in.mouse.y = CLAMP( | ||
111 | in.mouse.y + (s32)mouse_event.value, 0, (s32)screen_height); | ||
112 | } | ||
113 | } | ||
114 | in.mouse.update = true; | ||
115 | // TODO: Handle mouse keys | ||
116 | } | ||
117 | } | ||
118 | |||
119 | void | ||
120 | poll_input(void) { | ||
121 | poll_keyboard(); | ||
122 | poll_mouse(); | ||
123 | } | ||
124 | |||
125 | void | ||
73 | handle_keyboard(void) { | 126 | handle_keyboard(void) { |
74 | // Find mod keys. | 127 | // Find mod keys. |
75 | bool shift_mod = false; | 128 | bool shift_mod = false; |
76 | // bool ctrl_mod = false; | 129 | // bool ctrl_mod = false; |
77 | // bool alt_mod = false; | 130 | // bool alt_mod = false; |
78 | // bool meta_mod = false; | 131 | // bool meta_mod = false; |
79 | for (size_t i = 0; i < sizeof(input.map); i++) { | 132 | for (size_t i = 0; i < sizeof(in.map); i++) { |
80 | for (size_t j = 0; j < 8; j++) { | 133 | for (size_t j = 0; j < 8; j++) { |
81 | char key = input.map[i] & (1 << j); | 134 | char key = in.map[i] & (1 << j); |
82 | if (key) { | 135 | if (key) { |
83 | char key_code = i * 8 + j; | 136 | char key_code = i * 8 + j; |
84 | switch (key_code) { | 137 | switch (key_code) { |
@@ -98,9 +151,9 @@ handle_keyboard(void) { | |||
98 | 151 | ||
99 | // Handle normal keys. | 152 | // Handle normal keys. |
100 | u8 controller_now = 0; | 153 | u8 controller_now = 0; |
101 | for (size_t i = 0; i < sizeof(input.map); i++) { | 154 | for (size_t i = 0; i < sizeof(in.map); i++) { |
102 | for (size_t j = 0; j < 8; j++) { | 155 | for (size_t j = 0; j < 8; j++) { |
103 | char key = input.map[i] & (1 << j); | 156 | char key = in.map[i] & (1 << j); |
104 | if (key) { | 157 | if (key) { |
105 | char key_code = i * 8 + j; | 158 | char key_code = i * 8 + j; |
106 | // Normal keys. | 159 | // Normal keys. |
@@ -204,15 +257,32 @@ handle_keyboard(void) { | |||
204 | } | 257 | } |
205 | } | 258 | } |
206 | 259 | ||
207 | if (controller_now != input.controller) { | 260 | if (controller_now != in.controller) { |
208 | devctrl->dat[2] = controller_now; | 261 | devctrl->dat[2] = controller_now; |
209 | uxn_eval(&u, mempeek16(devctrl->dat, 0)); | 262 | uxn_eval(&u, mempeek16(devctrl->dat, 0)); |
210 | input.controller = controller_now; | 263 | in.controller = controller_now; |
211 | } | 264 | } |
212 | 265 | ||
213 | // Reset input state. | 266 | // Reset input state. |
214 | devctrl->dat[3] = 0; | 267 | devctrl->dat[3] = 0; |
215 | memset(input.map, 0, sizeof(input.map)); | 268 | memset(in.map, 0, sizeof(in.map)); |
269 | } | ||
270 | |||
271 | void | ||
272 | handle_mouse(void) { | ||
273 | if (in.mouse.update) { | ||
274 | // TODO: Handle mouse keys | ||
275 | mempoke16(devmouse->dat, 0x2, in.mouse.x); | ||
276 | mempoke16(devmouse->dat, 0x4, in.mouse.y); | ||
277 | uxn_eval(&u, mempeek16(devmouse->dat, 0)); | ||
278 | in.mouse.update = false; | ||
279 | } | ||
280 | } | ||
281 | |||
282 | void | ||
283 | handle_input(void) { | ||
284 | handle_keyboard(); | ||
285 | handle_mouse(); | ||
216 | } | 286 | } |
217 | 287 | ||
218 | void | 288 | void |
@@ -430,10 +500,10 @@ main(int argc, char *argv[]) { | |||
430 | uxn_eval(&u, 0x0100); | 500 | uxn_eval(&u, 0x0100); |
431 | Time frame_time = time_now(); | 501 | Time frame_time = time_now(); |
432 | while (true) { | 502 | while (true) { |
433 | poll_keyboard(); | 503 | poll_input(); |
434 | size_t elapsed = time_elapsed(frame_time); | 504 | size_t elapsed = time_elapsed(frame_time); |
435 | if (elapsed >= 16666666) { | 505 | if (elapsed >= 16666666) { |
436 | handle_keyboard(); | 506 | handle_input(); |
437 | 507 | ||
438 | // Echo input to standard output. | 508 | // Echo input to standard output. |
439 | uxn_eval(&u, mempeek16(devscreen->dat, 0)); | 509 | uxn_eval(&u, mempeek16(devscreen->dat, 0)); |