mirror of
https://xff.cz/git/u-boot/
synced 2025-08-31 16:22:36 +02:00
cmd: tmenu: Add extlinux timeout support and button controls
This commit is contained in:
99
cmd/tmenu.c
99
cmd/tmenu.c
@@ -1,6 +1,6 @@
|
|||||||
// SPDX-License-Identifier: GPL-2.0+
|
// SPDX-License-Identifier: GPL-2.0+
|
||||||
/*
|
/*
|
||||||
* Copyright (C) 2018 Ondrej Jirman <megous@megous.com>
|
* Copyright (C) 2018-2024 Ondrej Jirman <megous@megous.com>
|
||||||
*/
|
*/
|
||||||
#include <common.h>
|
#include <common.h>
|
||||||
#include <bootflow.h>
|
#include <bootflow.h>
|
||||||
@@ -14,6 +14,10 @@
|
|||||||
#include <linux/delay.h>
|
#include <linux/delay.h>
|
||||||
#include <video_font_8x16.h>
|
#include <video_font_8x16.h>
|
||||||
#include <touchpanel.h>
|
#include <touchpanel.h>
|
||||||
|
#include <led.h>
|
||||||
|
#include <button.h>
|
||||||
|
#include <sysreset.h>
|
||||||
|
#include <cli.h>
|
||||||
|
|
||||||
#define VIDEO_FONT_HEIGHT 16
|
#define VIDEO_FONT_HEIGHT 16
|
||||||
#define VIDEO_FONT_WIDTH 8
|
#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)
|
static void painter_line_h(struct painter* p, int dx, u32 color)
|
||||||
{
|
{
|
||||||
if (dx < 0) {
|
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)
|
static void painter_line_v(struct painter* p, int dy, u32 color)
|
||||||
{
|
{
|
||||||
if (dy < 0) {
|
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);
|
painter_move_dxy(p, VIDEO_FONT_WIDTH * 2, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
__maybe_unused
|
||||||
static void painter_char(struct painter* p, char ch, u32 color)
|
static void painter_char(struct painter* p, char ch, u32 color)
|
||||||
{
|
{
|
||||||
int i, row;
|
int i, row;
|
||||||
@@ -368,6 +375,11 @@ struct tmenu_boot_item {
|
|||||||
int action;
|
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[])
|
static int do_tmenu_bootflow(struct cmd_tbl *cmdtp, int flag, int argc, char *const argv[])
|
||||||
{
|
{
|
||||||
struct udevice *vdev, *tdev, *cdev;
|
struct udevice *vdev, *tdev, *cdev;
|
||||||
@@ -390,6 +402,8 @@ start_again:
|
|||||||
struct tmenu_boot_item items[64] = {};
|
struct tmenu_boot_item items[64] = {};
|
||||||
int n_items = 0;
|
int n_items = 0;
|
||||||
int bmp_loaded = 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;
|
struct bootflow *bflow;
|
||||||
for (ret = bootflow_first_glob(&bflow); !ret; ret = bootflow_next_glob(&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)
|
if (cfg->bmp && !bmp_loaded && get_pxe_file(ctx, cfg->bmp, image_load_addr) == 1)
|
||||||
bmp_loaded = 1;
|
bmp_loaded = 1;
|
||||||
|
|
||||||
|
if (timeout < cfg->timeout)
|
||||||
|
timeout = cfg->timeout;
|
||||||
|
|
||||||
struct list_head *pos;
|
struct list_head *pos;
|
||||||
list_for_each(pos, &cfg->labels) {
|
list_for_each(pos, &cfg->labels) {
|
||||||
struct pxe_label *label = list_entry(pos, struct pxe_label, list);
|
struct pxe_label *label = list_entry(pos, struct pxe_label, list);
|
||||||
struct tmenu_boot_item *it = &items[n_items++];
|
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);
|
//printf("label %s - %s %s\n", label->num, label->name, label->menu);
|
||||||
|
|
||||||
it->id = n_items;
|
it->id = n_items;
|
||||||
@@ -550,8 +571,8 @@ start_again:
|
|||||||
i->y = top + row * (gap + item_h);
|
i->y = top + row * (gap + item_h);
|
||||||
i->w = item_w;
|
i->w = item_w;
|
||||||
i->h = item_h;
|
i->h = item_h;
|
||||||
i->fill = 0xff755f10;
|
i->fill = MENU_COLOR_INACTIVE;
|
||||||
i->text_color = 0xffffffff;
|
i->text_color = MENU_COLOR_TEXT;
|
||||||
i->id = idx;
|
i->id = idx;
|
||||||
|
|
||||||
snprintf(i->text, sizeof i->text, "%s", items[idx].label);
|
snprintf(i->text, sizeof i->text, "%s", items[idx].label);
|
||||||
@@ -561,6 +582,7 @@ start_again:
|
|||||||
|
|
||||||
int selected = -1;
|
int selected = -1;
|
||||||
int redraw = 1;
|
int redraw = 1;
|
||||||
|
int highlighted = autoselect >= 0 ? autoselect : 0;
|
||||||
|
|
||||||
ret = touchpanel_start(tdev);
|
ret = touchpanel_start(tdev);
|
||||||
if (ret < 0) {
|
if (ret < 0) {
|
||||||
@@ -568,8 +590,68 @@ start_again:
|
|||||||
goto out_restore_console;
|
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:
|
next:
|
||||||
while (1) {
|
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) {
|
if (redraw) {
|
||||||
ui_draw(ui_items, n_items, &p);
|
ui_draw(ui_items, n_items, &p);
|
||||||
video_sync(vdev, true);
|
video_sync(vdev, true);
|
||||||
@@ -586,7 +668,11 @@ next:
|
|||||||
break;
|
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++) {
|
for (int idx = 0; idx < ret; idx++) {
|
||||||
int tx = touches[idx].x;
|
int tx = touches[idx].x;
|
||||||
int ty = touches[idx].y;
|
int ty = touches[idx].y;
|
||||||
@@ -594,12 +680,13 @@ next:
|
|||||||
struct ui_item* hit = ui_hit_find(ui_items, n_items, tx, ty);
|
struct ui_item* hit = ui_hit_find(ui_items, n_items, tx, ty);
|
||||||
if (hit) {
|
if (hit) {
|
||||||
selected = hit->id;
|
selected = hit->id;
|
||||||
hit->fill = 0xffb19019;
|
hit->fill = MENU_COLOR_CHOSEN;
|
||||||
redraw = 1;
|
redraw = 1;
|
||||||
goto next;
|
goto next;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// final selection processing
|
||||||
if (selected != -1) {
|
if (selected != -1) {
|
||||||
struct tmenu_boot_item *it = &items[selected];
|
struct tmenu_boot_item *it = &items[selected];
|
||||||
|
|
||||||
@@ -639,7 +726,7 @@ next:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
out_stop_touch:
|
//out_stop_touch:
|
||||||
ret = touchpanel_stop(tdev);
|
ret = touchpanel_stop(tdev);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
printf("Failed to stop %s, err=%d\n", tdev->name, ret);
|
printf("Failed to stop %s, err=%d\n", tdev->name, ret);
|
||||||
|
Reference in New Issue
Block a user