This is an automated email from the ASF dual-hosted git repository.

xiaoxiang781216 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/nuttx-apps.git


The following commit(s) were added to refs/heads/master by this push:
     new dd24407c7 examples/lvglterm: add USB HID keyboard input with cursor 
scrolling
dd24407c7 is described below

commit dd24407c71b4de06a033b6529e3bcb07faa25e7e
Author: Jorge Guzman <[email protected]>
AuthorDate: Mon Jul 6 17:34:37 2026 -0300

    examples/lvglterm: add USB HID keyboard input with cursor scrolling
    
    Rework the terminal input selection into an explicit three-way Kconfig
    choice so the format each keyboard delivers is picked up front:
    
      - On-screen keyboard (touch) - default, unchanged behaviour.
      - Matrix / upper-half keyboard - reads struct keyboard_event_s events
        (for example the M5Stack Cardputer matrix on /dev/kbd0).
      - USB HID keyboard - reads a byte stream (for example /dev/kbda with
        CONFIG_USBHOST_HIDKBD).
    
    The physical-keyboard variant now polls the device non-blocking from the
    LVGL thread (no dedicated task), mirroring the touch variant.  The USB
    path decodes the stream with the keyboard codec, so with a driver built
    for CONFIG_HIDKBD_ENCODED the Up/Down cursor keys scroll the terminal;
    a plain-ASCII stream still works as ordinary key presses.
    
    Signed-off-by: Jorge Guzman <[email protected]>
---
 examples/lvglterm/CMakeLists.txt |   6 +-
 examples/lvglterm/Kconfig        |  34 ++++--
 examples/lvglterm/Makefile       |   2 +-
 examples/lvglterm/lvglterm.c     |   4 +-
 examples/lvglterm/lvglterm_kbd.c | 240 ++++++++++++++++++++++-----------------
 5 files changed, 166 insertions(+), 120 deletions(-)

diff --git a/examples/lvglterm/CMakeLists.txt b/examples/lvglterm/CMakeLists.txt
index 22650f82f..c3f84b26e 100644
--- a/examples/lvglterm/CMakeLists.txt
+++ b/examples/lvglterm/CMakeLists.txt
@@ -21,10 +21,10 @@
 # 
##############################################################################
 
 if(CONFIG_EXAMPLES_LVGLTERM)
-  if(CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD)
-    set(lvglterm_input lvglterm_kbd.c)
-  else()
+  if(CONFIG_EXAMPLES_LVGLTERM_INPUT_TOUCH)
     set(lvglterm_input lvglterm_touch.c)
+  else()
+    set(lvglterm_input lvglterm_kbd.c)
   endif()
 
   nuttx_add_application(
diff --git a/examples/lvglterm/Kconfig b/examples/lvglterm/Kconfig
index 718af98b6..ca393e845 100644
--- a/examples/lvglterm/Kconfig
+++ b/examples/lvglterm/Kconfig
@@ -17,7 +17,9 @@ choice
        default EXAMPLES_LVGLTERM_INPUT_TOUCH
        ---help---
                Select how the terminal receives keystrokes.  Only one input 
source
-               is built at a time.
+               is built at a time.  The physical-keyboard options differ in 
the data
+               the keyboard device delivers on read(), so pick the one that 
matches
+               the hardware.
 
 config EXAMPLES_LVGLTERM_INPUT_TOUCH
        bool "On-screen keyboard (touch)"
@@ -26,24 +28,36 @@ config EXAMPLES_LVGLTERM_INPUT_TOUCH
                command line is typed and submitted with Enter.  This is the 
default
                and matches the original behaviour.
 
-config EXAMPLES_LVGLTERM_INPUT_KBD
-       bool "Physical keyboard (/dev/kbdN)"
+config EXAMPLES_LVGLTERM_INPUT_KBD_MATRIX
+       bool "Matrix / upper-half keyboard (keyboard events)"
        depends on INPUT_KEYBOARD
        ---help---
-               Input comes from a physical keyboard registered as a /dev/kbdN
-               device; keystrokes are streamed to the shell and the output 
fills
-               the whole screen.
+               Physical keyboard registered through the INPUT_KEYBOARD upper 
half,
+               whose read() returns struct keyboard_event_s events (for 
example the
+               M5Stack Cardputer matrix keyboard on /dev/kbd0).  Fn Up/Down 
scroll
+               the terminal.
+
+config EXAMPLES_LVGLTERM_INPUT_KBD_USB
+       bool "USB HID keyboard (byte stream)"
+       depends on USBHOST_HIDKBD
+       select LIBC_KBDCODEC
+       ---help---
+               USB HID keyboard (for example on /dev/kbda with 
CONFIG_USBHOST_HIDKBD).
+               read() returns a byte stream that is decoded with the keyboard 
codec:
+               normal keys go to the shell and, when the driver is built with
+               CONFIG_HIDKBD_ENCODED, the Up/Down cursor keys scroll the 
terminal.
 
 endchoice
 
 config EXAMPLES_LVGLTERM_KBD_DEV
        string "Keyboard device path"
+       default "/dev/kbda" if EXAMPLES_LVGLTERM_INPUT_KBD_USB
        default "/dev/kbd0"
-       depends on EXAMPLES_LVGLTERM_INPUT_KBD
+       depends on EXAMPLES_LVGLTERM_INPUT_KBD_MATRIX || 
EXAMPLES_LVGLTERM_INPUT_KBD_USB
        ---help---
-               Keyboard device the terminal reads key events from.  Can also be
-               overridden at run time by passing the path as the first 
command-line
-               argument (useful when more than one keyboard is present).
+               Keyboard device the terminal reads from.  Can also be 
overridden at
+               run time by passing the path as the first command-line argument
+               (useful when more than one keyboard is present).
 
 choice
        prompt "LVGL Terminal font"
diff --git a/examples/lvglterm/Makefile b/examples/lvglterm/Makefile
index 314351f9a..0d4c5d0c2 100644
--- a/examples/lvglterm/Makefile
+++ b/examples/lvglterm/Makefile
@@ -32,7 +32,7 @@ MODULE = $(CONFIG_EXAMPLES_LVGLTERM)
 
 MAINSRC = lvglterm.c
 
-ifeq ($(CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD),y)
+ifneq ($(CONFIG_EXAMPLES_LVGLTERM_INPUT_TOUCH),y)
 CSRCS = lvglterm_kbd.c
 else
 CSRCS = lvglterm_touch.c
diff --git a/examples/lvglterm/lvglterm.c b/examples/lvglterm/lvglterm.c
index 0814e9afe..f2dcb1f10 100644
--- a/examples/lvglterm/lvglterm.c
+++ b/examples/lvglterm/lvglterm.c
@@ -304,7 +304,7 @@ static int create_terminal(int argc, FAR char *argv[])
   if (pipe(g_nsh_stdin) < 0 || pipe(g_nsh_stdout) < 0 ||
       pipe(g_nsh_stderr) < 0)
     {
-      _err("pipe failed: %d\n", errno);
+      fprintf(stderr, "pipe failed: %d\n", errno);
       return ERROR;
     }
 
@@ -324,7 +324,7 @@ static int create_terminal(int argc, FAR char *argv[])
   if (ret < 0)
     {
       int errcode = errno;
-      _err("posix_spawn failed: %d\n", errcode);
+      fprintf(stderr, "posix_spawn failed: %d\n", errcode);
       return -errcode;
     }
 
diff --git a/examples/lvglterm/lvglterm_kbd.c b/examples/lvglterm/lvglterm_kbd.c
index 4c18aed7d..08ae83612 100644
--- a/examples/lvglterm/lvglterm_kbd.c
+++ b/examples/lvglterm/lvglterm_kbd.c
@@ -20,11 +20,21 @@
  *
  ****************************************************************************/
 
-/* Physical-keyboard input variant of the LVGL terminal: a task reads key
- * events from a /dev/kbdN keyboard device and streams them to NSH; the shell
- * output fills the whole screen.  Works on any board with a keyboard driver;
- * the device defaults to CONFIG_EXAMPLES_LVGLTERM_KBD_DEV and can be
- * overridden by the first command-line argument.
+/* Physical-keyboard input variant of the LVGL terminal: the keyboard device
+ * is polled (non-blocking) from the LVGL thread and key presses are streamed
+ * to NSH; the shell output fills the whole screen.  Works on any board with
+ * a keyboard driver; the device defaults to CONFIG_EXAMPLES_LVGLTERM_KBD_DEV
+ * and can be overridden by the first command-line argument.
+ *
+ * Two device flavours are supported, selected at build time:
+ *
+ *   - Upper-half keyboards (INPUT_KEYBOARD, e.g. the M5Stack Cardputer
+ *     matrix on /dev/kbd0) deliver struct keyboard_event_s events; the Fn
+ *     Up/Down keys are handled locally as scroll requests.
+ *   - USB HID keyboards (EXAMPLES_LVGLTERM_INPUT_KBD_USB, e.g. /dev/kbda)
+ *     deliver a byte stream decoded with the NuttX keyboard codec: normal
+ *     keys are forwarded to the shell and, when the driver is built with
+ *     CONFIG_HIDKBD_ENCODED, the Up/Down cursor keys scroll the terminal.
  */
 
 /****************************************************************************
@@ -34,13 +44,20 @@
 #include <nuttx/config.h>
 #include <sys/types.h>
 #include <stdint.h>
+#include <string.h>
 #include <unistd.h>
 #include <fcntl.h>
-#include <sched.h>
 #include <errno.h>
 #include <debug.h>
 
-#include <nuttx/input/keyboard.h>
+#ifdef CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD_MATRIX
+#  include <nuttx/input/keyboard.h>
+#endif
+
+#ifdef CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD_USB
+#  include <nuttx/streams.h>
+#  include <nuttx/input/kbd_codec.h>
+#endif
 
 #include <lvgl/lvgl.h>
 
@@ -67,8 +84,7 @@
  * Private Data
  ****************************************************************************/
 
-static int g_echo[2];              /* Keyboard task -> screen (local echo) */
-static volatile int g_scroll;      /* Pending scroll (lines), applied by LVGL 
*/
+static int g_kfd = -1;             /* Keyboard device fd (opened O_NONBLOCK) */
 static FAR const char *g_kbddev;   /* Keyboard device path (/dev/kbdN) */
 
 /****************************************************************************
@@ -76,87 +92,46 @@ static FAR const char *g_kbddev;   /* Keyboard device path 
(/dev/kbdN) */
  ****************************************************************************/
 
 /****************************************************************************
- * Name: kbd_task
+ * Name: feed_char
  *
  * Description:
- *   Read key presses from the keyboard device and forward them to NSH stdin
- *   (and to the echo pipe so the user sees what is typed).  The Fn Up/Down
- *   keys are handled locally as scroll requests.
+ *   Forward one character to NSH stdin and, for printable characters and
+ *   newline, echo it on screen.  Control keys such as backspace are echoed
+ *   by the shell's own line editor (via its stdout) to avoid handling them
+ *   twice.  Carriage return is normalised to newline.  Runs in the LVGL
+ *   thread, so it may touch the LVGL widgets directly.
  *
  ****************************************************************************/
 
-static int kbd_task(int argc, FAR char *argv[])
+static void feed_char(char ch)
 {
-  struct keyboard_event_s evt;
-  ssize_t nread;
-  int  kfd;
-  char ch;
-
-  kfd = open(g_kbddev, O_RDONLY);
-  if (kfd < 0)
+  if (ch == '\r')
     {
-      gerr("ERROR: open %s failed: %d\n", g_kbddev, errno);
-      return EXIT_FAILURE;
+      ch = '\n';
     }
 
-  for (; ; )
-    {
-      nread = read(kfd, &evt, sizeof(evt));
-      if (nread != (ssize_t)sizeof(evt))
-        {
-          if (nread <= 0)
-            {
-              usleep(10000);
-            }
-
-          continue;
-        }
-
-      if (evt.type != KEYBOARD_PRESS)
-        {
-          continue;
-        }
-
-      /* Fn navigation keys are handled locally: Up/Down scroll the terminal
-       * (applied by the LVGL thread) and are not sent to the shell.
-       * Left/Right are reserved and simply swallowed for now.
-       */
-
-      if (evt.code >= KEY_UP && evt.code <= KEY_RIGHT)
-        {
-          if (evt.code == KEY_UP)
-            {
-              g_scroll -= 1;
-            }
-          else if (evt.code == KEY_DOWN)
-            {
-              g_scroll += 1;
-            }
-
-          continue;
-        }
-
-      ch = (char)evt.code;
-      if (ch == '\r')
-        {
-          ch = '\n';
-        }
+  write(g_nsh_stdin[WRITE_PIPE], &ch, 1);
 
-      /* Always feed the shell.  Echo only printable characters and newline
-       * locally; control keys such as backspace are echoed by the shell's
-       * own line editor (via its stdout) to avoid handling them twice.
-       */
+  if (ch == '\n' || ((uint8_t)ch >= 0x20 && (uint8_t)ch < 0x7f))
+    {
+      lvglterm_add_output(&ch, 1);
+    }
+}
 
-      write(g_nsh_stdin[WRITE_PIPE], &ch, 1);
+/****************************************************************************
+ * Name: scroll_terminal
+ *
+ * Description:
+ *   Scroll the output text area up (older output) or down by one step.  Runs
+ *   in the LVGL thread.
+ *
+ ****************************************************************************/
 
-      if (ch == '\n' || ((uint8_t)ch >= 0x20 && (uint8_t)ch < 0x7f))
-        {
-          write(g_echo[WRITE_PIPE], &ch, 1);
-        }
-    }
+static void scroll_terminal(bool up)
+{
+  int32_t dy = up ? SCROLL_STEP : -SCROLL_STEP;
 
-  close(kfd);
-  return EXIT_SUCCESS;
+  lv_obj_scroll_by(g_output, 0, dy, LV_ANIM_OFF);
 }
 
 /****************************************************************************
@@ -168,7 +143,8 @@ static int kbd_task(int argc, FAR char *argv[])
  *
  * Description:
  *   Resolve the keyboard device (first command-line argument, else the
- *   configured default) and start the task that streams key presses to NSH.
+ *   configured default) and open it non-blocking so that it can be polled
+ *   from the LVGL thread.
  *
  ****************************************************************************/
 
@@ -176,53 +152,109 @@ void lvglterm_input_create(int argc, FAR char *argv[])
 {
   g_kbddev = (argc > 1) ? argv[1] : CONFIG_EXAMPLES_LVGLTERM_KBD_DEV;
 
-  if (pipe(g_echo) < 0)
+  g_kfd = open(g_kbddev, O_RDONLY | O_NONBLOCK);
+  if (g_kfd < 0)
     {
-      gerr("ERROR: echo pipe failed: %d\n", errno);
-      return;
+      gerr("ERROR: open %s failed: %d\n", g_kbddev, errno);
     }
-
-  /* The read end is polled by the LVGL timer and must not block */
-
-  fcntl(g_echo[READ_PIPE], F_SETFL,
-        fcntl(g_echo[READ_PIPE], F_GETFL) | O_NONBLOCK);
-
-  task_create("lvgltermkbd", SCHED_PRIORITY_DEFAULT, 2048, kbd_task, NULL);
 }
 
 /****************************************************************************
  * Name: lvglterm_input_poll
  *
  * Description:
- *   Apply any pending scroll and drain the local echo pipe onto the screen.
- *   Runs in the LVGL thread.
+ *   Drain any pending key presses from the keyboard device and forward them
+ *   to NSH (echoing what is typed on screen).  Runs in the LVGL thread from
+ *   the terminal's periodic timer, so the read must not block.
  *
  ****************************************************************************/
 
 void lvglterm_input_poll(void)
 {
-  char buf[64];
-  int  n;
-  int  s;
+  if (g_kfd < 0)
+    {
+      return;
+    }
 
-  /* Apply any pending scroll requested by the keyboard task (Up/Down).
-   * A negative value scrolls up (towards older output), positive scrolls
-   * down.  Consume only what we read so concurrent keypresses are not lost.
+#ifdef CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD_USB
+  /* USB HID keyboard: read() returns a byte stream that is decoded with the
+   * keyboard codec.  Normal keys are fed to the shell; the Up/Down cursor
+   * keys (only emitted when the driver is built with CONFIG_HIDKBD_ENCODED)
+   * scroll the terminal.  On a plain-ASCII stream every byte simply decodes
+   * to a normal key press, so this also works without encoding.
    */
 
-  s = g_scroll;
-  if (s != 0)
+  struct lib_meminstream_s stream;
+  struct kbd_getstate_s state;
+  char buf[64];
+  ssize_t nread;
+  uint8_t ch;
+  int ret;
+
+  nread = read(g_kfd, buf, sizeof(buf));
+  if (nread <= 0)
     {
-      g_scroll -= s;
-      lv_obj_scroll_by(g_output, 0, -s * SCROLL_STEP, LV_ANIM_OFF);
+      return;
+    }
+
+  memset(&state, 0, sizeof(state));
+  lib_meminstream(&stream, buf, nread);
+
+  for (; ; )
+    {
+      ret = kbd_decode((FAR struct lib_instream_s *)&stream, &state, &ch);
+      if (ret == KBD_ERROR)
+        {
+          break;
+        }
+
+      if (ret == KBD_PRESS)
+        {
+          feed_char((char)ch);
+        }
+      else if (ret == KBD_SPECPRESS)
+        {
+          if (ch == KEYCODE_UP)
+            {
+              scroll_terminal(true);
+            }
+          else if (ch == KEYCODE_DOWN)
+            {
+              scroll_terminal(false);
+            }
+        }
     }
+#else
+  /* Upper-half keyboard: read() returns keyboard_event_s events */
+
+  struct keyboard_event_s evt;
 
-  if (lvglterm_has_input(g_echo[READ_PIPE]))
+  while (read(g_kfd, &evt, sizeof(evt)) == (ssize_t)sizeof(evt))
     {
-      n = read(g_echo[READ_PIPE], buf, sizeof(buf));
-      if (n > 0)
+      if (evt.type != KEYBOARD_PRESS)
         {
-          lvglterm_add_output(buf, n);
+          continue;
         }
+
+      /* Fn navigation keys are handled locally: Up/Down scroll the terminal;
+       * Left/Right are reserved and simply swallowed for now.
+       */
+
+      if (evt.code >= KEY_UP && evt.code <= KEY_RIGHT)
+        {
+          if (evt.code == KEY_UP)
+            {
+              scroll_terminal(true);
+            }
+          else if (evt.code == KEY_DOWN)
+            {
+              scroll_terminal(false);
+            }
+
+          continue;
+        }
+
+      feed_char((char)evt.code);
     }
+#endif
 }

Reply via email to