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
}