- 14 模块 .c + meow_launcher_internal.h(642): util/font_theme/fb_compositor/settings_page/power_menu/notif_center/app_launcher/main_menu/audio/backlight/net/keys/about/statusbar; meow_launcherd.c 已删 - 按行逐字搬移(0-diff), 118函数提升非static+47组extern(内部头), #if0退役区不编入(源码留.pre_split.bak) - 关于本机小改: 作者 AskaEth/17 + info/item[13]防越界 + s_dd_modal 超高滚动(clamp/触摸滚动/音量键跟随/裁剪, 仅超高启用) - build_launcher.sh 编译列表→模块; 文档/注释引用 meow_launcherd.c→模块/目录 - 编译0warning/-Wall -Wextra告警集与原文一致(77条无新增)/qemu-mipsel冒烟一致(--scan/--sysdump); launcher=c0799357(505KB; md5因多文件重编译与原文异,非回归依据) - 备份 meow_launcherd.c.pre_split.bak/meow_launcherd.pre_split.bak; M2.69(0.7.64)已推卡并用户验证OK
295 lines
14 KiB
C
295 lines
14 KiB
C
/* meow_notif_panel.h — 通知中心面板的纯逻辑(header-only,宿主可单测)。
|
||
*
|
||
* 定位:把通知中心面板里「无副作用、可独立判定」的部分抽成 static inline,
|
||
* 供 launcher(notif_center.c 内嵌面板)与宿主单测(test_notif_panel.c)共用同一份实现,
|
||
* 避免两端漂移;绘制/触摸循环等依赖 launcher static 符号的部分留在 notif_center.c 内。
|
||
*
|
||
* 不依赖 launcher 头 / SDK 头,仅 <stdint.h> <string.h> <stdio.h> <time.h> —— 纯宿主可编。
|
||
*/
|
||
#ifndef MEOW_NOTIF_PANEL_H
|
||
#define MEOW_NOTIF_PANEL_H
|
||
|
||
#include <stdint.h>
|
||
#include <string.h>
|
||
#include <stdio.h>
|
||
#include <time.h>
|
||
|
||
/* ---- 手势判定 ---- */
|
||
|
||
/* 左缘宽度:与 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;
|
||
}
|
||
|
||
/* 滑条值的「跳步」吸附:把 v 取到最近的 step 整数倍。
|
||
* step<=1 原样返回(默认 audio.volume_step=1 ⇒ 连续,与设置页滑条逐值行为一致);
|
||
* step>1(可配 1-5)时吸附到最近整数倍,减少拖动逐值写 DAC/刷状态栏(音量滑条卡顿修复)。
|
||
* 不处理负数(音量/亮度值域均 ≥0);结果应再经 mnp_brightness_clamp / vol_clamp。 */
|
||
static inline int mnp_step_round(int v, int step)
|
||
{
|
||
if (step <= 1) return v;
|
||
int r = ((v + step / 2) / step) * step;
|
||
if (r < 0) r = 0;
|
||
return r;
|
||
}
|
||
|
||
/* ---- 亮度 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;
|
||
}
|
||
|
||
/* 列表滚动 clamp:把 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;
|
||
}
|
||
|
||
/* ---- 列表拖动滚动(方向遵循 input.swipe_invert,与 launcher 设置页列表同式)---- */
|
||
|
||
/* 最大滚动位移:total=内容总高、view=可视区高。内容不足视口时恒 0(不滚动)。 */
|
||
static inline int mnp_scroll_max(int total, int view)
|
||
{
|
||
return (total > view) ? (total - view) : 0;
|
||
}
|
||
|
||
/* 进入滚动时锁定的 scroll 基准(保证触发那一刻不跳变,连续跟手)。
|
||
* scroll=进入时 scroll;d=进入时位移(当前 y-原点 y,屏幕 y 向下为正);
|
||
* invert=0(标准:手指上滑→scroll 增大=内容向上滚看后面项)映射 -d;invert=1(反向)映射 +d。
|
||
* 与设置页 run_settings_page 的 `scroll_base = scroll_off + (invert ? -ddy : ddy)` 同式。 */
|
||
static inline int mnp_scroll_base(int scroll, int d, int invert)
|
||
{
|
||
return scroll + (invert ? -d : d);
|
||
}
|
||
|
||
/* 由基准 + 当前位移推目标 scroll(含方向映射);结果交 mnp_scroll_clamp 钳到 [0,max]。
|
||
* 与设置页 `scroll_off = clamp(scroll_base + (invert ? dydt : -dydt))` 同式。 */
|
||
static inline int mnp_scroll_target(int base, int d, int invert)
|
||
{
|
||
return base + (invert ? d : -d);
|
||
}
|
||
|
||
/* 是否已越过滚动边界成「回弹」(拖到顶/底仍继续拖)——用于列表滚满后继续拖 → 关闭。
|
||
* target=未钳位目标 scroll;max=mnp_scroll_max(...);tol=回弹容差(px,超过才认,
|
||
* 避免在边界小幅抖动即误关)。返回 1=已越过边界(回弹,可触发关闭语义)。 */
|
||
static inline int mnp_scroll_rebound(int target, int max, int tol)
|
||
{
|
||
return (target < -tol || target > max + tol);
|
||
}
|
||
|
||
/* ---- 开关平移动画(状态时间轴,纯逻辑,可单测)----
|
||
* 通知中心面板「打开/关闭滑入/收回」= 面板内容整体做 y-offset 平移(参照 SDK §5.2 快照 blit 平移)。
|
||
* 状态:progress p ∈ [0, fix],0=全隐藏(面板内容在面板区上缘之外),fix=到位(offset_y=0)。
|
||
* 本组函数只做标量时间轴/裁切计算,不碰 fb/几何——供 launcher(notif_center.c 内嵌面板)与
|
||
* 宿主单测(test_notif_panel.c)共用同一份实现,避免两端漂移。 */
|
||
|
||
/* 进度 p([0,fix])→ 面板内容 y-offset(px,∈[-ph,0])。
|
||
* p=0 → -ph(内容上移出面板区,全隐藏);p=fix → 0(到位)。线性。 */
|
||
static inline int mnp_anim_off(int p, int fix, int ph)
|
||
{
|
||
return (int)((long long)(p - fix) * ph / fix);
|
||
}
|
||
|
||
/* 沿时间轴单向推进:dt_ms(距上次 tick 的单调毫秒差)→ 新 p(向 target 逼近并 clamp)。
|
||
* fix=进度分母(如 1024);dur_ms=单程时长(ms)。dt_ms<=0 或已达 target 时返回原 p。
|
||
* 为保证单调前进(防快循环 dt 为 0 卡住),dt_ms>0 且未到位时 step 至少为 1。
|
||
* 反转(p>target)即回退,同样逐帧平滑。 */
|
||
static inline int mnp_anim_step(int p, int target, int fix, unsigned long long dt_ms, int dur_ms)
|
||
{
|
||
if (dt_ms == 0) return p;
|
||
if (p == target) return p;
|
||
long long step = (long long)dt_ms * fix / (dur_ms > 0 ? dur_ms : 1);
|
||
if (step < 1) step = 1;
|
||
if (target > p) { p += (int)step; if (p > target) p = target; }
|
||
else { p -= (int)step; if (p < target) p = target; }
|
||
return p;
|
||
}
|
||
|
||
/* 面板覆盖区底缘(screen y,exclusive):off 时面板可见区 = [top, top+ph+off)。
|
||
* off∈[-ph,0] ⇒ 结果∈[top, top+ph]。top 通常=状态栏高(NCP_TOP=28,不盖状态栏)。
|
||
* 调用方用「顶=top、高=covered_bot-top」作为脏区上缘/裁切范围。 */
|
||
static inline int mnp_anim_covered_bot(int top, int ph, int off)
|
||
{
|
||
return top + ph + off;
|
||
}
|
||
|
||
/* ---- 勿扰(DND)过滤(通知中心面板内开关 → 基于 urgency 过滤,纯逻辑,可单测)----
|
||
* urgency 见 meow_notif.h:0=LOW 低 / 1=NORMAL 普通 / 2=HIGH 高 / 3=CRITICAL 紧急(缺省 NORMAL)。
|
||
* dnd=0(关):全部显示;dnd=1(开):只显示 urgency>=2(HIGH/CRITICAL)。返回 1=应显示。 */
|
||
static inline int mnp_dnd_should_show(int urgency, int dnd)
|
||
{
|
||
if (!dnd) return 1;
|
||
return urgency >= 2;
|
||
}
|
||
|
||
/* ---- 单条通知行左滑删除(纯逻辑,可单测)----
|
||
* 交互(launcher run_notif_center):行内左横滑(横向主导+左移)→ 该行跟随左移(off<0,露出删除底);
|
||
* 松手过阈值 → meow_notif_dismiss + 行移除/重排;未过/反向滑回 → 回弹复原(跟手、半程可回)。
|
||
* 本组函数只做标量:clamp / 删除阈值 / 落位步进,不碰 fb/几何——供 launcher 与宿主单测共用。 */
|
||
|
||
/* 行左移 clamp:把手指位移钳到 [-row_w, 0](正向=右移钳 0;越左钳 -row_w=卡全滑出)。
|
||
* row_w=行宽(如 NCP_ROW_W),off<=0 表示左移程度(负得多=滑出多)。 */
|
||
static inline int mnp_swipe_clamp(int off, int row_w)
|
||
{
|
||
if (off > 0) off = 0;
|
||
if (off < -row_w) off = -row_w;
|
||
return off;
|
||
}
|
||
|
||
/* 松手删除判定:│off│ >= row_w*frac_pct/100 或 │off│ >= min_px → 返回 1(应删除);
|
||
* 否则 0(未过阈值/半程取消 → 回弹)。frac_pct 为行宽百分比(如 40)、min_px 为绝对像素保底
|
||
* (防极窄行/极小滑也判删,取二者较大者实际生效——满足「≥row_w 某比例 或 ≥ 阈值 px」)。 */
|
||
static inline int mnp_swipe_delete(int off, int row_w, int frac_pct, int min_px)
|
||
{
|
||
if (off > 0) off = 0;
|
||
int amt = -off; /* 左移幅度(px,>=0) */
|
||
if (amt >= row_w * frac_pct / 100) return 1;
|
||
if (amt >= min_px) return 1;
|
||
return 0;
|
||
}
|
||
|
||
/* 落位动画线性步进:off 沿方向向 target 走 dt_ms*px_per_ms,到 target 即停(可正/反向回弹或滑出)。
|
||
* dt_ms=距上次 tick 单调毫秒差;px_per_ms=速率(px/ms,如 3)。dt_ms<=0 或已到位返回原 off。
|
||
* 单调、可打断(任意时刻改 off 即反向),与面板开关平移动画(mnp_anim_step)同「状态时间轴」思想,
|
||
* 但按固定 px/ms 而非剩余比例(行位移远小于面板高,线性更稳)。 */
|
||
static inline int mnp_swipe_step(int off, int target, unsigned long long dt_ms, int px_per_ms)
|
||
{
|
||
if (dt_ms == 0 || off == target) return off;
|
||
int delta = target - off;
|
||
int dir = (delta < 0) ? -1 : 1;
|
||
long long absd = (long long)(dir < 0 ? -delta : delta);
|
||
long long step = (long long)dt_ms * px_per_ms;
|
||
if (step > absd) step = absd;
|
||
if (step < 1) step = 1; /* 单调前进,防快循环 dt 极小卡住 */
|
||
return off + dir * (int)step;
|
||
}
|
||
|
||
/* ---- ⑧ 一键清除通知判定(纯逻辑,可单测)----
|
||
* 通知中心「一键清除」= 把可清除的非活动通知先转储留档(走既有 dump 链),再清空显示;
|
||
* 红线(源:用户 2026-08-30):① ongoing 常驻、② 媒体通知(媒体会话单例)、③ 带进度条进行中的通知
|
||
* (progress>=0;终态 100% 已由 meow_notif_update_progress 里程碑落盘释放,不再活跃)——一律保留。
|
||
* 本函数只做「该不该清」的标量判定,不碰存储/fb;ongoing/media/progress 直读 meow_notif_t 字段。 */
|
||
|
||
/* 「可清除」判定:非 ongoing 且 非 media 且 无进行中进度(progress<0)。返回 1=应清除;0=保留(红线)。 */
|
||
static inline int mnp_clearable(int ongoing, int media, int progress)
|
||
{
|
||
return !ongoing && !media && progress < 0;
|
||
}
|
||
|
||
/* ---- ⑨ 面板「跟手下拉、半程可回」(纯逻辑,可单测)----
|
||
* 把面板打开/关闭从「触发式时间轴过渡」升级为「手指拖动实时跟手」:progress∈[0,fix],
|
||
* reveal(面板当前露出高度,∈[0,ph])随手指位移连续映射;松手按阈值锚定补到位/收回。
|
||
* 本组函数只做标量:reveal→progress / seed+delta clamp / 松手锚定,不碰 fb/几何——
|
||
* 供 launcher(notif_center.c 内嵌面板)与宿主单测(test_notif_panel.c)共用同一份实现。 */
|
||
|
||
/* 跟手 reveal→progress:reveal∈[0,ph](面板露出高度)→ p∈[0,fix]。线性、方向无关。
|
||
* 与 mnp_anim_off 互逆(off = -ph + reveal;p = fix*reveal/ph)。 */
|
||
static inline int mnp_drag_progress(int reveal, int fix, int ph)
|
||
{
|
||
if (reveal <= 0) return 0;
|
||
if (reveal >= ph) return fix;
|
||
return (int)((long long)reveal * fix / ph);
|
||
}
|
||
|
||
/* reveal 跟手 clamp:seed(进入拖动时锁定的露出高) + delta(自进入竖向位移, y 向下为正) → clamp[0,ph]。
|
||
* 连续可反向:反向 delta 减小 reveal,未到边界不跳变(半程可回)。ph<=0 时恒 0。 */
|
||
static inline int mnp_drag_reveal(int seed, int delta, int ph)
|
||
{
|
||
if (ph <= 0) return 0;
|
||
int r = seed + delta;
|
||
if (r < 0) r = 0;
|
||
if (r > ph) r = ph;
|
||
return r;
|
||
}
|
||
|
||
/* 松手锚定:progress p(∈[0,fix])与阈值(fix*thr_pct/100,如 40%→410)相比。
|
||
* 返回 1=≥阈值 → 前去 fix(打开完成 / 关闭手势未过阈值→回弹重新展开);
|
||
* 返回 0=<阈值 或 反向拖回 → 回 0(开半程取消收回 / 关闭过阈值→收回)。 */
|
||
static inline int mnp_drag_anchor(int p, int fix, int thr_pct)
|
||
{
|
||
int thr = fix * thr_pct / 100;
|
||
return p >= thr ? 1 : 0;
|
||
}
|
||
|
||
#endif /* MEOW_NOTIF_PANEL_H */
|