Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/power_menu.c
T
MeowOS e6f0b665e3 refactor(launcher): 拆分 meow_launcherd.c(9778行)→meow_launcherd/ 多文件(agent-149)
- 14 模块 .c + meow_launcher_internal.h(642): util/font_theme/fb_compositor/settings_page/power_menu/notif_center/app_launcher/main_menu/audio/backlight/net/keys/about/statusbar; meow_launcherd.c 已删
- 按行逐字搬移(0-diff), 118函数提升非static+47组extern(内部头), #if0退役区不编入(源码留.pre_split.bak)
- 关于本机小改: 作者 AskaEth/17 + info/item[13]防越界 + s_dd_modal 超高滚动(clamp/触摸滚动/音量键跟随/裁剪, 仅超高启用)
- build_launcher.sh 编译列表→模块; 文档/注释引用 meow_launcherd.c→模块/目录
- 编译0warning/-Wall -Wextra告警集与原文一致(77条无新增)/qemu-mipsel冒烟一致(--scan/--sysdump); launcher=c0799357(505KB; md5因多文件重编译与原文异,非回归依据)
- 备份 meow_launcherd.c.pre_split.bak/meow_launcherd.pre_split.bak; M2.69(0.7.64)已推卡并用户验证OK
2026-08-30 21:38:30 +08:00

407 lines
23 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/* power_menu.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/
/* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/
#include "meow_launcher_internal.h"
static const int pm_cy[PM_NUM] = { 250, 250, 390, 390, 530 }; /* 圆心 y(行距 140) */
static const char *pm_labels_cn[PM_NUM] = { "关机", "重启", "回HiBy", "升级", "取消" };
static const char *pm_labels_en[PM_NUM] = { "POWER OFF", "RESTART", "HIBYOS", "UPGRADE", "CANCEL" };
/* 电源菜单圆钮图标:从 pm_icons.h 的 56×56 alpha 位图 blit 到 fb(以圆钮中心 cx,cy 为锚居中)。
* alpha 按 col(RGB565)映射:图标前景色随选中/未选中主题色变化(日夜间由 pm_theme_* 提供)。
* dst_px 为 fb16bpp 写入:新像素 = col 前景与背景(btn bg)按 alpha 混合。 */
static void blit_pm_icon(int idx, int cx, int cy, uint16_t col)
{
const unsigned char *src = pm_icons[idx].data;
int S = PM_ICON_SIZE;
int x0 = cx - S / 2, y0 = cy - S / 2;
for (int y = 0; y < S; y++) {
int yy = y0 + y;
if (yy < 0 || yy >= fb_yres) continue;
uint16_t *row = (uint16_t *)fb + yy * (fb_line / 2);
for (int x = 0; x < S; x++) {
int a = src[y * S + x];
if (a < 8) continue; /* 透明像素跳过 */
int xx = x0 + x;
if (xx < 0 || xx >= fb_xres) continue;
/* 前景 col 已含所需亮度;此处仅做软边 alpha 混合到背景,避免锯齿 */
uint16_t bg = row[xx];
if (a >= 240) { row[xx] = col; continue; }
int a4 = a >> 4; /* 0..15 */
int r = ((col >> 11) * a4 + ((bg >> 11) & 0x1F) * (16 - a4)) >> 4;
int g = (((col >> 5) & 0x3F) * a4 + ((bg >> 5) & 0x3F) * (16 - a4)) >> 4;
int b = ((col & 0x1F) * a4 + (bg & 0x1F) * (16 - a4)) >> 4;
row[xx] = (uint16_t)((r << 11) | (g << 5) | b);
}
}
}
/* 绘制电源菜单圆钮 i(MD3 圆形:外描边+内填充+圆内 Material 图标;选中高亮)。
* 圆内放 Material Symbols 位图,文字标签放圆下(Android 12 式:icon 上、label 下)。 */
static void draw_pm_button(int i, int selected)
{
int cx = pm_cx[i], cy = pm_cy[i];
/* 主题(日夜间由 pm_theme_* 决定;默认取 dark,后续接设置) */
const pm_theme_t *th = &pm_theme_dark;
/* 先铺一块比任何选中圈都大的卡片底色圆,盖掉上一态残留的选中描边(问题3:
* 焦点移走后 PM_R+2..PM_R+6 之间残留亮圈。以 0x1C2438 卡片色清底)。 */
fill_circle(cx, cy, PM_R + 10, 0x1C2438);
/* MD3 无边框高亮:选中=未选中底色稍亮填充(无描边圈),仅底色区分焦点;避免圆形描边残留。 */
if (selected)
fill_circle(cx, cy, PM_R, th->btn_sel_bg);
else
fill_circle(cx, cy, PM_R, th->btn_nor_bg);
/* 圆内 Material 图标(alpha 位图 + 主题前景色) */
blit_pm_icon(i, cx, cy, selected ? th->icon_sel : th->icon_nor);
uint32_t col = selected ? 0xFFFFFF : 0xC8D0E8;
/* 圆下文字标签(真字体/中文 或 点阵 ASCII 回退) */
int ly = cy + PM_R + 8;
if (mfont_ready) {
const char *lab = pm_labels_cn[i];
int lw = meow_font_width_text(lab, 18);
meow_font_drawtext((uint16_t *)fb, cx - lw / 2, ly, lab, rgb565(col), 18);
} else {
const char *lab = pm_labels_en[i];
int lw = (int)strlen(lab) * 5;
draw_text(cx - lw / 2, ly + 1, lab, 1, col);
}
}
void sys_do_reboot(int cmd)
{
/* M2a:正常电源事件(关机/重启)触发清除板载通知数据之前,先转储所有未上卡通知
* (NOTIFICATION_SYSTEM.md §8)。meow_notif_before_poweroff 幂等安全(未 init 直接返回)。 */
meow_notif_before_poweroff();
sync(); /* 先落盘,避免数据丢失 */
logmsg("sys_do_reboot cmd=0x%x", cmd);
syscall(SYS_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, cmd, NULL);
logmsg("sys_do_reboot returned: %s", strerror(errno)); /* 正常不会返回 */
for (;;) usleep(1000); /* 防御:永不返回 */
}
/* 回HibyOS:写板载 /data/meow_boot_default=1(开机默认权威标记,O_TRUNC 单值、不写 TF 卡),
* 再 reboot 回选单并默认 HibyOS。meow.conf 不再写/承载 boot_default(bootmenu 以板载标记为准)。 */
static void set_boot_default_hiby(void)
{
int fd = open("/data/meow_boot_default", O_WRONLY | O_CREAT | O_TRUNC, 0644);
if (fd >= 0) { write(fd, "1\n", 2); close(fd); logmsg("pm: /data/meow_boot_default=1"); }
}
/* 电源菜单(M2.6):2×2 网格,dirty 绘制消频闪;输入修复:
* - 触摸按下捕获坐标、抬起命中按钮即确认(同 run_menu MT 防抖);
* - 音量上/下导航;播放=确认;电源新按(松开长按后)=确认当前钮(非取消);
* - 取消=选「取消」钮;8s 持续按住=强制关机(绕过)。
* 返回 1=采取动作,0=取消。 */
int show_power_menu(void)
{
int fbfd = open(FB_DEV, O_RDWR);
if (fbfd >= 0) { ioctl(fbfd, FBIOBLANK, 0); close(fbfd); } /* 亮起面板 */
pm_capture_app_frame(); /* A-0:菜单背景 = app 当前帧(fb0 buffer0),再压暗绘制其上 */
dim_screen();
/* M2.8 Bug 3-residual:若电源菜单是在「息屏」态打开(按住 3s),此处 FBIOBLANK(0)
* 刚上电,LCDC 扫描地址可能非 buffer0;强制 re-pan 让压暗的应用列表 + 菜单落在被显示的
* 缓冲,否则菜单画在非显示缓冲上不可见。 */
fb_pan_buf0_force();
meow_screen_on = 1; /* 菜单打开即视为亮屏 */
/* M2.11 B:默认光标 = 「取消」(安全项,index 4)。用户打开菜单后未导航就按电源/播放,
* 只会取消关菜单,绝不因「按电源键」本身关机/重启。要真关机必须先导航到「关机」再确认。 */
int sel = PM_NUM - 1; /* 默认取消(安全) */
/* M2.30 MD3:居中圆角卡片背景 + 标题居中 + 圆钮网格。卡片白色柔和描边/深色底。 */
fill_round_rect(PM_CARD_X0, PM_CARD_Y0, PM_CARD_W, PM_CARD_H, PM_CARD_R, 0x141824);
fill_round_rect(PM_CARD_X0 + 2, PM_CARD_Y0 + 2, PM_CARD_W - 4, PM_CARD_H - 4, PM_CARD_R - 2, 0x1C2438);
/* 卡片顶部标题,水平居中 */
char title[32];
snprintf(title, sizeof title, "POWER MENU");
int title_y = PM_CARD_Y0 + 26; /* 标题在卡片内顶部(卡片顶 115,标题基线 ~141) */
if (mfont_ready) meow_font_drawtext((uint16_t *)fb, (fb_xres - meow_font_width_text(title, 20)) / 2, title_y, title, rgb565(0xFFFFFF), 20);
else draw_text((fb_xres - (int)strlen(title) * 6) / 2, title_y, title, 2, 0xFFFFFF);
/* 底部提示行(卡片下方,非圆钮所需) */
int hint_y = PM_CARD_Y0 + PM_CARD_H + 10;
if (mfont_ready) meow_font_drawtext((uint16_t *)fb, (fb_xres - meow_font_width_text("音量导航 电源确认", 18)) / 2, hint_y, "音量导航 电源确认", rgb565(0x777799), 18);
else draw_text((fb_xres - (int)strlen("VOLUME NAV POWER OK") * 5) / 2, hint_y + 1, "VOLUME NAV POWER OK", 1, 0x777799);
meow_dirty_full(); /* M2.16:把压暗列表+卡片+标题+(下方圆钮)作为一整帧呈现 */
/* 触摸状态(按下→抬起):统一 meow_touch 接口(goodix 时序免疫,原点由首个有效 ABS 锁) */
meow_touch mt;
mt_reset(&mt);
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
struct keypoll kp_power, kp_keys;
kp_open(&kp_power, DEV_POWER);
kp_open(&kp_keys, DEV_KEYS);
int prev_vold = 0, prev_volu = 0, prev_play = 0, prev_pwr = 0;
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
prev_pwr = kp_is_down(&kp_power, KEY_POWER) ? 1 : 0;
prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN) ? 1 : 0;
prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP) ? 1 : 0;
prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE) ? 1 : 0;
int act = -1;
unsigned long long pm_t0 = now_ms();
unsigned long long pm_last_interact = pm_t0; /* M2.33:菜单空闲自动关闭基准(任何互动刷新) */
int pm_init = 0, last_sel = -1;
/* M2.10 Bug:电源菜单 8s 强关(按住电源≥8s=绕过强关)会覆盖用户的菜单操作 —— 用户按住
* 电源打开菜单后(尤其仍按着电源)点选「回到HibyOS/重启」等按钮,8s 一到先触发强关 → 关机
* 而非执行所选动作。修法:一旦用户与菜单互动(音量导航/触摸/播放/电源确认),置 pm_interacted=1
* 取消 8s 强关,让所选动作(如「回到HibyOS」→ reboot RESTART)生效。仅当全程未互动才保留「按住
* 8s=强关」。*/
int pm_interacted = 0;
for (;;) {
/* dirty 绘制:首次全画;此后仅选中变化才重画受影响两钮(消频闪)→ 标脏 + present。 */
if (!pm_init) {
for (int i = 0; i < PM_NUM; i++) draw_pm_button(i, i == sel);
pm_init = 1; last_sel = sel;
meow_ui_flush(); /* A-1/A-2:SDK present(vsync+帧率门控+blit 脏=全帧,含压暗列表+标题+按钮) */
} else if (sel != last_sel) {
draw_pm_button(last_sel, 0);
draw_pm_button(sel, 1);
/* M2.30 圆形:dirty 区 = 圆(圆心±半径)+ 圆下文字(半径+偏移+行高),四周留 10px */
int dr = (PM_R + 6) + 10, dtxt = PM_R + 26 + 10;
meow_dirty_add(pm_cx[last_sel] - dr, pm_cy[last_sel] - dr, dr * 2, dr + dtxt);
meow_dirty_add(pm_cx[sel] - dr, pm_cy[sel] - dr, dr * 2, dr + dtxt);
last_sel = sel;
meow_ui_flush(); /* A-1/A-2:SDK present,blit 两钮区 */
}
if (sim_power_pick >= 0 && now_ms() - pm_t0 >= 2000) act = sim_power_pick;
int vold = 0, volu = 0, play = 0, pwr = 0;
if (kp_keys.ok) { kp_poll(&kp_keys, NULL, NULL);
vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
play = kp_is_down(&kp_keys, KEY_PLAYPAUSE); }
if (kp_power.ok) { kp_poll(&kp_power, NULL, NULL); pwr = kp_is_down(&kp_power, KEY_POWER); }
if (vold && !prev_vold) { sel = (sel + 1) % PM_NUM; pm_interacted = 1; pm_last_interact = now_ms(); }
if (volu && !prev_volu) { sel = (sel - 1 + PM_NUM) % PM_NUM; pm_interacted = 1; pm_last_interact = now_ms(); }
/* M2.19:播放键不再作确认(让位给全局媒体控制)。电源菜单的物理确认
* 已由电源键负责(下行),播放键删除,重复确认移除。 */
(void)play;
if (pwr && !prev_pwr) { act = sel; pm_interacted = 1; pm_last_interact = now_ms(); } /* 电源新按=确认(非取消) */
prev_vold = vold; prev_volu = volu; prev_play = play; prev_pwr = pwr;
/* 触摸:按下(meow_touch 原点锁定,goodix 时序免疫),抬起按按钮命中确认 */
if (tfd >= 0) {
struct pollfd pfd = { tfd, POLLIN, 0 };
if (poll(&pfd, 1, 0) > 0) {
unsigned char ev[16];
while (read(tfd, ev, sizeof ev) == (ssize_t)sizeof ev) {
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
int32_t val = *(int32_t *)(ev + 12);
if (type == EV_ABS) {
if (code == ABS_X || code == ABS_MT_POSITION_X) mt_abs(&mt, val, mt.last_y);
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) mt_abs(&mt, mt.last_x, val);
} else if (type == EV_KEY && code == BTN_TOUCH) {
if (val && !mt.down) {
mt_down(&mt);
pm_interacted = 1;
pm_last_interact = now_ms();
} else if (!val && mt.down) {
pm_interacted = 1;
pm_last_interact = now_ms();
int dx, dy;
/* M2.25:抬手位移 >= MEOW_TOUCH_SWIPE_THRESH 判滑动(返回0),
* 不做确认;否则有效 tap,按下原点命中的按钮即确认。 */
if (mt_up(&mt, &dx, &dy)) {
/* M2.30 圆形命中:按下原点距圆心 <= PM_R+6 即命中 */
for (int i = 0; i < PM_NUM; i++) {
int ddx = mt.down_x - pm_cx[i], ddy = mt.down_y - pm_cy[i];
int rr = (PM_R + 6) * (PM_R + 6);
if (ddx * ddx + ddy * ddy <= rr) { sel = i; act = i; break; }
}
}
}
}
}
}
}
/* 8s 强关(绕过):仅当电源持续按住、且用户全程未与菜单互动才触发 —— 放在触摸/按键处理
* 之后,避免「用户已点选按钮」的同一轮被强关抢先(M2.10 Bug)。 */
if (pwr && !pm_interacted && now_ms() - pm_t0 >= 5000) {
if (sim_no_exec) { logmsg("pm: [sim-no-exec] POWER HOLD 8s -> would force poweroff"); }
else { logmsg("pm: POWER HOLD 8s -> force poweroff"); sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF); }
return 1;
}
/* M2.33 菜单空闲超时自动关闭:菜单受 MeowOS screen_off_timeout 管理(系统界面不豁免)。
* 超过超时无任何互动 → 关闭菜单(视为取消)并自动息屏。目的是:息屏后单击电源亮屏时,
* 电源按下信号不会命中菜单按钮(菜单已关),杜绝误触关机/执行菜单动作。 */
if (screen_off_timeout > 0 &&
(unsigned long long)(now_ms() - pm_last_interact) >= (unsigned long long)screen_off_timeout * 1000) {
logmsg("pm: idle %ds -> close menu + auto screen-off (act=cancel)", screen_off_timeout);
meow_panel_off();
meow_auto_slept = 1;
act = 4; /* 取消(关菜单) */
break;
}
if (act >= 0) break;
usleep(POLL_MS * 1000);
}
if (tfd >= 0) close(tfd);
/* M2.11 B:电源键在菜单 = 按当前项确认。act 与 syscall 类型逐项核对:
* act==4(取消) -> 只关菜单,绝不关机(return 0,永不到 reboot);
* act==0(关机) -> 才 poweroff;act==1(重启)/act==2(回HibyOS)/act==3(升级模式) -> 才 reboot。
* 只有 act 为 0/1/2/3 才可能执行 sys_do_reboot;power 键本身绝不触发关机。 */
logmsg("pm: act=%d -> %s", act,
act == 0 ? "关机" : act == 1 ? "重启" : act == 2 ? "回HibyOS" : act == 3 ? "升级模式" : "取消");
if (act == 4) { logmsg("pm: cancel (close menu only)"); return 0; }
if (act == 0) {
logmsg("pm: POWER OFF");
if (!sim_no_exec) {
/* 先显关机 logo(cmd_jpeg_display 是独立二进制,能正常显图),再直连 poweroff */
system("PATH=/bin:/sbin:/usr/bin:/usr/sbin cmd_jpeg_display /etc/shutdown_logo.jpeg >/dev/null 2>&1");
sys_do_reboot(LINUX_REBOOT_CMD_POWER_OFF);
}
} else if (act == 1) {
logmsg("pm: REBOOT");
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
} else if (act == 2) {
logmsg("pm: RETURN TO HIBYOS");
set_boot_default_hiby();
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
} else if (act == 3) {
/* M2.29 升级模式:写 mtd5=ota:kernel2(bootmode.sh Recovery = flash_erase + nandwrite)
* 再 reboot —— uboot 读 mtd5=ota:kernel2 引导 kernel2(升级模式,自动刷 TF 卡
* r3proii.upt,双进度条,跑完重启进新固件)。已验证此链路可靠。 */
logmsg("pm: ENTER UPGRADE");
if (!sim_no_exec) {
system("PATH=/bin:/sbin:/usr/bin:/usr/sbin /usr/bin/bootmode.sh Recovery >/dev/null 2>&1");
sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
}
}
return 1;
}
/* M2.6 G「Reboot to player」:MeowOS 常驻模式点「返回播放器」时,播放器已死,弹确认框
* 「返回播放器需重启,现在重启?[是/否]」。是=写选单默认 HibyOS + reboot;否=留在 MeowOS。
* VOL=是/否 切换;PLAY=确认;POWER=取消;触摸点 是/否 确认。风格同电源菜单。
* 返回 1=是(已 reboot,通常不回),0=否/取消(调用方重绘应用列表)。 */
/* 「返回播放器」确认框「是/否」按钮(左右),选中高亮。
* M2.9 Bug 4:改为 dirty 绘制 —— 仅 sel 变化时重画两钮(旧钮取消高亮 + 新钮高亮),
* 不随主循环每 10ms 全刷(原实现每帧重画同一内容,与 LCD 刷新竞争产生频闪)。 */
static void draw_cr_button(int sel, int i)
{
int x0 = (i == 0) ? 80 : 280, y0 = 420, w = 120, h = 60;
fill_round_rect(x0, y0, w, h, 12, (i == sel) ? 0x2A3A6A : 0x1A1A2A);
const char *lbl = (i == 0) ? "是" : "否";
if (mfont_ready) {
int lw = meow_font_width_text(lbl, 20);
meow_font_drawtext((uint16_t *)fb, x0 + w / 2 - lw / 2, y0 + 30 - meow_font_line_height(20) / 2,
lbl, rgb565((i == sel) ? 0xFFFFFF : 0xC8D0E8), 20);
} else {
draw_text(x0 + w / 2 - 6, y0 + 24, (i == 0) ? "YES" : "NO", 2, (i == sel) ? 0xFFFFFF : 0xC8D0E8);
}
}
int confirm_reboot_player(void)
{
dim_screen();
/* M2.8 Bug 3-residual:确认框同电源菜单 —— 若打开前刚经历息屏/blank,LCDC 扫描地址可能
* 非 buffer0;强制 re-pan 让「压暗列表 + 是/否」落在被显示的缓冲。 */
fb_pan_buf0_force();
if (mfont_ready) meow_font_drawtext((uint16_t *)fb, 20, 320, "返回播放器需重启,现在重启?", rgb565(0xFFFFFF), 20);
else draw_text(20, 322, "REBOOT TO RETURN TO PLAYER?", 2, 0xFFFFFF);
meow_touch mt;
mt_reset(&mt);
int tfd = open(DEV_TOUCH, O_RDONLY | O_NONBLOCK);
struct keypoll kp_power, kp_keys;
kp_open(&kp_power, DEV_POWER);
kp_open(&kp_keys, DEV_KEYS);
int prev_vold = 0, prev_volu = 0, prev_play = 0, prev_pwr = 0;
kp_poll(&kp_power, NULL, NULL); kp_poll(&kp_keys, NULL, NULL);
prev_pwr = kp_is_down(&kp_power, KEY_POWER) ? 1 : 0;
prev_vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN) ? 1 : 0;
prev_volu = kp_is_down(&kp_keys, KEY_VOLUMEUP) ? 1 : 0;
prev_play = kp_is_down(&kp_keys, KEY_PLAYPAUSE) ? 1 : 0;
int sel = 1; /* 默认「否」:不误重启 */
int act = -1;
int cr_init = 0, cr_last_sel = -1; /* M2.9 Bug 4:dirty——仅 sel 变化才重画两钮 */
/* 标题/按键提示:打开时画一次(原实现把它们放主循环每 10ms 全刷,与 LCD 刷新竞争 →
* 「是/否」钮频闪;改为只在打开/状态变化时绘制)。标题在前面已画一次,这里画提示行。 */
if (mfont_ready) meow_font_drawtext((uint16_t *)fb, 20, 560, "音量切换 电源取消", rgb565(0x666688), 20);
else draw_text(20, 562, "VOL NAV POWER EXIT", 1, 0x666688);
meow_dirty_full(); /* M2.16:压暗列表+标题+提示作为一整帧呈现 */
for (;;) {
/* dirty 绘制:首次全画两钮;此后仅 sel 变化才重画(消频闪)→ 标脏 + present。 */
if (!cr_init) {
for (int i = 0; i < 2; i++) draw_cr_button(sel, i);
cr_init = 1; cr_last_sel = sel;
meow_ui_flush();
} else if (sel != cr_last_sel) {
draw_cr_button(sel, cr_last_sel); /* 旧选中:取消高亮 */
draw_cr_button(sel, sel); /* 新选中:高亮 */
meow_dirty_add((cr_last_sel == 0 ? 80 : 280) - 10, 412, 140, 76);
meow_dirty_add((sel == 0 ? 80 : 280) - 10, 412, 140, 76);
cr_last_sel = sel;
meow_ui_flush();
}
int vold = 0, volu = 0, play = 0, pwr = 0;
if (kp_keys.ok) { kp_poll(&kp_keys, NULL, NULL);
vold = kp_is_down(&kp_keys, KEY_VOLUMEDOWN); volu = kp_is_down(&kp_keys, KEY_VOLUMEUP);
play = kp_is_down(&kp_keys, KEY_PLAYPAUSE); }
if (kp_power.ok) { kp_poll(&kp_power, NULL, NULL); pwr = kp_is_down(&kp_power, KEY_POWER); }
if (vold && !prev_vold) sel = (sel + 1) % 2;
if (volu && !prev_volu) sel = (sel - 1 + 2) % 2;
/* M2.19:播放键不再作「是/确认」(让位给全局媒体控制)。确认框的确认
* 走触摸点选(下方 tfd 命中),电源键=取消(下行);播放键删除。 */
(void)play;
if (pwr && !prev_pwr) act = 3; /* 电源=取消 */
prev_vold = vold; prev_volu = volu; prev_play = play; prev_pwr = pwr;
if (tfd >= 0) {
struct pollfd pfd = { tfd, POLLIN, 0 };
if (poll(&pfd, 1, 0) > 0) {
unsigned char ev[16];
while (read(tfd, ev, sizeof ev) == (ssize_t)sizeof ev) {
uint16_t type = *(uint16_t *)(ev + 8), code = *(uint16_t *)(ev + 10);
int32_t val = *(int32_t *)(ev + 12);
if (type == EV_ABS) {
if (code == ABS_X || code == ABS_MT_POSITION_X) mt_abs(&mt, val, mt.last_y);
else if (code == ABS_Y || code == ABS_MT_POSITION_Y) mt_abs(&mt, mt.last_x, val);
} else if (type == EV_KEY && code == BTN_TOUCH) {
if (val && !mt.down) {
mt_down(&mt);
} else if (!val && mt.down) {
int dx, dy;
if (mt_up(&mt, &dx, &dy)) {
for (int i = 0; i < 2; i++) {
int x0 = (i == 0) ? 80 : 280;
/* 命中「是/否」钮:x∈[x0-8, x0+128], y∈[412, 488](含端点) */
if (mt_in(&mt, x0 - 8, 412, 137, 77))
{ sel = i; act = i; break; }
}
}
}
}
}
}
}
if (act >= 0) break;
usleep(POLL_MS * 1000);
}
if (tfd >= 0) close(tfd);
if (act == 0) {
logmsg("reboot-to-player: YES -> default HibyOS + reboot");
set_boot_default_hiby();
if (!sim_no_exec) sys_do_reboot(LINUX_REBOOT_CMD_RESTART);
return 1;
}
logmsg("reboot-to-player: cancel");
return 0;
}