- system.pin:4 位数字 PIN,sha256+salt 存 settings.conf(格式与面板 panel.pin 全等,绝无明文)。 panel.pin 已设且 system.pin 未设时锁屏接受 panel.pin,首次验证成功后并入 system.pin。 - 锁屏时机:息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁; 开机不锁(boot 进主屏不拦)。 - 锁屏页:launcher 渲染系统级全屏页,App 不可见不可触;SDK 按钮画数字键盘(不调 IME), 输入 4 位自动校验;● 遮罩不回显数字;错 5 次锁 5 分钟(持久化锁定戳/计数,重启不清)。 - 安全细节:锁屏页电源键=息屏(允许再睡),音量键不动,不响应左缘返回/通知中心手势。 兜底:SSH root 删 system.pin 键即解锁(无其它后门)。HibyOS 不涉及。 - 宿主测试(test_lock_screen):设置/校验/错5锁定持久化(重载模拟重启)/唤醒触发/开机不锁 /panel.pin 迁移,全 PASS;既有 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 回归 无碍;交叉 -Wall -Wextra 0 warning。meow_launcherd md5 670f978e71c1e81cfe86d47c21248c9d。
259 lines
11 KiB
C
259 lines
11 KiB
C
/* lock_screen_ui.c — MeowOS 锁屏页(系统级全屏页面:launcher 渲染,App 不可见不可触)。
|
||
*
|
||
* 用户定案(2026-09-02):
|
||
* - 息屏唤醒后(默认开,system.pin_lock_on_wake=1 可关)+ 电源菜单「锁定」手动锁;开机不锁。
|
||
* - 数字键盘直接用 SDK 按钮画(不调 IME);输入 4 位自动校验;错 5 次锁 5 分钟(持久化)。
|
||
* - 正确 → 进主屏(return 1);锁屏页期间电源键=息屏(允许再睡,return 0),音量键不动;
|
||
* 不响应左缘返回 / 通知中心手势(本函数不实现这些手势,触摸只认键盘 tap)。
|
||
* - 输入不回显数字(● 遮罩)。兜底见 lock_screen.c(SSH root 删 system.pin 即解锁)。
|
||
*
|
||
* 渲染/输入循环与电源菜单同构(meow_touch + kd_* + kp_*),阻塞到解锁或电源息屏。
|
||
* 纯逻辑在 lock_screen.c(可宿主单测);本文件只做 UI 与输入胶水。
|
||
*/
|
||
#include "meow_launcher_internal.h"
|
||
|
||
/* ---- 键盘几何(480×720):3 列×4 行圆角按钮 ---- */
|
||
#define LK_X0 32 /* 首列左缘 */
|
||
#define LK_COLW 130 /* 列宽 */
|
||
#define LK_COLG 8 /* 列间隙 */
|
||
#define LK_Y0 320 /* 首行顶缘 */
|
||
#define LK_ROW_H 82 /* 行高 */
|
||
#define LK_ROWG 10 /* 行间隙 */
|
||
#define LK_ROWS 4
|
||
#define LK_COLS 3
|
||
/* 12 键(行优先):digit=-2 清空 / -1 退格 / >=0 数字 */
|
||
#define LK_ACT_CLR (-2)
|
||
#define LK_ACT_BS (-1)
|
||
static const int lk_keys[LK_ROWS][LK_COLS] = {
|
||
{ 1, 2, 3 },
|
||
{ 4, 5, 6 },
|
||
{ 7, 8, 9 },
|
||
{ LK_ACT_CLR, 0, LK_ACT_BS },
|
||
};
|
||
|
||
/* 键盘按钮 row*3+col 的命中矩形(x,y,w,h) */
|
||
static void lk_key_rect(int row, int col, int *x, int *y, int *w, int *h)
|
||
{
|
||
*x = LK_X0 + col * (LK_COLW + LK_COLG);
|
||
*y = LK_Y0 + row * (LK_ROW_H + LK_ROWG);
|
||
*w = LK_COLW;
|
||
*h = LK_ROW_H;
|
||
}
|
||
|
||
/* 居中文本(真实字体优先;回退 ASCII 点阵) */
|
||
static void lk_text(int cx, int y, const char *s, int size, uint32_t color)
|
||
{
|
||
if (mfont_ready) {
|
||
int w = meow_font_width_text(s, size);
|
||
meow_font_drawtext((uint16_t *)fb, cx - w / 2, y, s, rgb565(color), size);
|
||
} else {
|
||
draw_text(cx - (int)strlen(s) * 3, y + 1, s, 1, color);
|
||
}
|
||
}
|
||
|
||
/* 单个 PIN 圆点:filled=1 实心(已输入),0=空心(未输入)。 */
|
||
static void lk_pin_dot(int cx, int cy, int r, int filled, uint32_t ink, uint32_t bg)
|
||
{
|
||
if (filled) {
|
||
fill_circle(cx, cy, r, ink);
|
||
} else {
|
||
fill_circle(cx, cy, r, ink); /* 外圈(描边) */
|
||
fill_circle(cx, cy, r - 3, bg); /* 内孔(表面底色) */
|
||
}
|
||
}
|
||
|
||
static void lk_draw_pin(int entered, const meow_ui_theme_t *th)
|
||
{
|
||
int cx = fb_xres / 2;
|
||
int y = 210;
|
||
int gap = 48, r = 20;
|
||
int x0 = cx - (3 * gap) / 2; /* 首圆圆心;四圆等距 gap */
|
||
for (int i = 0; i < LCK_PIN_LEN; i++)
|
||
lk_pin_dot(x0 + i * gap, y, r, i < entered, th->primary, th->surface);
|
||
}
|
||
|
||
static void lk_draw(int entered, const char *status, time_t locked_until)
|
||
{
|
||
const meow_ui_theme_t *th = s_ui_theme();
|
||
fill_rect(0, 0, fb_xres, fb_yres, th->surface); /* 全屏表面底色 */
|
||
lk_text(fb_xres / 2, 120, locked_until > 0 ? "已锁定" : "输入 PIN 解锁", 24, th->on_surface);
|
||
lk_draw_pin(entered, th);
|
||
lk_text(fb_xres / 2, 260, status, 18, status[0] ? th->error : th->on_surface_variant);
|
||
for (int r = 0; r < LK_ROWS; r++) {
|
||
for (int c = 0; c < LK_COLS; c++) {
|
||
int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h);
|
||
fill_round_rect(x, y, w, h, 12, th->surface_container);
|
||
int key = lk_keys[r][c];
|
||
if (key >= 0) {
|
||
char d[2] = { (char)('0' + key), 0 };
|
||
if (mfont_ready) {
|
||
int fw = meow_font_width_text(d, 30);
|
||
meow_font_drawtext((uint16_t *)fb, x + w / 2 - fw / 2,
|
||
y + LK_ROW_H / 2 - meow_font_line_height(30) / 2,
|
||
d, rgb565(th->on_surface), 30);
|
||
} else {
|
||
draw_text(x + w / 2 - 7, y + LK_ROW_H / 2 - 14, d, 2, th->on_surface);
|
||
}
|
||
} else if (key == LK_ACT_CLR) {
|
||
const char *s = "C"; const char *t = s;
|
||
if (mfont_ready) t = "清空";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface);
|
||
} else { /* LK_ACT_BS */
|
||
const char *t = "DEL";
|
||
if (mfont_ready) t = "退格";
|
||
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t, 22, th->on_surface);
|
||
}
|
||
}
|
||
}
|
||
meow_dirty_full();
|
||
fb_pan_buf0_force();
|
||
meow_ui_flush();
|
||
}
|
||
|
||
/* 阻塞式锁屏页。返回 1=已解锁,0=锁屏期间电源键息屏。 */
|
||
int run_lock_screen(void)
|
||
{
|
||
int fbfd = open(FB_DEV, O_RDWR);
|
||
if (fbfd >= 0) { ioctl(fbfd, FBIOBLANK, 0); close(fbfd); } /* 亮起面板 */
|
||
meow_screen_on = 1;
|
||
fb_pan_buf0_force();
|
||
|
||
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 entered = 0;
|
||
char digits[LCK_PIN_LEN + 1] = {0};
|
||
char status[64] = "";
|
||
time_t lock_until = 0;
|
||
{
|
||
time_t rem = lck_lock_remaining();
|
||
if (rem > 0) { lock_until = time(NULL) + rem; snprintf(status, sizeof status, "已锁定"); }
|
||
}
|
||
time_t last_countdown = 0;
|
||
|
||
lk_draw(entered, status, lock_until); /* 首帧 */
|
||
|
||
int unlocked = 0;
|
||
for (;;) {
|
||
time_t now = time(NULL);
|
||
int redraw = 0;
|
||
|
||
/* 锁定到期自动解禁(清计数+戳),仅重画状态行 */
|
||
if (lock_until > 0 && now >= lock_until) {
|
||
lck_lock_remaining();
|
||
lock_until = 0;
|
||
status[0] = 0;
|
||
redraw = 1;
|
||
}
|
||
if (lock_until > 0 && now != last_countdown) {
|
||
last_countdown = now;
|
||
time_t rem = lock_until - now;
|
||
if (mfont_ready) snprintf(status, sizeof status, "已锁定,请 %d:%02d 后重试",
|
||
(int)(rem / 60), (int)(rem % 60));
|
||
else snprintf(status, sizeof status, "LOCKED %d:%02d", (int)(rem / 60), (int)(rem % 60));
|
||
redraw = 1;
|
||
}
|
||
|
||
/* 电源键=息屏(允许再睡);音量键本页不动 */
|
||
if (kp_power.ok) {
|
||
kp_poll(&kp_power, NULL, NULL);
|
||
if (kp_is_down(&kp_power, KEY_POWER)) {
|
||
logmsg("lock: power key -> screen off (stay locked)");
|
||
meow_panel_off();
|
||
break; /* return 0 */
|
||
}
|
||
}
|
||
kp_poll(&kp_keys, NULL, NULL);
|
||
(void)kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
|
||
(void)kp_is_down(&kp_keys, KEY_VOLUMEUP);
|
||
|
||
/* 触摸:只认键盘 tap(滑动手势忽略:不左缘返回、不通知中心下拉) */
|
||
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)) {
|
||
int handled = 0;
|
||
for (int r = 0; r < LK_ROWS && !handled; r++) {
|
||
for (int c = 0; c < LK_COLS && !handled; c++) {
|
||
int x, y, w, h; lk_key_rect(r, c, &x, &y, &w, &h);
|
||
if (!mt_in(&mt, x, y, w, h)) continue;
|
||
handled = 1;
|
||
int key = lk_keys[r][c];
|
||
if (lock_until > 0) break; /* 锁定期间键盘不响应 */
|
||
if (key >= 0) {
|
||
if (entered < LCK_PIN_LEN) {
|
||
digits[entered++] = (char)('0' + key);
|
||
digits[entered] = 0;
|
||
}
|
||
} else if (key == LK_ACT_CLR) {
|
||
entered = 0; digits[0] = 0;
|
||
} else if (key == LK_ACT_BS) {
|
||
if (entered > 0) { entered--; digits[entered] = 0; }
|
||
}
|
||
redraw = 1;
|
||
if (entered == LCK_PIN_LEN) {
|
||
if (lck_pin_check(digits)) {
|
||
lck_pin_success();
|
||
logmsg("lock: PIN OK -> unlock");
|
||
unlocked = 1;
|
||
goto done;
|
||
} else {
|
||
int rem = lck_pin_fail();
|
||
entered = 0; digits[0] = 0;
|
||
if (rem > 0) {
|
||
lock_until = time(NULL) + rem;
|
||
last_countdown = 0;
|
||
snprintf(status, sizeof status, "已锁定");
|
||
} else {
|
||
if (mfont_ready) snprintf(status, sizeof status, "PIN 错误");
|
||
else snprintf(status, sizeof status, "WRONG PIN");
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 整页重绘(锁屏低频交互,简单整页即可) */
|
||
if (redraw) lk_draw(entered, status, lock_until);
|
||
usleep(POLL_MS * 1000);
|
||
}
|
||
done:
|
||
if (tfd >= 0) close(tfd);
|
||
kp_close(&kp_power);
|
||
kp_close(&kp_keys);
|
||
logmsg("lock: exit unlocked=%d screen_on=%d", unlocked, meow_screen_on);
|
||
return unlocked;
|
||
}
|
||
|
||
/* 电源菜单「锁定」/ 唤醒共用的进入点。未配凭据 → no-op(无鉴别物,锁了也解不开)。
|
||
* 返回与 run_lock_screen 同(1 已解锁 / 0 电源息屏 / 0 无凭据跳过)。 */
|
||
int meow_lock_screen_engage(void)
|
||
{
|
||
if (!lck_pin_configured()) {
|
||
logmsg("lock: no system.pin/panel.pin configured -> skip lock (no-op)");
|
||
return 0;
|
||
}
|
||
return run_lock_screen();
|
||
}
|