Description
I’m having a fair amount of difficulty getting an encoder to “click” a button. I have two device nodes: /dev/input/event0 which corresponds to the encoder axis, and I have /dev/input/event1 which corresponds to the “click” mechanism.
When I start my program, I am able to change focus between two buttons. Once I click one of them, though, the program no longer responds to the axis input, and the focus remains on the clicked button. Long-pressing does nothing. I have further confirmed through GDB that the lv_timer_handler() is “canceling” any long-presses. Shortly after my callback sends the LV_INDEV_STATE_PRESSED, I find that the callback forces it back to “released” even if I have the button held down as I run the debugger.
What MCU/Processor/Board and compiler are you using?
Luckfox Lyra B (embedded Linux)
What do you want to achieve?
I want the encoder to function as described in the documentation. Specifically, clicking a button should not prevent me from changing the focus to another one.
What have you tried so far?
All of the following include adding my lv_indev_t * and buttons to an lv_group_t*.
- One callback to handle both encoder and encoder button
- Separate callbacks for both encoder and button
- Changing the button type to LV_INDEV_TYPE_KEYPAD
- Adding a dummy event callback to both buttons
- Forcing the group to not allow editing/freezing
- Setting the indev long press time
Code to reproduce
Add the relevant code snippets here.
NOTE Not all of the code here is seen in the repro video. I can provide that if it is necessary for further analysis. This should be able to reproduce the behavior seen.
void encoder_read(lv_indev_t*, lv_indev_data_t*);
void *tick_thread(void *args)
{
while(1) {
usleep(5000); /* Sleep for 5000 microseconds (5 milliseconds) */
lv_tick_inc(5); /* Tell LVGL that 5 milliseconds have elapsed */
}
}
static int encoder_wheel_fd, encoder_btn_fd;
int main(int argc, const char ** argv) {
/* DRM and Display Init */
const char *device = "/dev/dri/card0";
lv_display_t *disp = lv_linux_drm_create();
lv_linux_drm_set_file(disp, device, -1);
/* Create dummy Encoder input (because LVGL doesn't handle evdev correctly) */
lv_group_t * g = lv_group_create();
lv_indev_t *scroll = lv_indev_create();
lv_indev_set_type(scroll, LV_INDEV_TYPE_ENCODER);
lv_indev_set_read_cb(scroll, encoder_read);
lv_indev_set_group(scroll, g);
/* Use evdev to open encoder nodes */
encoder_wheel_fd = open("/dev/input/event0", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
encoder_btn_fd = open("/dev/input/event1", O_RDONLY | O_NONBLOCK | O_CLOEXEC);
if (encoder_wheel_fd < 0 || encoder_btn_fd < 0)
{
LV_LOG_WARN("Encoder failed to initialize fully");
}
/* Create Menu Buttons */
lv_style_t style_btn_press;
lv_style_init(&style_btn_press);
lv_style_set_border_color(&style_btn_press, lv_color_make(0xFF, 0xFF, 0xFF));
lv_style_set_border_width(&style_btn_press, 2);
lv_obj_t * menuicon1 = lv_imagebutton_create(lv_screen_active());
lv_imagebutton_set_src(menuicon1, LV_IMAGEBUTTON_STATE_RELEASED, NULL, "A:/menu.png", NULL);
lv_obj_add_style(menuicon1, &style_btn_press, LV_STATE_FOCUSED);
lv_obj_align(menuicon1, LV_ALIGN_BOTTOM_MID, -64, 0);
lv_group_add_obj(g, menuicon1);
lv_obj_t * menuicon = lv_imagebutton_create(lv_screen_active());
lv_imagebutton_set_src(menuicon, LV_IMAGEBUTTON_STATE_RELEASED, NULL, "A:/menu.png", NULL);
lv_obj_add_style(menuicon, &style_btn_press, LV_STATE_FOCUSED);
lv_obj_align(menuicon, LV_ALIGN_BOTTOM_MID, 0, 0);
lv_group_add_obj(g, menuicon);
pthread_t thread_id;
pthread_create(&thread_id, NULL, tick_thread, NULL);
/* Handle LVGL tasks */
while (1) {
lv_timer_handler();
usleep(5000);
}
close(encoder_wheel_fd);
close (encoder_btn_fd);
return 0;
}
void encoder_read(lv_indev_t * indev, lv_indev_data_t * data)
{
struct input_event ev1;
ssize_t n1 = read(encoder_wheel_fd, &ev1, sizeof(struct input_event));
struct input_event ev2;
ssize_t n2 = read(encoder_btn_fd, &ev2, sizeof(struct input_event));
if (n1 == (ssize_t)sizeof(struct input_event) && ev1.type != 0)
{
data->enc_diff = ev1.value;
}
if (n2 == (ssize_t)sizeof(struct input_event) && ev2.type != 0)
{
if (ev2.value == 0)
{
data->state = LV_INDEV_STATE_PRESSED;
data->key = LV_KEY_ENTER;
}
else
{
data->state = LV_INDEV_STATE_RELEASED;
}
}
}
Screenshot and/or video
repro_vid.zip (3.3 MB)