Input device from terminal

Description

What MCU/Processor/Board and compiler are you using?

LVGL simulator, VS Code on Linux, Platformio, emulator_64bits

What LVGL version are you using?

LVGL v9.1.0

What do you want to achieve?

I want to catch an input key from the keyboard, inserted from the terminal on VSCode.
I’m following the LearnLVGL Version 9 by Mair and I’m in the chapter where he’s explaining how to use the input devices on LVGL.

What have you tried so far?

I have a windows with a button, I want to click the button without the mouse, simply hitting ENTER from the keyboard. Then a message box should appear and hitting ENTER again it must close itself.
The actions will repeat every time I hit ENTER. I don’t want to use the mouse/touchscreen.

I’m able to compile the code and run it, but when it starts I get an infinte catch of the pressed key. It doesn’t stop unless I quit the simulation (see the following sections).

Code to reproduce

File ButtonDev.cpp:

#include "InputDevices.h"
#include "lvgl.h"
#include <stdio.h>
#include <stdint.h>

// I want to catch the input from the terminal.
//
#include "utils.h"

static void on_button_pressed_cb(lv_event_t * p_event);
static void on_key_press_cb(lv_indev_t * p_indev, lv_indev_data_t * p_data);

// How to operate without touchscreen or a mouse?
// We want the ENTER key of the keyboard received to open a windows at the first
// press, pressing again it closes the same window.
//
void
button_input (void)
{
    // Creating an input/output device.
    //
    lv_indev_t * p_key_input(lv_indev_create());

    // We can use:
    // LV_INDEV_TYPE_NONE - nothing
    // LV_INDEV_TYPE_POINTER - mouse/touchpad
    // LV_INDEV_TYPE_KEYPAD - keypad
    // LV_INDEV_TYPE_BUTTON - button
    // LV_INDEV_TYPE_ENCODER - encoder (rotation plus click)
    //
    lv_indev_set_type(p_key_input, LV_INDEV_TYPE_KEYPAD);

    // Reading the input device.
    //
    lv_indev_set_read_cb(p_key_input, on_key_press_cb);

    lv_obj_t * p_btn(lv_button_create(lv_screen_active()));
    lv_obj_t * p_lbl(lv_label_create(p_btn));
    lv_label_set_text(p_lbl, "Click me");

    // The object which must be controlled by input devices needs to be added to
    // a group, where there is a single focused object that receives the action.
    // We associate the button to the group.
    // Then we associate the group to the input device.
    //
    lv_group_t * p_grp(lv_group_create());
    lv_group_set_default(p_grp);
    lv_group_add_obj(p_grp, p_btn);
    lv_indev_set_group(p_key_input, p_grp);

    lv_obj_add_event_cb(p_btn, on_button_pressed_cb, LV_EVENT_CLICKED, p_grp);
}   /* button_input() */

static void
on_button_pressed_cb (lv_event_t * p_event)
{
    lv_group_t * p_grp((lv_group_t * ) lv_event_get_user_data(p_event));

    lv_obj_t * p_msg_box(lv_msgbox_create(lv_screen_active()));
    lv_msgbox_add_title(p_msg_box, "Got clicked");
    lv_msgbox_add_text(p_msg_box, "Button was clicked");
    
    // To close the window. We must change the focus because we have 2 objects
    // attached to the group.
    //
    lv_obj_t * p_close_btn(lv_msgbox_add_close_button(p_msg_box));
    lv_group_add_obj(p_grp, p_close_btn);
    lv_group_focus_obj(p_close_btn);
}   /* on_button_pressed_cb() */

static void
on_key_press_cb (lv_indev_t * p_indev, lv_indev_data_t * p_data)
{
    // Check if pressing a button.
    //
    int32_t key_code(get_key());

    if (0 != key_code)
    {
        lv_log("Key code pressed is %d\n", key_code);
        fflush(stdout);

        // We want to check if we're pressing ENTER.
        //
        if (10 == key_code)
        {
            // We need to tell the key has been pressed.
            //
            p_data->state = LV_INDEV_STATE_PRESSED;
            p_data->key = LV_KEY_ENTER;
        }
        else
        {
            p_data->state = LV_INDEV_STATE_RELEASED;
        }
    }
    else
    {
        // Key hasn't been pressed.
        //
        p_data->state = LV_INDEV_STATE_RELEASED;
    }
}   /* on_key_press_cb() */

File utils.cpp

#ifdef __linux__
#   include "utils.h"
#   include <termios.h>
#   include <unistd.h>
#   include <stdio.h>
#   include <fcntl.h>
#endif /* __linux__ */

int32_t
get_key (void)
{
    // Hold the current terminal configuration.
    //
    struct termios oldterm = {0};
    struct termios newterm = {0};

    // We want to get the current terminal settings.
    //
    tcgetattr(STDIN_FILENO, &oldterm);
    newterm = oldterm;

    // Set the terminal to disable two flags: we don't care about the new line
    // and we don't want the echo. So it doesn't block.
    //
    newterm.c_lflag &= ~(ICANON | ECHO);
    tcsetattr(STDIN_FILENO, TCSANOW, &newterm);

    // Obtain the file handler.
    //
    int32_t h_oldfile = fcntl(STDIN_FILENO, F_GETFL, 0);

    // Set it so it doesn't block.
    //
    fcntl(STDIN_FILENO, F_SETFL, h_oldfile | O_NONBLOCK);

    // After gettin the char, we set back the original configuration of the
    // file handler and terminal.
    //
    int32_t in_ch = getchar();
    tcsetattr(STDIN_FILENO, TCSANOW, &oldterm);
    fcntl(STDIN_FILENO, F_SETFL, h_oldfile);

    // Before return, we return the char and we clear the terminal.
    //
    if (EOF != in_ch)
    {
        ungetc(in_ch, stdin);
        return in_ch;
    }

    return 0;
}   /* get_key() */

Screenshot and/or video

This is the log when I press the ENTER key from the terminal:

Key code pressed is 10
Key code pressed is 10
Key code pressed is 10
Key code pressed is 10
Key code pressed is 10
Key code pressed is 10
... forever

And the log when I press another key (such as letter “a”), and then I press letter “o”:

Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
oKey code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97
Key code pressed is 97

It prints letter “o” but ignores it. It continues printing the letter code for “a”, which is 97.
The message box can’t be opened and closed from the terminal.

There are two problems:

  1. The button is repeatedly read as the same, also when released
  2. No other button can be read after that

I’ve just solved.

The problems appear to be due to ungetc(in_ch, stdin) because this function pushes back the character into the input buffer stream.

If I remove this called function it works like I want!

Here the log:

 Key code pressed is 32
 Key code pressed is 32
eKey code pressed is 101
eKey code pressed is 101

Key code pressed is 10

Key code pressed is 10

Key code pressed is 10

Key code pressed is 10
eKey code pressed is 101