mirror of
https://github.com/apache/nuttx-apps.git
synced 2026-08-01 20:29:00 +00:00
Merge 4f211f6d58 into 37a1f0e068
This commit is contained in:
commit
8366ced063
13 changed files with 839 additions and 129 deletions
|
|
@ -36,7 +36,7 @@ config EXAMPLES_HIDKBD_DEVNAME
|
|||
config EXAMPLES_HIDKBD_ENCODED
|
||||
bool "Encode Special Keys"
|
||||
default y
|
||||
depends on HIDKBD_ENCODED && LIBC_KBDCODEC
|
||||
depends on INPUT_KEYBOARD_BYTESTREAM
|
||||
---help---
|
||||
Decode special key press events in the user buffer. In this case,
|
||||
the example coded will use the interfaces defined in
|
||||
|
|
|
|||
|
|
@ -35,6 +35,8 @@
|
|||
#include <nuttx/usb/usbhost.h>
|
||||
|
||||
#ifdef CONFIG_EXAMPLES_HIDKBD_ENCODED
|
||||
# include <ctype.h>
|
||||
|
||||
# include <nuttx/streams.h>
|
||||
# include <nuttx/input/kbd_codec.h>
|
||||
#endif
|
||||
|
|
@ -55,7 +57,8 @@
|
|||
# define CONFIG_EXAMPLES_HIDKBD_DEVNAME "/dev/kbda"
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_HIDKBD_ENCODED) || !defined(CONFIG_LIBC_KBDCODEC)
|
||||
#if !defined(CONFIG_INPUT_KEYBOARD_BYTESTREAM) || \
|
||||
!defined(CONFIG_LIBC_KBDCODEC)
|
||||
# undef CONFIG_EXAMPLES_HIDKBD_ENCODED
|
||||
#endif
|
||||
|
||||
|
|
|
|||
|
|
@ -17,9 +17,7 @@ choice
|
|||
default EXAMPLES_LVGLTERM_INPUT_TOUCH
|
||||
---help---
|
||||
Select how the terminal receives keystrokes. Only one input source
|
||||
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.
|
||||
is built at a time.
|
||||
|
||||
config EXAMPLES_LVGLTERM_INPUT_TOUCH
|
||||
bool "On-screen keyboard (touch)"
|
||||
|
|
@ -28,37 +26,29 @@ 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_MATRIX
|
||||
bool "Matrix / upper-half keyboard (keyboard events)"
|
||||
config EXAMPLES_LVGLTERM_INPUT_KBD
|
||||
bool "Physical keyboard"
|
||||
depends on INPUT_KEYBOARD
|
||||
---help---
|
||||
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.
|
||||
Any keyboard registered with keyboard_register(): USB HID, a matrix,
|
||||
the simulator, virtio. The terminal does not need to know which one
|
||||
it is. Cursor Up and Down scroll the terminal, everything else goes
|
||||
to the shell.
|
||||
|
||||
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_MATRIX || EXAMPLES_LVGLTERM_INPUT_KBD_USB
|
||||
depends on EXAMPLES_LVGLTERM_INPUT_KBD
|
||||
---help---
|
||||
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).
|
||||
|
||||
Note that the USB HID driver names its devices /dev/kbda onwards,
|
||||
since they come and go as keyboards are plugged in.
|
||||
|
||||
choice
|
||||
prompt "LVGL Terminal font"
|
||||
default EXAMPLES_LVGLTERM_FONT_UNSCII_16
|
||||
|
|
|
|||
|
|
@ -26,15 +26,13 @@
|
|||
* 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:
|
||||
* Any keyboard registered with keyboard_register() works: USB HID, a
|
||||
* matrix, the simulator, virtio. Which one it is makes no difference here.
|
||||
*
|
||||
* - 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.
|
||||
* The device delivers struct keyboard_event_s events unless the kernel was
|
||||
* built with INPUT_KEYBOARD_BYTESTREAM, in which case it delivers the byte
|
||||
* stream that the keyboard codec defines. That is a property of the build
|
||||
* rather than of the hardware, so it is what selects the reader below.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -50,11 +48,9 @@
|
|||
#include <errno.h>
|
||||
#include <debug.h>
|
||||
|
||||
#ifdef CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD_MATRIX
|
||||
# include <nuttx/input/keyboard.h>
|
||||
#endif
|
||||
#include <nuttx/input/keyboard.h>
|
||||
|
||||
#ifdef CONFIG_EXAMPLES_LVGLTERM_INPUT_KBD_USB
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
# include <nuttx/streams.h>
|
||||
# include <nuttx/input/kbd_codec.h>
|
||||
#endif
|
||||
|
|
@ -67,17 +63,6 @@
|
|||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
/* Out-of-band key codes for the Fn + navigation cluster (cursor keys),
|
||||
* reported by keyboard drivers that follow this convention (for example the
|
||||
* M5Stack Cardputer). Up/Down scroll the terminal instead of going to the
|
||||
* shell; drivers that do not emit these codes keep normal behaviour.
|
||||
*/
|
||||
|
||||
#define KEY_UP 0x80
|
||||
#define KEY_DOWN 0x81
|
||||
#define KEY_LEFT 0x82
|
||||
#define KEY_RIGHT 0x83
|
||||
|
||||
#define SCROLL_STEP 24 /* Pixels scrolled per Up/Down keypress */
|
||||
|
||||
/****************************************************************************
|
||||
|
|
@ -134,6 +119,41 @@ static void scroll_terminal(bool up)
|
|||
lv_obj_scroll_by(g_output, 0, dy, LV_ANIM_OFF);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: handle_key
|
||||
*
|
||||
* Description:
|
||||
* Act on one key press. Cursor Up and Down scroll the terminal, anything
|
||||
* else goes to the shell.
|
||||
*
|
||||
* Input Parameters:
|
||||
* code - The character, or a value from enum kbd_keycode_e
|
||||
* special - True if the code is a keycode rather than a character. The
|
||||
* two ranges overlap, so this is what tells them apart.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void handle_key(uint32_t code, bool special)
|
||||
{
|
||||
if (special)
|
||||
{
|
||||
if (code == KEYCODE_UP)
|
||||
{
|
||||
scroll_terminal(true);
|
||||
}
|
||||
else if (code == KEYCODE_DOWN)
|
||||
{
|
||||
scroll_terminal(false);
|
||||
}
|
||||
|
||||
/* Any other special key has no meaning to a terminal */
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
feed_char(code);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
|
@ -171,38 +191,35 @@ void lvglterm_input_create(int argc, FAR char *argv[])
|
|||
|
||||
void lvglterm_input_poll(void)
|
||||
{
|
||||
char buf[64];
|
||||
ssize_t nread;
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
struct lib_meminstream_s stream;
|
||||
struct kbd_getstate_s state;
|
||||
uint8_t ch;
|
||||
int ret;
|
||||
#else
|
||||
FAR struct keyboard_event_s *evt;
|
||||
#endif
|
||||
|
||||
if (g_kfd < 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#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.
|
||||
*/
|
||||
|
||||
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)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
memset(&state, 0, sizeof(state));
|
||||
lib_meminstream(&stream, buf, nread);
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
ret = kbd_decode((FAR struct lib_instream_s *)&stream, &state, &ch);
|
||||
ret = kbd_decode(&stream.common, &state, &ch);
|
||||
if (ret == KBD_ERROR)
|
||||
{
|
||||
break;
|
||||
|
|
@ -210,51 +227,29 @@ void lvglterm_input_poll(void)
|
|||
|
||||
if (ret == KBD_PRESS)
|
||||
{
|
||||
feed_char((char)ch);
|
||||
handle_key(ch, false);
|
||||
}
|
||||
else if (ret == KBD_SPECPRESS)
|
||||
{
|
||||
if (ch == KEYCODE_UP)
|
||||
{
|
||||
scroll_terminal(true);
|
||||
}
|
||||
else if (ch == KEYCODE_DOWN)
|
||||
{
|
||||
scroll_terminal(false);
|
||||
}
|
||||
handle_key(ch, true);
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Upper-half keyboard: read() returns keyboard_event_s events */
|
||||
evt = (FAR struct keyboard_event_s *)buf;
|
||||
|
||||
struct keyboard_event_s evt;
|
||||
|
||||
while (read(g_kfd, &evt, sizeof(evt)) == (ssize_t)sizeof(evt))
|
||||
while (nread >= (ssize_t)sizeof(struct keyboard_event_s))
|
||||
{
|
||||
if (evt.type != KEYBOARD_PRESS)
|
||||
if (evt->type == KEYBOARD_PRESS)
|
||||
{
|
||||
continue;
|
||||
handle_key(evt->code, false);
|
||||
}
|
||||
else if (evt->type == KEYBOARD_SPECPRESS)
|
||||
{
|
||||
handle_key(evt->code, true);
|
||||
}
|
||||
|
||||
/* 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);
|
||||
nread -= sizeof(struct keyboard_event_s);
|
||||
evt++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -192,6 +192,38 @@ static int init_kbd_dev(struct keyboard_dev *dev)
|
|||
****************************************************************************/
|
||||
|
||||
static int translate_key(uint32_t keycode)
|
||||
{
|
||||
/* A keyboard driver reports Enter as the character that it produces, so
|
||||
* it never arrives as KEYCODE_ENTER. The game binds the menu to
|
||||
* KEY_ENTER, which is the carriage return, so the line feed that a
|
||||
* driver actually sends has to be folded onto it.
|
||||
*/
|
||||
|
||||
if (keycode == '\n')
|
||||
{
|
||||
return KEY_ENTER;
|
||||
}
|
||||
|
||||
return keycode;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: translate_speckey
|
||||
*
|
||||
* Description:
|
||||
* Translates a value from enum kbd_keycode_e into one from doomkeys.h.
|
||||
*
|
||||
* A special key arrives with a KEYBOARD_SPECPRESS or KEYBOARD_SPECREL
|
||||
* event rather than as a character: the keycodes overlap the character
|
||||
* range, so the event type is what tells an arrow key from the character
|
||||
* that shares its value.
|
||||
*
|
||||
* Return:
|
||||
* The translated key code, or zero if the key has no meaning to the game.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static int translate_speckey(uint32_t keycode)
|
||||
{
|
||||
switch (keycode)
|
||||
{
|
||||
|
|
@ -203,10 +235,76 @@ static int translate_key(uint32_t keycode)
|
|||
return KEY_UPARROW;
|
||||
case KEYCODE_DOWN:
|
||||
return KEY_DOWNARROW;
|
||||
|
||||
/* Drivers disagree on Enter: a USB HID keyboard reports the line feed
|
||||
* that the key produces, while the simulator reports KEYCODE_ENTER.
|
||||
* Both have to land on KEY_ENTER, so both are handled, here and in
|
||||
* translate_key().
|
||||
*/
|
||||
|
||||
case KEYCODE_ENTER:
|
||||
return KEY_ENTER;
|
||||
|
||||
case KEYCODE_BACKDEL:
|
||||
return KEY_BACKSPACE;
|
||||
case KEYCODE_FWDDEL:
|
||||
return KEY_DEL;
|
||||
case KEYCODE_INSERT:
|
||||
return KEY_INS;
|
||||
case KEYCODE_HOME:
|
||||
return KEY_HOME;
|
||||
case KEYCODE_END:
|
||||
return KEY_END;
|
||||
case KEYCODE_PAGEUP:
|
||||
return KEY_PGUP;
|
||||
case KEYCODE_PAGEDOWN:
|
||||
return KEY_PGDN;
|
||||
case KEYCODE_PAUSE:
|
||||
return KEY_PAUSE;
|
||||
case KEYCODE_CAPSLOCK:
|
||||
return KEY_CAPSLOCK;
|
||||
|
||||
/* Fire, run and strafe. The game binds these by default, so a
|
||||
* keyboard that does not report its modifiers cannot play it.
|
||||
*/
|
||||
|
||||
case KEYCODE_LCTRL:
|
||||
case KEYCODE_RCTRL:
|
||||
return KEY_RCTRL;
|
||||
case KEYCODE_LSHIFT:
|
||||
case KEYCODE_RSHIFT:
|
||||
return KEY_RSHIFT;
|
||||
case KEYCODE_LALT:
|
||||
case KEYCODE_RALT:
|
||||
return KEY_RALT;
|
||||
|
||||
case KEYCODE_F1:
|
||||
return KEY_F1;
|
||||
case KEYCODE_F2:
|
||||
return KEY_F2;
|
||||
case KEYCODE_F3:
|
||||
return KEY_F3;
|
||||
case KEYCODE_F4:
|
||||
return KEY_F4;
|
||||
case KEYCODE_F5:
|
||||
return KEY_F5;
|
||||
case KEYCODE_F6:
|
||||
return KEY_F6;
|
||||
case KEYCODE_F7:
|
||||
return KEY_F7;
|
||||
case KEYCODE_F8:
|
||||
return KEY_F8;
|
||||
case KEYCODE_F9:
|
||||
return KEY_F9;
|
||||
case KEYCODE_F10:
|
||||
return KEY_F10;
|
||||
case KEYCODE_F11:
|
||||
return KEY_F11;
|
||||
case KEYCODE_F12:
|
||||
return KEY_F12;
|
||||
|
||||
default:
|
||||
return keycode;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -319,24 +417,53 @@ void i_handle_keyboard_event(struct keyboard_event_s *kevent)
|
|||
*/
|
||||
|
||||
event_t event;
|
||||
bool press;
|
||||
|
||||
switch (kevent->type)
|
||||
{
|
||||
case KEYBOARD_PRESS:
|
||||
event.type = ev_keydown;
|
||||
case KEYBOARD_RELEASE:
|
||||
press = (kevent->type == KEYBOARD_PRESS);
|
||||
event.data1 = translate_key(kevent->code);
|
||||
event.data2 = get_localized_key(kevent->code);
|
||||
event.data3 = get_typed_char(kevent->code);
|
||||
|
||||
if (event.data1 != 0)
|
||||
{
|
||||
d_post_event(&event);
|
||||
}
|
||||
break;
|
||||
|
||||
case KEYBOARD_RELEASE:
|
||||
case KEYBOARD_SPECPRESS:
|
||||
case KEYBOARD_SPECREL:
|
||||
press = (kevent->type == KEYBOARD_SPECPRESS);
|
||||
event.data1 = translate_speckey(kevent->code);
|
||||
break;
|
||||
|
||||
default:
|
||||
return;
|
||||
}
|
||||
|
||||
if (event.data1 == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (press)
|
||||
{
|
||||
event.type = ev_keydown;
|
||||
|
||||
/* A special key has no printable character, so it contributes
|
||||
* nothing to data2 and data3.
|
||||
*/
|
||||
|
||||
if (kevent->type == KEYBOARD_PRESS)
|
||||
{
|
||||
event.data2 = get_localized_key(kevent->code);
|
||||
event.data3 = get_typed_char(kevent->code);
|
||||
}
|
||||
else
|
||||
{
|
||||
event.data2 = 0;
|
||||
event.data3 = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
event.type = ev_keyup;
|
||||
event.data1 = translate_key(kevent->code);
|
||||
|
||||
/* data2/data3 are initialized to zero for ev_keyup.
|
||||
* For ev_keydown it's the shifted Unicode character
|
||||
|
|
@ -347,16 +474,9 @@ void i_handle_keyboard_event(struct keyboard_event_s *kevent)
|
|||
|
||||
event.data2 = 0;
|
||||
event.data3 = 0;
|
||||
|
||||
if (event.data1 != 0)
|
||||
{
|
||||
d_post_event(&event);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
d_post_event(&event);
|
||||
}
|
||||
|
||||
void i_start_text_input(int x1, int y1, int x2, int y2)
|
||||
|
|
|
|||
|
|
@ -505,19 +505,11 @@ static void i_get_event(void)
|
|||
|
||||
while ((err = get_kbd_event(&kbdevent)) == 0)
|
||||
{
|
||||
switch (kbdevent.type)
|
||||
{
|
||||
case KEYBOARD_PRESS:
|
||||
/* All four event types are handled: dropping the special ones here
|
||||
* would silently lose the arrow keys.
|
||||
*/
|
||||
|
||||
/* deliberate fall-though */
|
||||
|
||||
case KEYBOARD_RELEASE:
|
||||
i_handle_keyboard_event(&kbdevent);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
i_handle_keyboard_event(&kbdevent);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ config MICROWINDOWS_KBD_EVENT
|
|||
|
||||
config MICROWINDOWS_KBD_RAW
|
||||
bool "Raw-mode keyboard driver"
|
||||
depends on LIBC_KBDCODEC
|
||||
depends on INPUT_KEYBOARD_BYTESTREAM
|
||||
---help---
|
||||
Reads raw byte stream from a character device (e.g., /dev/kbda)
|
||||
and decodes escape sequences via the kbd_codec library. Suitable
|
||||
|
|
|
|||
|
|
@ -1090,6 +1090,7 @@ config NSH_USBKBD
|
|||
bool "Use USB keyboard input"
|
||||
default n
|
||||
depends on NSH_CONSOLE && USBHOST_HIDKBD && !NSH_USBCONSOLE
|
||||
depends on INPUT_KEYBOARD_BYTESTREAM
|
||||
---help---
|
||||
Normally NSH uses the same device for stdin, stdout, and stderr. By
|
||||
default, that device is /dev/console. If this option is selected,
|
||||
|
|
@ -1098,6 +1099,9 @@ config NSH_USBKBD
|
|||
interface) and the data from the keyboard will drive NSH. NSH
|
||||
output (stdout and stderr) will still go to /dev/console.
|
||||
|
||||
NSH reads the keyboard as a stream of characters, which is what
|
||||
INPUT_KEYBOARD_BYTESTREAM makes a keyboard device deliver.
|
||||
|
||||
if NSH_USBKBD
|
||||
|
||||
config NSH_USBKBD_DEVNAME
|
||||
|
|
|
|||
35
system/kbd/CMakeLists.txt
Normal file
35
system/kbd/CMakeLists.txt
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# ##############################################################################
|
||||
# apps/system/kbd/CMakeLists.txt
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more contributor
|
||||
# license agreements. See the NOTICE file distributed with this work for
|
||||
# additional information regarding copyright ownership. The ASF licenses this
|
||||
# file to you under the Apache License, Version 2.0 (the "License"); you may not
|
||||
# use this file except in compliance with the License. You may obtain a copy of
|
||||
# the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations under
|
||||
# the License.
|
||||
#
|
||||
# ##############################################################################
|
||||
|
||||
if(CONFIG_SYSTEM_KBD)
|
||||
nuttx_add_application(
|
||||
NAME
|
||||
${CONFIG_SYSTEM_KBD_PROGNAME}
|
||||
PRIORITY
|
||||
${CONFIG_SYSTEM_KBD_PRIORITY}
|
||||
STACKSIZE
|
||||
${CONFIG_SYSTEM_KBD_STACKSIZE}
|
||||
MODULE
|
||||
${CONFIG_SYSTEM_KBD}
|
||||
SRCS
|
||||
kbd_main.c)
|
||||
endif()
|
||||
38
system/kbd/Kconfig
Normal file
38
system/kbd/Kconfig
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#
|
||||
# For a description of the syntax of this configuration file,
|
||||
# see the file kconfig-language.txt in the NuttX tools repository.
|
||||
#
|
||||
|
||||
config SYSTEM_KBD
|
||||
tristate "Keyboard dump"
|
||||
default n
|
||||
depends on INPUT_KEYBOARD
|
||||
---help---
|
||||
Dump what a keyboard reports. This reads any keyboard registered
|
||||
with keyboard_register(), whatever the hardware behind it is, so it
|
||||
is the tool to reach for when bringing up a new keyboard.
|
||||
|
||||
Replaces the hidkbd and keyboard examples, which did the same thing
|
||||
for one kind of keyboard each.
|
||||
|
||||
if SYSTEM_KBD
|
||||
|
||||
config SYSTEM_KBD_PROGNAME
|
||||
string "Program name"
|
||||
default "kbd"
|
||||
|
||||
config SYSTEM_KBD_PRIORITY
|
||||
int "Task priority"
|
||||
default 100
|
||||
|
||||
config SYSTEM_KBD_STACKSIZE
|
||||
int "Task stack size"
|
||||
default DEFAULT_TASK_STACKSIZE
|
||||
|
||||
config SYSTEM_KBD_DEVPATH
|
||||
string "Keyboard device path"
|
||||
default "/dev/kbd0"
|
||||
---help---
|
||||
The keyboard to read when none is named on the command line.
|
||||
|
||||
endif
|
||||
25
system/kbd/Make.defs
Normal file
25
system/kbd/Make.defs
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
############################################################################
|
||||
# apps/system/kbd/Make.defs
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
ifneq ($(CONFIG_SYSTEM_KBD),)
|
||||
CONFIGURED_APPS += $(APPDIR)/system/kbd
|
||||
endif
|
||||
32
system/kbd/Makefile
Normal file
32
system/kbd/Makefile
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
############################################################################
|
||||
# apps/system/kbd/Makefile
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
# contributor license agreements. See the NOTICE file distributed with
|
||||
# this work for additional information regarding copyright ownership. The
|
||||
# ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
# "License"); you may not use this file except in compliance with the
|
||||
# License. You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
#
|
||||
############################################################################
|
||||
|
||||
include $(APPDIR)/Make.defs
|
||||
|
||||
PROGNAME = $(CONFIG_SYSTEM_KBD_PROGNAME)
|
||||
PRIORITY = $(CONFIG_SYSTEM_KBD_PRIORITY)
|
||||
STACKSIZE = $(CONFIG_SYSTEM_KBD_STACKSIZE)
|
||||
MODULE = $(CONFIG_SYSTEM_KBD)
|
||||
|
||||
MAINSRC = kbd_main.c
|
||||
|
||||
include $(APPDIR)/Application.mk
|
||||
476
system/kbd/kbd_main.c
Normal file
476
system/kbd/kbd_main.c
Normal file
|
|
@ -0,0 +1,476 @@
|
|||
/****************************************************************************
|
||||
* apps/system/kbd/kbd_main.c
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed to the Apache Software Foundation (ASF) under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership. The
|
||||
* ASF licenses this file to you under the Apache License, Version 2.0 (the
|
||||
* "License"); you may not use this file except in compliance with the
|
||||
* License. You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
* License for the specific language governing permissions and limitations
|
||||
* under the License.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
/* Dump what a keyboard reports.
|
||||
*
|
||||
* Every keyboard registered with keyboard_register() is read the same way,
|
||||
* whatever the hardware behind it is: a USB HID keyboard, a matrix, the
|
||||
* simulator, virtio. So this works with all of them, and it is the place
|
||||
* to look when bringing up a new one.
|
||||
*
|
||||
* The device delivers struct keyboard_event_s events unless the kernel was
|
||||
* built with INPUT_KEYBOARD_BYTESTREAM, in which case it delivers the byte
|
||||
* stream that the keyboard codec defines. This follows that setting rather
|
||||
* than having a switch of its own.
|
||||
*
|
||||
* With -i it goes the other way and injects into a uinput keyboard, either
|
||||
* what it reads from its own stdin or every key of another keyboard. So an
|
||||
* application can be driven from a serial console with no keyboard hardware
|
||||
* at all, and a real keyboard and an injected one can drive it at the same
|
||||
* time, which neither can do on its own since an application opens a single
|
||||
* device.
|
||||
*/
|
||||
|
||||
/****************************************************************************
|
||||
* Included Files
|
||||
****************************************************************************/
|
||||
|
||||
#include <nuttx/config.h>
|
||||
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <nuttx/input/keyboard.h>
|
||||
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
# include <nuttx/streams.h>
|
||||
# include <nuttx/input/kbd_codec.h>
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Pre-processor Definitions
|
||||
****************************************************************************/
|
||||
|
||||
#define KBD_READ_SIZE 64
|
||||
|
||||
/****************************************************************************
|
||||
* Private Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kbd_typename
|
||||
****************************************************************************/
|
||||
|
||||
static FAR const char *kbd_typename(uint32_t type)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case KEYBOARD_PRESS:
|
||||
return "press";
|
||||
|
||||
case KEYBOARD_RELEASE:
|
||||
return "release";
|
||||
|
||||
case KEYBOARD_SPECPRESS:
|
||||
return "specpress";
|
||||
|
||||
case KEYBOARD_SPECREL:
|
||||
return "specrel";
|
||||
|
||||
default:
|
||||
return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kbd_show
|
||||
*
|
||||
* Description:
|
||||
* Print one key. A normal key carries a character, a special key carries
|
||||
* a value from enum kbd_keycode_e, and the type is what says which.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static void kbd_show(uint32_t code, uint32_t type)
|
||||
{
|
||||
if (type == KEYBOARD_PRESS || type == KEYBOARD_RELEASE)
|
||||
{
|
||||
printf("%-9s code %3" PRIu32 " '%c'\n", kbd_typename(type), code,
|
||||
isprint(code) ? (int)code : '.');
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%-9s keycode %" PRIu32 "\n", kbd_typename(type), code);
|
||||
}
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kbd_dump
|
||||
*
|
||||
* Description:
|
||||
* Print everything in one read.
|
||||
*
|
||||
* Returned Value:
|
||||
* The number of keys printed.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
static size_t kbd_dump(FAR char *buffer, size_t nbytes)
|
||||
{
|
||||
struct lib_meminstream_s stream;
|
||||
struct kbd_getstate_s state;
|
||||
uint8_t ch;
|
||||
size_t nkeys = 0;
|
||||
int ret;
|
||||
|
||||
lib_meminstream(&stream, buffer, nbytes);
|
||||
memset(&state, 0, sizeof(state));
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
ret = kbd_decode(&stream.common, &state, &ch);
|
||||
if (ret == KBD_ERROR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
kbd_show(ch, ret);
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
}
|
||||
#else
|
||||
static size_t kbd_dump(FAR char *buffer, size_t nbytes)
|
||||
{
|
||||
FAR struct keyboard_event_s *key = (FAR struct keyboard_event_s *)buffer;
|
||||
size_t nkeys = 0;
|
||||
|
||||
if ((nbytes % sizeof(struct keyboard_event_s)) != 0)
|
||||
{
|
||||
fprintf(stderr, "kbd: short read of %zu bytes, ignoring\n", nbytes);
|
||||
return 0;
|
||||
}
|
||||
|
||||
while (nbytes >= sizeof(struct keyboard_event_s))
|
||||
{
|
||||
kbd_show(key->code, key->type);
|
||||
|
||||
nbytes -= sizeof(struct keyboard_event_s);
|
||||
key++;
|
||||
nkeys++;
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
}
|
||||
#endif
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kbd_inject
|
||||
*
|
||||
* Description:
|
||||
* Turn each byte read from stdin into a key press and release on the
|
||||
* given device. The device has to be a uinput keyboard: a real one has
|
||||
* nothing to inject into.
|
||||
*
|
||||
* Note that this always writes struct keyboard_event_s. Injection goes
|
||||
* through the lower half write method, which is not affected by
|
||||
* INPUT_KEYBOARD_BYTESTREAM.
|
||||
*
|
||||
* Returned Value:
|
||||
* The number of keys injected.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static bool kbd_emit(int fd, uint32_t code, uint32_t type)
|
||||
{
|
||||
struct keyboard_event_s key;
|
||||
|
||||
key.code = code;
|
||||
key.type = type;
|
||||
|
||||
if (write(fd, &key, sizeof(key)) != (ssize_t)sizeof(key))
|
||||
{
|
||||
fprintf(stderr, "kbd: inject failed: %d\n", errno);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static size_t kbd_inject(int fd)
|
||||
{
|
||||
unsigned char buf[KBD_READ_SIZE];
|
||||
uint32_t code;
|
||||
ssize_t nread;
|
||||
ssize_t i;
|
||||
size_t nkeys = 0;
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
nread = read(STDIN_FILENO, buf, sizeof(buf));
|
||||
if (nread <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < nread; i++)
|
||||
{
|
||||
/* A terminal sends a carriage return where a keyboard would
|
||||
* send a line feed.
|
||||
*/
|
||||
|
||||
code = (buf[i] == '\r') ? '\n' : buf[i];
|
||||
|
||||
if (!kbd_emit(fd, code, KEYBOARD_PRESS))
|
||||
{
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
kbd_emit(fd, code, KEYBOARD_RELEASE);
|
||||
nkeys++;
|
||||
}
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Name: kbd_forward
|
||||
*
|
||||
* Description:
|
||||
* Copy every key from one keyboard onto another. Pointing an
|
||||
* application at the destination lets a real keyboard and an injected one
|
||||
* drive it at the same time, which neither can do on its own since an
|
||||
* application opens a single device.
|
||||
*
|
||||
* Returned Value:
|
||||
* The number of keys forwarded.
|
||||
*
|
||||
****************************************************************************/
|
||||
|
||||
static size_t kbd_forward(int fd, int srcfd)
|
||||
{
|
||||
char buf[KBD_READ_SIZE];
|
||||
ssize_t nread;
|
||||
size_t nkeys = 0;
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
struct lib_meminstream_s stream;
|
||||
struct kbd_getstate_s state;
|
||||
uint8_t ch;
|
||||
int ret;
|
||||
#else
|
||||
FAR struct keyboard_event_s *key;
|
||||
#endif
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
nread = read(srcfd, buf, sizeof(buf));
|
||||
if (nread <= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
|
||||
/* The source speaks the byte stream and the destination only takes
|
||||
* events, so this decodes rather than copies. The decoder returns
|
||||
* the event type directly.
|
||||
*/
|
||||
|
||||
memset(&state, 0, sizeof(state));
|
||||
lib_meminstream(&stream, buf, nread);
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
ret = kbd_decode(&stream.common, &state, &ch);
|
||||
if (ret == KBD_ERROR)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
if (!kbd_emit(fd, ch, ret))
|
||||
{
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
nkeys++;
|
||||
}
|
||||
#else
|
||||
key = (FAR struct keyboard_event_s *)buf;
|
||||
|
||||
while (nread >= (ssize_t)sizeof(struct keyboard_event_s))
|
||||
{
|
||||
if (!kbd_emit(fd, key->code, key->type))
|
||||
{
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
nread -= sizeof(struct keyboard_event_s);
|
||||
key++;
|
||||
nkeys++;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
return nkeys;
|
||||
}
|
||||
|
||||
/****************************************************************************
|
||||
* Public Functions
|
||||
****************************************************************************/
|
||||
|
||||
/****************************************************************************
|
||||
* Name: main
|
||||
****************************************************************************/
|
||||
|
||||
int main(int argc, FAR char *argv[])
|
||||
{
|
||||
FAR const char *devpath = CONFIG_SYSTEM_KBD_DEVPATH;
|
||||
FAR const char *srcpath = NULL;
|
||||
char buffer[KBD_READ_SIZE];
|
||||
bool inject = false;
|
||||
size_t nkeys = 0;
|
||||
size_t limit = 0;
|
||||
ssize_t nbytes;
|
||||
int argi = 1;
|
||||
int srcfd;
|
||||
int fd;
|
||||
|
||||
if (argi < argc && strcmp(argv[argi], "-i") == 0)
|
||||
{
|
||||
inject = true;
|
||||
argi++;
|
||||
}
|
||||
|
||||
if (argi < argc)
|
||||
{
|
||||
devpath = argv[argi++];
|
||||
}
|
||||
|
||||
if (argi < argc)
|
||||
{
|
||||
/* When injecting, the extra argument is the keyboard to forward from
|
||||
* rather than a count: there is nothing to count.
|
||||
*/
|
||||
|
||||
if (inject)
|
||||
{
|
||||
srcpath = argv[argi];
|
||||
}
|
||||
else
|
||||
{
|
||||
limit = strtoul(argv[argi], NULL, 10);
|
||||
}
|
||||
}
|
||||
|
||||
/* Wait for the device. A USB keyboard appears when it is plugged in, so
|
||||
* this is the normal way to start rather than an error path.
|
||||
*/
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
fd = open(devpath, (inject ? O_WRONLY : O_RDONLY) | O_CLOEXEC);
|
||||
if (fd >= 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
printf("kbd: waiting for %s: %d\n", devpath, errno);
|
||||
fflush(stdout);
|
||||
sleep(3);
|
||||
}
|
||||
|
||||
if (inject)
|
||||
{
|
||||
if (srcpath == NULL)
|
||||
{
|
||||
printf("kbd: injecting into %s, type to send, Ctrl-D to stop\n",
|
||||
devpath);
|
||||
fflush(stdout);
|
||||
|
||||
nkeys = kbd_inject(fd);
|
||||
}
|
||||
else
|
||||
{
|
||||
srcfd = open(srcpath, O_RDONLY | O_CLOEXEC);
|
||||
if (srcfd < 0)
|
||||
{
|
||||
fprintf(stderr, "kbd: open %s failed: %d\n", srcpath, errno);
|
||||
close(fd);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
printf("kbd: forwarding %s into %s\n", srcpath, devpath);
|
||||
fflush(stdout);
|
||||
|
||||
nkeys = kbd_forward(fd, srcfd);
|
||||
close(srcfd);
|
||||
}
|
||||
|
||||
goto done;
|
||||
}
|
||||
|
||||
printf("kbd: reading %s, %s\n", devpath,
|
||||
#ifdef CONFIG_INPUT_KEYBOARD_BYTESTREAM
|
||||
"byte stream"
|
||||
#else
|
||||
"keyboard events"
|
||||
#endif
|
||||
);
|
||||
fflush(stdout);
|
||||
|
||||
for (; ; )
|
||||
{
|
||||
nbytes = read(fd, buffer, sizeof(buffer));
|
||||
if (nbytes < 0)
|
||||
{
|
||||
if (errno == EINTR)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fprintf(stderr, "kbd: read %s failed: %d\n", devpath, errno);
|
||||
break;
|
||||
}
|
||||
else if (nbytes == 0)
|
||||
{
|
||||
/* The keyboard is gone */
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
nkeys += kbd_dump(buffer, nbytes);
|
||||
|
||||
if (limit > 0 && nkeys >= limit)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
done:
|
||||
close(fd);
|
||||
printf("kbd: %zu keys\n", nkeys);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue