Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/lock_screen_ui.c
T
AskaEth a3d6e21db5 fix(launcher,lock): §310 M6.5 追加两修——App 内手动息屏不锁屏 + 锁屏脏区被 App 覆盖
真机 M6.4 反馈(c730333 EVIOCGRAB 之后仍未好全):

P1 App 内电源键手动息屏→亮屏无锁屏(桌面/超时路径都有锁、唯独此路径丢)
  根因:App 会话内电源键单击息屏由 **watchdog** 处理(meow_watchdog.c handle_power →
  wd_panel_off,直接 blank 面板背光 0),**不置** meow_auto_slept;而 launcher app 会话循环的
  唤醒锁屏只在 `if (meow_auto_slept)`(超时路径 set)内触发(app_launcher.c:1996/2029)。
  手动路径唤醒时 meow_auto_slept==0 → 不锁屏(真机实测)。
  修法(app_launcher.c):App 循环每圈以读背光 `==0` 补判 watchdog 手动息屏(app_panel_manual_off),
  唤醒分支改 `meow_auto_slept || app_panel_manual_off`,两类唤醒都走 lck_wake_should_lock 锁屏,
  两条路径对齐。overlay_gate.h 增 overlay_wake_should_lock 判据。

P2 锁屏脏区被 App 覆盖(App 刷新控件盖到锁屏上 / 未刷新区显示锁屏自带脏区)
  根因:媒体保活 App 不冻结仍在绘制;SDK present 抑制(/tmp/meow_notif_active)有 ≤200ms
  标记节流窗(meow_ui_gfx.c MEOW_OVERLAY_CHK_MS)——锁屏首扑后 App 仍可能进 1-2 帧,其
  部分脏区落在锁屏上(App 控件覆盖),未覆盖区才是锁屏。另外锁屏关闭需整帧重绘消残影。
  修法:
   ① lk_draw 首帧已 fill_rect(0,0,xres,yres,th->surface) 全屏不透明底(复核满足);
   ② run_lock_screen 进场 300ms(OVERLAY_LOCK_COVER_MS > 200ms 标记窗)后**重铺一次全屏
      不透明底**盖掉 App 抑制窗内的 1-2 帧残影;App 此后被抑制不再上屏,锁屏稳定遮盖;
   ③ 锁屏关闭(run_app_lock_overlay 关闭路径)已 pm_capture_app_frame+meow_ui_flush+
      app_overlay_statusbar 整帧重绘含状态栏(复核满足)。

新增 test_lock_render.c(6 断言:首帧整帧不透明覆盖断言 + 部分填充漏区对照)+ test_overlay_gate.c
扩展(S1c 手动息屏触发锁屏 4 断言 + S2b 重铺阈值>200ms 2 断言)。launcher 全量宿主回归 +
SDK 叠加/状态栏/输入回归全 PASS;mipsel 交叉 -Wall -Wextra 0 warning。
md5 62c817c204b9d9751a4d8f411d1eb69b(/usr/bin 只读,待 M6.5 固件打包;fw_content.conf 未动)。
2026-09-02 11:49:37 +08:00

462 lines
22 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"
#include "overlay_gate.h" /* OVERLAY_LOCK_COVER_MS(锁屏重铺阈值,与单测同源) */
/* §310 残影:锁屏处于 App 前台叠加会话时置位当前前台 App pid(run_app_lock_overlay 写入)。
* run_lock_screen 每圈据此探测 App 是否已死(组合 App 冻结缓冲 evdev 会在解锁瞬间回放成点击 /
* 叠加期 OOM 杀让 fb0 留残帧——探到死即中止锁屏,交回 waitpid 循环回收 + run_menu 重绘主屏)。
* 菜单直接锁屏(无 App)时保持 0 = 不探测。 */
int g_lock_overlay_pid = 0;
/* ---- 键盘几何(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);
/* §310 输入穿透(媒体保活 App 不被 SIGSTOP,实时读 evdev):锁屏页独占触控设备——
* EVIOCGRAB 使本 fd 之外的其它进程(保活 App 的 SDK 输入线程 / watchdog 手势)在该设备
* 上收不到事件(事件仅投递抓取者),且抓取期事件不会为未抓取 fd 缓冲/回放。由此无论 App
* 冻结与否,锁屏期**任何** App 都收不到触摸(真全模态)。失败(驱动不支持)则记日志降级,
* 仍靠 keep_running/SIGSTOP+标记隔离。关闭 tfd 时抓取自动解除(仍显式释放一次)。 */
if (tfd >= 0) {
int grab = 1;
if (ioctl(tfd, EVIOCGRAB, &grab) < 0)
logmsg("lock: EVIOCGRAB touch fail: %s (graceful degrade, marker+SIGSTOP isola only)",
strerror(errno));
}
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); /* 首帧(fill_rect 全屏不透明主题底 + 键盘) */
int unlocked = 0;
unsigned long long entry_ms = now_ms();
int covered_once = 0; /* §310 问题2-②:标记节流窗后重铺一次全屏底,盖掉 App 残帧 */
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;
}
/* §310 问题2-②:媒体保活 App 不冻结、SDK present 抑制有 ≤200ms 标记节流窗——锁屏首扑后
* App 仍可能再进 1-2 帧(部分脏区盖到锁屏上)。等标记窗过后(≥300ms)重铺一次全屏不透明底
* 盖掉 App 残帧;App 此后被抑制不再上屏,锁屏稳定遮盖(真机「App 控件盖到锁屏上」)。 */
if (!covered_once && now_ms() - entry_ms >= OVERLAY_LOCK_COVER_MS) {
covered_once = 1;
logmsg("lock: re-cover full opaque after marker window (app stave suppressed)");
lk_draw(entered, status, lock_until);
}
/* §310 残影:锁屏叠加期 App 崩溃(僵尸)→ 中止锁屏走统一退出路径(交回 waitpid 循环
* 回收 + run_menu 重绘主屏含状态栏)。仅当 run_app_lock_overlay 置位 g_lock_overlay_pid
* 才探(菜单直接锁屏无 App 可死,恒不中止)。 */
if (g_lock_overlay_pid > 0 &&
lck_overlay_should_abort(g_lock_overlay_pid, proc_is_zombie(g_lock_overlay_pid))) {
logmsg("lock: overlay app pid %d died during lock -> abort (unified exit path)",
g_lock_overlay_pid);
g_lock_overlay_pid = 0;
goto died;
}
/* 锁定到期自动解禁(清计数+戳),仅重画状态行 */
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) {
int grab = 0; /* 显式释放 EVIOCGRAB(关闭 tfd 也会自动解除,双保险) */
ioctl(tfd, EVIOCGRAB, &grab);
close(tfd);
}
kp_close(&kp_power);
kp_close(&kp_keys);
logmsg("lock: exit unlocked=%d screen_on=%d", unlocked, meow_screen_on);
return unlocked;
died:
if (tfd >= 0) {
int grab = 0;
ioctl(tfd, EVIOCGRAB, &grab);
close(tfd);
}
kp_close(&kp_power);
kp_close(&kp_keys);
logmsg("lock: exit overlay-app-died -> LCK_OVERLAY_RET_DIED");
return LCK_OVERLAY_RET_DIED;
}
/* 电源菜单「锁定」:**主动锁屏**(用户定案 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);
/* §310 输入穿透:设置页 PIN 模态同样独占触控设备(后台保活 App 不冻结实时读 evdev,防其
* 同坐标控件被 PIN 键盘点击双重触发)。失败降级(仍靠标记+SIGSTOP 隔离)。 */
if (tfd >= 0) {
int grab = 1;
if (ioctl(tfd, EVIOCGRAB, &grab) < 0)
logmsg("pin-modal: EVIOCGRAB touch fail: %s (graceful degrade)", strerror(errno));
}
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) {
int grab = 0; /* 显式释放 EVIOCGRAB(关闭 tfd 亦自动解除,双保险) */
ioctl(tfd, EVIOCGRAB, &grab);
close(tfd);
}
kp_close(&kp_power);
kp_close(&kp_keys);
return result;
}