From 0fadd1906a61ae0295872469539a53a51b42f740 Mon Sep 17 00:00:00 2001 From: Ondrej Jirman Date: Thu, 28 Mar 2024 20:39:07 +0100 Subject: [PATCH] cmd: tmenu: Add extlinux timeout support and button controls --- cmd/tmenu.c | 99 +++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 6 deletions(-) diff --git a/cmd/tmenu.c b/cmd/tmenu.c index 870f9cce33f..c246d2d2f00 100644 --- a/cmd/tmenu.c +++ b/cmd/tmenu.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0+ /* - * Copyright (C) 2018 Ondrej Jirman + * Copyright (C) 2018-2024 Ondrej Jirman */ #include #include @@ -14,6 +14,10 @@ #include #include #include +#include +#include +#include +#include #define VIDEO_FONT_HEIGHT 16 #define VIDEO_FONT_WIDTH 8 @@ -60,6 +64,7 @@ static void painter_rect_fill(struct painter* p, uint w, uint h, u32 color) } } +__maybe_unused static void painter_line_h(struct painter* p, int dx, u32 color) { if (dx < 0) { @@ -71,6 +76,7 @@ static void painter_line_h(struct painter* p, int dx, u32 color) } } +__maybe_unused static void painter_line_v(struct painter* p, int dy, u32 color) { if (dy < 0) { @@ -107,6 +113,7 @@ static void painter_bigchar(struct painter* p, char ch, u32 color) painter_move_dxy(p, VIDEO_FONT_WIDTH * 2, 0); } +__maybe_unused static void painter_char(struct painter* p, char ch, u32 color) { int i, row; @@ -368,6 +375,11 @@ struct tmenu_boot_item { int action; }; +#define MENU_COLOR_CHOSEN 0xff15801Fu +#define MENU_COLOR_HIGHLIGHT 0xffb19019u +#define MENU_COLOR_INACTIVE 0xff755f10u +#define MENU_COLOR_TEXT 0xffffffffu + static int do_tmenu_bootflow(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[]) { struct udevice *vdev, *tdev, *cdev; @@ -390,6 +402,8 @@ start_again: struct tmenu_boot_item items[64] = {}; int n_items = 0; int bmp_loaded = 0; + int timeout = 0; // 0 = no timeout, >0 number of 0.1s to wait + int autoselect = -1; // which item is autoselected by extlinux.conf (for timeout) struct bootflow *bflow; for (ret = bootflow_first_glob(&bflow); !ret; ret = bootflow_next_glob(&bflow)) { @@ -420,11 +434,18 @@ start_again: if (cfg->bmp && !bmp_loaded && get_pxe_file(ctx, cfg->bmp, image_load_addr) == 1) bmp_loaded = 1; + if (timeout < cfg->timeout) + timeout = cfg->timeout; + struct list_head *pos; list_for_each(pos, &cfg->labels) { struct pxe_label *label = list_entry(pos, struct pxe_label, list); struct tmenu_boot_item *it = &items[n_items++]; + // select default option for boot + if (autoselect < 0 && label->name && cfg->default_label && !strcmp(label->name, cfg->default_label)) + autoselect = n_items - 1; + //printf("label %s - %s %s\n", label->num, label->name, label->menu); it->id = n_items; @@ -550,8 +571,8 @@ start_again: i->y = top + row * (gap + item_h); i->w = item_w; i->h = item_h; - i->fill = 0xff755f10; - i->text_color = 0xffffffff; + i->fill = MENU_COLOR_INACTIVE; + i->text_color = MENU_COLOR_TEXT; i->id = idx; snprintf(i->text, sizeof i->text, "%s", items[idx].label); @@ -561,6 +582,7 @@ start_again: int selected = -1; int redraw = 1; + int highlighted = autoselect >= 0 ? autoselect : 0; ret = touchpanel_start(tdev); if (ret < 0) { @@ -568,8 +590,68 @@ start_again: goto out_restore_console; } + struct udevice *led_g = NULL, *led_r = NULL, *led_b = NULL; + uclass_get_device_by_name(UCLASS_LED, "led-red", &led_r); + uclass_get_device_by_name(UCLASS_LED, "led-green", &led_g); + uclass_get_device_by_name(UCLASS_LED, "led-blue", &led_b); + + enum { VOL_UP, VOL_DOWN, POWER }; + struct btn { struct udevice *dev; int prev; int cur; int press; int release; } btns[3] = {}; + button_get_by_label("Volume Up", &btns[VOL_UP].dev); + button_get_by_label("Volume Down", &btns[VOL_DOWN].dev); + button_get_by_label("Power", &btns[POWER].dev); + + int cycles = 0; next: while (1) { + for (int i = 0; i < ARRAY_SIZE(btns); i++) { + struct btn* b = &btns[i]; + b->prev = b->cur; + b->cur = b->dev && button_get_state(b->dev) == BUTTON_ON; + b->press = b->cur && !b->prev; + b->release = !b->cur && b->prev; + + // any button press cancels the timeout + if (b->cur) + timeout = 0; + } + + // menu up/down navigation feedback + if (btns[VOL_UP].press) { + highlighted--; + if (highlighted < 0) + highlighted = n_items - 1; + redraw = true; + } else if (btns[VOL_DOWN].press) { + highlighted = (highlighted + 1) % n_items; + redraw = true; + } + + if (redraw) + for (int i = 0; i < n_items; i++) + ui_items[i].fill = i == highlighted ? MENU_COLOR_HIGHLIGHT : MENU_COLOR_INACTIVE; + + // power button press + if (btns[POWER].press) { + ui_items[highlighted].fill = MENU_COLOR_CHOSEN; + redraw = true; + if (led_g) + led_set_state(led_g, LEDST_ON); + } else if (btns[POWER].release) { + selected = highlighted; + redraw = true; + } + + // handle autoselect timeout + if (autoselect >= 0 && timeout > 0 && timeout == cycles / 2) { + selected = autoselect; + ui_items[selected].fill = MENU_COLOR_CHOSEN; + redraw = true; + } + + cycles++; + + // UI drawing if (redraw) { ui_draw(ui_items, n_items, &p); video_sync(vdev, true); @@ -586,7 +668,11 @@ next: break; } - /* find first matching tap down */ + // cancel timeout on touch + if (ret > 0) + timeout = 0; + + // find first matching tap down for (int idx = 0; idx < ret; idx++) { int tx = touches[idx].x; int ty = touches[idx].y; @@ -594,12 +680,13 @@ next: struct ui_item* hit = ui_hit_find(ui_items, n_items, tx, ty); if (hit) { selected = hit->id; - hit->fill = 0xffb19019; + hit->fill = MENU_COLOR_CHOSEN; redraw = 1; goto next; } } + // final selection processing if (selected != -1) { struct tmenu_boot_item *it = &items[selected]; @@ -639,7 +726,7 @@ next: } } -out_stop_touch: +//out_stop_touch: ret = touchpanel_stop(tdev); if (ret < 0) printf("Failed to stop %s, err=%d\n", tdev->name, ret);