Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/lock_screen_ui.c
T
AskaEth c3abc3fee4 feat(launcher,lock): 锁屏补充三定案—面板一次性解锁恢复口 / 设置页 PIN 管理 / 电源菜单主动锁强化
- 面板一次性解锁恢复口:/tmp/meow_pin_unlock_once 存在即被锁屏页消费删除并放行一次
  (不校验 PIN、不清 system.pin、不重置失败计数;重启标志自然消失;lck_unlock_once_consume,
  launcher 侧只做检查/消费,panel 端点由主会话另实现)。
- 系统设置「安全 → 锁屏 PIN」页(lock_settings.c,subpage.c 脚手架):状态(未设置/已设置/
  锁定中+剩余)/ 设置(4 位双输确认)/ 修改(需旧 PIN 一次)/ 清除(**免旧 PIN**,仅二次确认弹窗)
  / system.pin_lock_on_wake 开关;数字输入用 SDK 按钮键盘(lck_input_pin_modal)。
- 电源菜单「锁定」主动锁强化:立即熄屏 + 写 /tmp/meow_pin_locked(主动锁标志,重启消失,
  唤醒时无论 pin_lock_on_wake 是否开都必锁屏)+ rm /tmp/meow_pin_unlock_once 作废挂起绕过;
  lck_wake_should_lock() 作为唤醒门取代 lck_lock_required(1)(主菜单 2 处 + App 会话 2 处)。
- 测试:test_lock_screen 扩 51 ok——bypass 存在即消费/不存在不影响/消费后删除;清除免旧 PIN;
  主动锁标志唤醒必锁/清除后回退;全回归 settings_brightness/notif_panel/notif_tf/vol_ramp_jack 无碍;
  交叉 -Wall -Wextra 0 warning。meow_launcherd md5 bdf884756ef8a8353aa8e5dfb6e10a0a。
- SYSTEM_SETTINGS.md 同步锁屏行为段。
2026-09-02 00:57:17 +08:00

397 lines
18 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.
/* 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;
/* 面板一次性解锁恢复口:进入时消费(存在即删除+放行一次,不校验 PIN/不清 PIN/不重置失败计数) */
if (lck_unlock_once_consume()) {
lck_manual_lock_clear(); /* 进入系统即视为解除主动锁 */
logmsg("lock: unlock-once bypass consumed at entry -> release");
return 1;
}
lk_draw(entered, status, lock_until); /* 首帧 */
int unlocked = 0;
for (;;) {
time_t now = time(NULL);
int redraw = 0;
/* 面板一次性解锁恢复口:锁屏页停留期(每次重试前/每轮)也消费 */
if (lck_unlock_once_consume()) {
lck_manual_lock_clear();
logmsg("lock: unlock-once bypass consumed in-loop -> release");
unlocked = 1;
goto done;
}
/* 锁定到期自动解禁(清计数+戳),仅重画状态行 */
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();
lck_manual_lock_clear(); /* 主动锁标志消解 */
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;
}
/* 电源菜单「锁定」:**主动锁屏**(用户定案 2026-09-02 强化)。
* 语义 = 立即熄屏 + 进入锁定 + 作废挂起的面板绕过标志:
* ① rm /tmp/meow_pin_unlock_once(主动锁屏作废一次性绕过);
* ② 写 /tmp/meow_pin_locked(主动锁标志 → 唤醒时无论 system.pin_lock_on_wake 是否开都必锁屏);
* ③ meow_panel_off() 立即熄屏(唤醒时经主屏唤醒路径弹锁屏页)。
* 不立即弹锁屏页(已熄屏);未配凭据 → no-op。返回 0(关菜单,run_menu 继续)。 */
int meow_lock_screen_engage(void)
{
if (!lck_pin_configured()) {
logmsg("lock: no system.pin/panel.pin configured -> skip manual lock (no-op)");
return 0;
}
if (access(LCK_UNLOCK_ONCE_FILE, F_OK) == 0) {
unlink(LCK_UNLOCK_ONCE_FILE);
logmsg("lock: manual active-lock revoked pending unlock-once bypass");
}
lck_manual_lock_set();
meow_panel_off(); /* 立即熄屏;唤醒时必走锁屏 */
logmsg("lock: manual active-lock -> screen off + lock on wake");
return 0;
}
/* ---- 设置页「锁屏 PIN」数字输入模态(system 级 PIN 设置,用 SDK 按钮画数字键盘)----
* 全屏遮罩:title + 4 圆点(●)+ 数字键盘(1-9 / C / 0 / 退格)。输入满 4 位即返回 1 并拷贝到 out
* (不校验——调用方做双输确认/旧 PIN 校验);电源键=取消返回 0;音量键不动。 */
int lck_input_pin_modal(const char *title, char *out, int outcap)
{
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};
int result = 0;
int dirty = 1; /* 首帧必画 */
for (;;) {
/* 只在首帧/输入变化时整页重绘(消频闪:锁屏低频交互),后续轮次不动 LCDC */
if (dirty) {
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, title, 24, th->on_surface);
lk_draw_pin(entered, th);
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 *t2 = "C"; if (mfont_ready) t2 = "清空";
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t2, 22, th->on_surface);
} else {
const char *t2 = "DEL"; if (mfont_ready) t2 = "退格";
lk_text(x + w / 2, y + LK_ROW_H / 2 - 9, t2, 22, th->on_surface);
}
}
}
meow_dirty_full();
fb_pan_buf0_force();
meow_ui_flush();
dirty = 0;
}
if (kp_power.ok) {
kp_poll(&kp_power, NULL, NULL);
if (kp_is_down(&kp_power, KEY_POWER)) { result = 0; break; } /* 取消 */
}
kp_poll(&kp_keys, NULL, NULL);
(void)kp_is_down(&kp_keys, KEY_VOLUMEDOWN);
(void)kp_is_down(&kp_keys, KEY_VOLUMEUP);
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 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);
if (!mt_in(&mt, x, y, w, h)) continue;
int key = lk_keys[r][c];
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; } }
dirty = 1;
if (entered == LCK_PIN_LEN) {
if (out && outcap > LCK_PIN_LEN) { snprintf(out, (size_t)outcap, "%s", digits); result = 1; }
else result = 1;
goto done2;
}
break;
}
}
}
}
}
}
}
}
usleep(POLL_MS * 1000);
}
done2:
if (tfd >= 0) close(tfd);
kp_close(&kp_power);
kp_close(&kp_keys);
return result;
}