This is an automated email from the ASF dual-hosted git repository. anchao pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git
commit 2d68f38f7bcdafa932a3d20f54c6ff28a453d7d4 Author: satoru akita <[email protected]> AuthorDate: Tue Oct 20 21:45:40 2026 +0900 games/NXDoom: Add an option to read the keys from a serial terminal. NXDoom reads its keys only from a keyboard character driver, so a board without one cannot play it. Some boards, such as the Spresense, have nothing but a serial console. Add GAMES_NXDOOM_UARTKBD, which reads bytes from the tty named by GAMES_NXDOOM_KBDPATH (for example /dev/console) and turns them into keyboard events: * W, A, S, D and the arrow keys move, J fires, K runs, L strafes, E or space uses, and Q or Esc opens the menu. * A terminal reports a key press but never its release, so each press is released again 250 ms after its last byte. The code is in its own file, i_input_uartkbd.c, which is built only when the option is on, so that i_input.c has just one call to it. Also remove the dead "#if 0" block that was left in i_input.c. The option is disabled by default, so existing configurations are not affected. Tested on a Spresense with an ILI9340 display: the menu opens, moves and closes from keys sent over the console. Assisted-by: Claude:claude-sonnet-5-5 Signed-off-by: satoru akita <[email protected]> --- games/NXDoom/CMakeLists.txt | 5 + games/NXDoom/Kconfig.input | 10 ++ games/NXDoom/Makefile | 6 + games/NXDoom/src/i_input.c | 62 +------- games/NXDoom/src/i_input.h | 4 + games/NXDoom/src/i_input_uartkbd.c | 285 +++++++++++++++++++++++++++++++++++++ 6 files changed, 314 insertions(+), 58 deletions(-) diff --git a/games/NXDoom/CMakeLists.txt b/games/NXDoom/CMakeLists.txt index 2284f923b..f8644fc3c 100644 --- a/games/NXDoom/CMakeLists.txt +++ b/games/NXDoom/CMakeLists.txt @@ -196,6 +196,11 @@ if(CONFIG_GAMES_NXDOOM) list(APPEND SRCS src/i_rtttl_music.c) endif() + # Optional keys from a serial terminal + if(CONFIG_GAMES_NXDOOM_UARTKBD) + list(APPEND SRCS src/i_input_uartkbd.c) + endif() + set(INCDIRS src src/doom textscreen pcsound) nuttx_add_application( diff --git a/games/NXDoom/Kconfig.input b/games/NXDoom/Kconfig.input index 98ce5acd9..0da40578c 100644 --- a/games/NXDoom/Kconfig.input +++ b/games/NXDoom/Kconfig.input @@ -11,6 +11,16 @@ endchoice # Input method if GAMES_NXDOOM_KEYBOARD +config GAMES_NXDOOM_UARTKBD + bool "Keys from a serial terminal" + default n + ---help--- + Read the keys from a serial terminal instead of a keyboard driver. + KBDPATH then names a tty, such as /dev/console. A terminal reports + a key press only, so each press is released again once the terminal + stops repeating it. WASD or the arrow keys move, J fires, K runs, + L strafes, E or space uses, Q or Esc opens the menu. + config GAMES_NXDOOM_KBDPATH string "Keyboard device path" default "/dev/kbd0" diff --git a/games/NXDoom/Makefile b/games/NXDoom/Makefile index 2aa00d96a..3e42504a6 100644 --- a/games/NXDoom/Makefile +++ b/games/NXDoom/Makefile @@ -219,6 +219,12 @@ ifeq ($(CONFIG_GAMES_NXDOOM_RTTTL_MUSIC),y) CSRCS += $(COMMONSRCDIR)/i_rtttl_music.c endif +# If the keys come from a serial terminal, then add that source + +ifeq ($(CONFIG_GAMES_NXDOOM_UARTKBD),y) +CSRCS += $(COMMONSRCDIR)/i_input_uartkbd.c +endif + MAINSRC = $(COMMONSRCDIR)/i_main.c include $(APPDIR)/Application.mk diff --git a/games/NXDoom/src/i_input.c b/games/NXDoom/src/i_input.c index 566debe0a..d86c1c122 100644 --- a/games/NXDoom/src/i_input.c +++ b/games/NXDoom/src/i_input.c @@ -63,64 +63,6 @@ struct keyboard_dev g_kbd_dev = }; #endif -#if 0 -static const int g_scancode_translate_table[] = SCANCODE_TO_KEYS_ARRAY; - -/* Lookup table for mapping ASCII characters to their equivalent when - * shift is pressed on a US layout keyboard. This is the original table - * as found in the Doom sources, comments and all. - */ - -static const char shiftxform[] = -{ - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, - 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, - 31, ' ', '!', '"', '#', '$', '%', '&', - '"', /* shift-' */ - '(', ')', '*', '+', - '<', /* shift-, */ - '_', /* shift-- */ - '>', /* shift-. */ - '?', /* shift-/ */ - ')', /* shift-0 */ - '!', /* shift-1 */ - '@', /* shift-2 */ - '#', /* shift-3 */ - '$', /* shift-4 */ - '%', /* shift-5 */ - '^', /* shift-6 */ - '&', /* shift-7 */ - '*', /* shift-8 */ - '(', /* shift-9 */ - ':', - ':', /* shift-; */ - '<', - '+', /* shift-= */ - '>', '?', '@', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - '[', /* shift-[ */ - '!', /* shift-backslash - OH MY GOD DOES WATCOM SUCK */ - ']', /* shift-] */ - '"', '_', - '\'', /* shift-` */ - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', - 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', - '{', '|', '}', '~', 127 -}; - -/* If true, i_start_text_input() has been called, and we are populating - * the data3 field of ev_keydown events. - */ - -static boolean text_input_enabled = true; - -/* Bit mask of mouse button state. */ - -static unsigned int mouse_button_state = 0; -#endif - /* Disallow mouse and joystick movement to cause forward/backward * motion. Specified with the '-novert' command line parameter. * This is an int to allow saving to config file @@ -386,6 +328,10 @@ int get_kbd_event(struct keyboard_event_s *sample) return err; } +#ifdef CONFIG_GAMES_NXDOOM_UARTKBD + return uartkbd_get_event(g_kbd_dev.fd, sample); +#endif + /* Read events until we're out of them */ nbytes = read(g_kbd_dev.fd, sample, sizeof(*sample)); diff --git a/games/NXDoom/src/i_input.h b/games/NXDoom/src/i_input.h index 97fc81bf7..dceeffb67 100644 --- a/games/NXDoom/src/i_input.h +++ b/games/NXDoom/src/i_input.h @@ -74,4 +74,8 @@ void i_handle_mouse_event(void); int get_kbd_event(struct keyboard_event_s *sample); #endif +#ifdef CONFIG_GAMES_NXDOOM_UARTKBD +int uartkbd_get_event(int fd, struct keyboard_event_s *sample); +#endif + #endif /* __I_INPUT__ */ diff --git a/games/NXDoom/src/i_input_uartkbd.c b/games/NXDoom/src/i_input_uartkbd.c new file mode 100644 index 000000000..b33704775 --- /dev/null +++ b/games/NXDoom/src/i_input_uartkbd.c @@ -0,0 +1,285 @@ +/**************************************************************************** + * apps/games/NXDoom/src/i_input_uartkbd.c + * + * SPDX-License-Identifier: GPLv2 + * + * Copyright(C) 1993-1996 Id Software, Inc. + * Copyright(C) 2005-2014 Simon Howard + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * DESCRIPTION: + * Keyboard events from a serial terminal, for a board without a keyboard + * driver. + * + ****************************************************************************/ + +/**************************************************************************** + * Included Files + ****************************************************************************/ + +#include <stdbool.h> +#include <stdint.h> +#include <unistd.h> + +#include <nuttx/input/kbd_codec.h> +#include <nuttx/input/keyboard.h> + +#include "i_input.h" +#include "i_timer.h" + +/**************************************************************************** + * Pre-processor Definitions + ****************************************************************************/ + +#define UARTKBD_MAXHELD 8 +#define UARTKBD_HOLDMS 250 /* Release a key this long after its last byte */ + +/**************************************************************************** + * Private Types + ****************************************************************************/ + +struct uartkbd_held +{ + bool active; + uint32_t type; + uint32_t code; + int deadline; +}; + +/**************************************************************************** + * Private Data + ****************************************************************************/ + +static struct uartkbd_held g_held[UARTKBD_MAXHELD]; + +/**************************************************************************** + * Private Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: uartkbd_hold + * + * Description: + * A serial terminal reports a key press only, never its release, so a + * press is turned into a press event now and a release event once the + * terminal has stopped repeating the key for UARTKBD_HOLDMS. Returns + * true when the key was not already held, so that the caller emits the + * press. A key that finds no free slot is ignored, because its release + * could never be reported. + * + ****************************************************************************/ + +static bool uartkbd_hold(uint32_t type, uint32_t code) +{ + int now = i_get_time_ms(); + int i; + int freeslot = -1; + + for (i = 0; i < UARTKBD_MAXHELD; i++) + { + if (g_held[i].active && g_held[i].type == type && + g_held[i].code == code) + { + g_held[i].deadline = now + UARTKBD_HOLDMS; + return false; + } + + if (!g_held[i].active && freeslot < 0) + { + freeslot = i; + } + } + + if (freeslot < 0) + { + return false; + } + + g_held[freeslot].active = true; + g_held[freeslot].type = type; + g_held[freeslot].code = code; + g_held[freeslot].deadline = now + UARTKBD_HOLDMS; + return true; +} + +/**************************************************************************** + * Name: uartkbd_decode + * + * Description: + * Turns one byte from the terminal into a key, tracking the arrow key + * escape sequences. Returns true when *type and *code are valid. + * + * w a s d, arrow keys move / turn + * j fire (Ctrl) + * k run (Shift) + * l strafe (Alt) + * space, e use + * Esc, q menu / back + * everything else passed through as the typed character + * + ****************************************************************************/ + +static bool uartkbd_decode(int fd, uint8_t ch, uint32_t *type, + uint32_t *code) +{ + static bool in_csi; /* Inside an ESC [ or ESC O sequence */ + + if (in_csi) + { + in_csi = false; + *type = KEYBOARD_SPECPRESS; + switch (ch) + { + case 'A': + *code = KEYCODE_UP; + return true; + case 'B': + *code = KEYCODE_DOWN; + return true; + case 'C': + *code = KEYCODE_RIGHT; + return true; + case 'D': + *code = KEYCODE_LEFT; + return true; + default: + return false; + } + } + + if (ch == 0x1b) + { + char next; + + /* A lone Esc cannot be told from the start of an arrow sequence + * without waiting, so the sequence is only assumed when the next + * byte of it is already there. + */ + + if (read(fd, &next, 1) == 1 && (next == '[' || next == 'O')) + { + in_csi = true; + return false; + } + + *type = KEYBOARD_PRESS; + *code = 0x1b; + return true; + } + + *type = KEYBOARD_SPECPRESS; + switch (ch) + { + case 'w': + case 'W': + *code = KEYCODE_UP; + return true; + case 's': + case 'S': + *code = KEYCODE_DOWN; + return true; + case 'a': + case 'A': + *code = KEYCODE_LEFT; + return true; + case 'd': + case 'D': + *code = KEYCODE_RIGHT; + return true; + case 'j': + case 'J': + *code = KEYCODE_LCTRL; + return true; + case 'k': + case 'K': + *code = KEYCODE_LSHIFT; + return true; + case 'l': + case 'L': + *code = KEYCODE_LALT; + return true; + case 0x7f: + *code = KEYCODE_BACKDEL; + return true; + } + + *type = KEYBOARD_PRESS; + switch (ch) + { + case 'e': + case 'E': + *code = ' '; + return true; + case 'q': + case 'Q': + *code = 0x1b; + return true; + case '\r': + *code = '\n'; + return true; + default: + *code = ch; + return true; + } +} + +/**************************************************************************** + * Public Functions + ****************************************************************************/ + +/**************************************************************************** + * Name: uartkbd_get_event + * + * Description: + * Read a single synthesized keyboard event from the serial terminal + * that fd refers to. Returns 0 on success, EAGAIN if there is no event. + * + ****************************************************************************/ + +int uartkbd_get_event(int fd, struct keyboard_event_s *sample) +{ + int now; + uint8_t ch; + int i; + + /* Release any key that the terminal has stopped repeating */ + + now = i_get_time_ms(); + for (i = 0; i < UARTKBD_MAXHELD; i++) + { + if (g_held[i].active && now - g_held[i].deadline >= 0) + { + g_held[i].active = false; + sample->type = (g_held[i].type == KEYBOARD_SPECPRESS) ? + KEYBOARD_SPECREL : KEYBOARD_RELEASE; + sample->code = g_held[i].code; + return 0; + } + } + + /* Then take new bytes until one of them is a new key press */ + + while (read(fd, &ch, 1) == 1) + { + uint32_t type; + uint32_t code; + + if (uartkbd_decode(fd, ch, &type, &code) && uartkbd_hold(type, code)) + { + sample->type = type; + sample->code = code; + return 0; + } + } + + return EAGAIN; +}
