Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/meow_notif_panel.h
T
MeowOS e6f0b665e3 refactor(launcher): 拆分 meow_launcherd.c(9778行)→meow_launcherd/ 多文件(agent-149)
- 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
2026-08-30 21:38:30 +08:00

295 lines
14 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.
/* 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 */