/* meow_notif_panel.h — 通知中心面板的纯逻辑(header-only,宿主可单测)。 * * 定位:把通知中心面板里「无副作用、可独立判定」的部分抽成 static inline, * 供 launcher(meow_launcherd.c 内嵌面板)与宿主单测(test_notif_panel.c)共用同一份实现, * 避免两端漂移;绘制/触摸循环等依赖 launcher static 符号的部分留在 meow_launcherd.c 内。 * * 不依赖 launcher 头 / SDK 头,仅 —— 纯宿主可编。 */ #ifndef MEOW_NOTIF_PANEL_H #define MEOW_NOTIF_PANEL_H #include #include #include #include /* ---- 手势判定 ---- */ /* 左缘宽度:与 launcher meow_touch.h MEOW_EDGE_W(24) / SDK 全局左缘返回同口径。 * 顶部下滑打开通知中心时,左缘起点保留给「左缘返回」,不与其抢占(spec-ref §5.1)。 */ #ifndef MNP_EDGE_W #define MNP_EDGE_W 24 #endif /* 顶部热区下滑 → 打开通知中心(纯函数)。 * down_x/down_y=抬手原点;dx/dy=抬手位移(当前-原点,即 t_last - t_down);hotzone=顶部热区高 * (launcher=SB_BAR_H=28);thresh=下滑触发位移阈值(launcher=MENU_SWIPE_THRESH=65)。 * 返回 1=应打开。约束:起点不在左缘(down_x>=MNP_EDGE_W,左缘保留给返回)+ 起点落在顶部热区 * (down_y ∈ [0,hotzone))+ 向下主导(dy>=thresh 且 |dy|>|dx|)。调用方通常先判左缘返回 * (meow_edge_back)+ 本函数,两者互斥。 */ static inline int mnp_top_swipe_down(int down_x, int down_y, int dx, int dy, int hotzone, int thresh) { if (down_x < MNP_EDGE_W) return 0; /* 左缘保留给返回手势 */ if (down_y < 0 || down_y >= hotzone) return 0; if (dy < thresh) return 0; /* 未达下滑阈值 / 向上 */ int ax = dx < 0 ? -dx : dx; return ax < dy; /* 向下主导(横向位移小于竖向) */ } /* 竖向主导上滑(面板内关闭):dy<=-thresh 且 |dy|>|dx|。返回 1=应关闭。 */ static inline int mnp_vertical_up(int dx, int dy, int thresh) { if (dy > -thresh) return 0; int ax = dx < 0 ? -dx : dx; return ax < -dy; } /* ---- 滑条 ---- */ /* 滑条值映射:px(屏幕坐标)→ 值,clamp 到 [min,max]。sx=track 左缘、sw=track 宽。 * 与 launcher 设置页 s_slider_val_from_x 同构(此处参数化,供面板亮度/音量滑条与单测用)。 */ static inline int mnp_slider_val_from_x(int px, int sx, int sw, int min, int max) { int range = max - min; int v = min + (range ? (px - sx) * range / sw : 0); if (v < min) v = min; if (v > max) v = max; return v; } /* ---- 亮度 clamp ---- */ /* 亮度 clamp:下限 vmin(=display.brightness_min,≥1 防黑屏)、上限 100(防闪屏)。 * vmin<1 时钳到 1。与 SYSTEM_SETTINGS §1 / spec-ref A-3 一致(旧文档 25 为陈旧值)。 */ static inline int mnp_brightness_clamp(int v, int vmin) { if (vmin < 1) vmin = 1; if (v < vmin) v = vmin; if (v > 100) v = 100; return v; } /* ---- 时间显示 ---- */ /* 通知时间(created_ms 墙钟 epoch ms)→ 显示串。 * <60s「刚刚」;<60min「N分钟前」;否则本地「HH:MM」。now_ms=当前墙钟 epoch ms。 */ static inline void mnp_time_ago(uint64_t created_ms, uint64_t now_ms, char *buf, int len) { if (!buf || len <= 0) return; if (now_ms < created_ms) now_ms = created_ms; /* 时钟回拨防御 */ uint64_t diff = now_ms - created_ms; if (diff < 60000u) { if (len >= 1) snprintf(buf, (size_t)len, "刚刚"); return; } if (diff < 3600000u) { snprintf(buf, (size_t)len, "%llu分钟前", (unsigned long long)(diff / 60000u)); return; } time_t t = (time_t)(created_ms / 1000u); struct tm tm; if (localtime_r(&t, &tm)) snprintf(buf, (size_t)len, "%02d:%02d", tm.tm_hour, tm.tm_min); else if (len >= 1) snprintf(buf, (size_t)len, "--:--"); } /* ---- 列表命中 ---- */ /* 通知列表行命中:y 为绝对纵坐标;list_top/list_bot 为列表区上下缘;row_h 为行高。 * 返回行号(0 起);越界返回 -1。 */ static inline int mnp_row_at(int y, int list_top, int list_bot, int row_h) { if (y < list_top || y >= list_bot) return -1; int r = (y - list_top) / row_h; return r; } /* 列表滚动 clainmp:把 scroll 钳到 [0, total-view]。content 高度=total(总行数*row_h)。 */ static inline int mnp_scroll_clamp(int scroll, int total, int view) { if (total <= view) return 0; int mx = total - view; if (scroll < 0) scroll = 0; if (scroll > mx) scroll = mx; return scroll; } #endif /* MEOW_NOTIF_PANEL_H */