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;
+}

Reply via email to