Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/power_menu.c
T

498 lines
26 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);
}
}
/* ---- 电源管理服务 IPC 客户端(SYSTEM_FRAMEWORK_SERVICES §3.2:launcher 经服务触发电源动作)----
* launcher 不再直连 reboot(2)syscall/写 mtd5/写 boot 默认 —— 统一经 meow_svc_power 的
* `/usr/data/meow_power.sock`(`MEOW_IPC_POWER_SOCK` 可覆盖)发 MSG_POWER_ACTION。 */
#define PW_SOCK_DEFAULT "/usr/data/meow_power.sock"
static int pw_write_all(int fd, const uint8_t *b, size_t n)
{
size_t off = 0;
while (off < n) { ssize_t w = write(fd, b + off, n - off); if (w <= 0) return -1; off += (size_t)w; }
return 0;
}
static int pw_read_all(int fd, uint8_t *b, size_t n)
{
size_t off = 0;
while (off < n) { ssize_t r = read(fd, b + off, n - off); if (r <= 0) return -1; off += (size_t)r; }
return 0;
}
/* 发一帧并等应答(读超时 1.5s,服务不可达/无响应不至于卡死菜单)。 */
static int pw_xchg(int fd, uint16_t type, uint32_t req, const void *pl, uint32_t len,
uint8_t *rbuf, uint32_t rcap, uint16_t *rtype)
{
uint8_t hdr[MEOW_IPC_HDR_LEN];
uint32_t magic = MEOW_IPC_MAGIC, rlen = len;
uint16_t ver = MEOW_IPC_VERSION, t = type;
memcpy(hdr + 0, &magic, 4); memcpy(hdr + 4, &ver, 2); memcpy(hdr + 6, &t, 2);
memcpy(hdr + 8, &req, 4); memcpy(hdr + 12, &rlen, 4);
if (pw_write_all(fd, hdr, sizeof hdr) < 0) return -1;
if (len && pl && pw_write_all(fd, pl, len) < 0) return -1;
struct pollfd pf = { fd, POLLIN, 0 };
int pr = poll(&pf, 1, 1500);
if (pr <= 0) return -1;
uint8_t rhdr[MEOW_IPC_HDR_LEN];
if (pw_read_all(fd, rhdr, sizeof rhdr) < 0) return -1;
uint32_t rmagic, rlen2; uint16_t rver, rt;
memcpy(&rmagic, rhdr, 4); memcpy(&rver, rhdr + 4, 2); memcpy(&rt, rhdr + 6, 2);
memcpy(&rlen2, rhdr + 12, 4);
if (rmagic != MEOW_IPC_MAGIC || rver != MEOW_IPC_VERSION) return -1;
if (rlen2 > rcap) rlen2 = rcap;
if (rlen2 && pw_read_all(fd, rbuf, rlen2) < 0) return -1;
if (rtype) *rtype = rt;
return 0;
}
/* 经电源服务执行动作。返回 MEOW_OK 或错误码;res/reslen 可收状态文本。 */
int pm_power_ipc(uint32_t cmd, uint32_t flag, const char *arg, char *res, int reslen)
{
const char *sock = getenv("MEOW_IPC_POWER_SOCK");
if (!sock || !sock[0]) sock = PW_SOCK_DEFAULT;
int fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) return MEOW_ERR_IO;
struct sockaddr_un a;
memset(&a, 0, sizeof a);
a.sun_family = AF_UNIX;
strncpy(a.sun_path, sock, sizeof a.sun_path - 1);
if (connect(fd, (struct sockaddr *)&a, sizeof a) < 0) { close(fd); return MEOW_ERR_IO; }
m_hello_t h;
memset(&h, 0, sizeof h);
snprintf(h.app_id, sizeof h.app_id, "%s", "os.meow.launcher");
h.version = 1;
uint8_t rbuf[256]; uint16_t rtype = 0;
if (pw_xchg(fd, MSG_HELLO, 0x100, &h, sizeof h, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_ACK) {
close(fd); return MEOW_ERR_IO;
}
m_ack_t ak;
memcpy(&ak, rbuf, sizeof ak);
if (ak.code != MEOW_OK) { close(fd); return (int)ak.code; }
m_power_action_t pa;
memset(&pa, 0, sizeof pa);
pa.cmd = cmd;
pa.flag = flag;
if (arg) snprintf(pa.arg, sizeof pa.arg, "%s", arg);
if (pw_xchg(fd, MSG_POWER_ACTION, 0x101, &pa, sizeof pa, rbuf, sizeof rbuf, &rtype) < 0 || rtype != MSG_POWER_STATUS) {
close(fd); return MEOW_ERR_IO;
}
m_power_status_t st;
memcpy(&st, rbuf, sizeof st);
close(fd);
if (res && reslen > 0) {
uint16_t ml = st.msg_len;
if (ml >= (uint16_t)reslen) ml = (uint16_t)(reslen - 1);
memcpy(res, st.msg, ml);
res[ml] = 0;
}
return (int)st.code;
}
/* launcher 侧统一电源动作入口:先转储未上卡通知(进程本地,见 §3.2 定案),再经服务 IPC 执行。
* 返回 MEOW_OK=已受理(服务将执行,通常随即 reboot/poweroff);非 OK=服务不可达/拒绝,调用方应
* 稳妥处理(如菜单按「取消」返回,绝不误报动作成功导致 launcher 退出卡死)。 */
int meow_power_action(uint32_t cmd)
{
meow_notif_before_poweroff(); /* 幂等安全(未 init 直接返回) */
int rc = pm_power_ipc(cmd, 0, NULL, NULL, 0);
if (rc != MEOW_OK)
logmsg("meow_power_action cmd=%u FAILED rc=%d (power service unavailable?)", cmd, rc);
return rc;
}
/* 电源菜单(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_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;
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_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"); return 1; }
logmsg("pm: POWER HOLD 8s -> force poweroff");
if (meow_power_action(POWER_SHUTDOWN) != MEOW_OK) {
logmsg("pm: power service unavailable, abort force-off (menu closed, no action)");
return 0;
}
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 与电源动作逐项核对:
* act==4(取消) -> 只关菜单,绝不关机(return 0,永不到电源动作);
* act==0(关机)/act==1(重启)/act==2(回HibyOS)/act==3(升级模式) -> 才经服务 IPC 执行电源动作。
* 只有 act 为 0/1/2/3 才可能发电源动作;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; }
/* 动作统一经电源管理服务 IPC(meow_svc_power);launcher 不再直连 poweroff/reboot(2)/写 mtd5/
* 写 boot 默认——关机/重启/升级/回HiBy 的动作原语都在服务内实现(§3.2 单一动作链路)。 */
/* 公共服务:执行动作;服务不可达 → 按「取消」返回(launcher 存活,绝不误报已动作卡死)。
* meow_power_action 内部已先 meow_notif_before_poweroff()(转储未上卡通知)。 */
if (act == 0) {
logmsg("pm: POWER OFF");
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would poweroff"); return 1; }
if (meow_power_action(POWER_SHUTDOWN) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 1) {
logmsg("pm: REBOOT");
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would reboot"); return 1; }
if (meow_power_action(POWER_REBOOT) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 2) {
logmsg("pm: RETURN TO HIBYOS");
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would reboot to hiby"); return 1; }
if (meow_power_action(POWER_BACK_HIBY) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
} else if (act == 3) {
logmsg("pm: ENTER UPGRADE");
if (sim_no_exec) { logmsg("pm: [sim-no-exec] would enter upgrade"); return 1; }
if (meow_power_action(POWER_UPGRADE) != MEOW_OK) {
logmsg("pm: power service unavailable, abort (menu closed, no action)");
return 0;
}
}
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_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;
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_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");
if (sim_no_exec) { logmsg("reboot-to-player: [sim-no-exec] would reboot to hiby"); return 1; }
if (meow_power_action(POWER_BACK_HIBY) != MEOW_OK) {
logmsg("reboot-to-player: power service unavailable, abort (stay in MeowOS)");
return 0;
}
return 1;
}
logmsg("reboot-to-player: cancel");
return 0;
}