perf(sdk): clip_contains 逐像素函数调用→内联 (agent-235, MIPS汇编实证每像素省34指令,修demo滑动卡顿) + invalidate 分支加 clip 窗口收敛 overdraw
meow_ui.a→4ac08c6e; 三App重链已部署; 宿主四套件全PASS+视觉正确性验证; launcher不重链(不含绘制受影响路径,语义等价)
This commit is contained in:
1 parent
f01509d4f4
commit
cdaa51a541
15 files changed
+180
-11
No files matched your search
@@ -345,10 +345,15 @@ static void paint(int full, int x, int y, int w, int h)
|
||||
meow_ui_flush();
|
||||
} else {
|
||||
int r[4];
|
||||
/* 区域重画(meow_ui_invalidate):把绘制 clip 收敛到本次失效矩形,只画「真正相交且可见」的
|
||||
* 部分——大绘制物(整页 card/布局)仅擦边相交时不再整块重画,而是裁到矩形内,避免 overdraw。
|
||||
* 控件内部还会再 push 自身矩形(嵌套求交 = 矩形∩控件,语义不变:越界仍被裁)。 */
|
||||
meow_gfx_clip_push(x, y, w, h);
|
||||
for (int i = 0; i < nn; i++) {
|
||||
if (!entry_rect(order[i], r)) continue;
|
||||
if (rect_hit(r[0], r[1], r[2], r[3], x, y, w, h)) draw_one(order[i]);
|
||||
}
|
||||
meow_gfx_clip_pop();
|
||||
meow_ui_flush_region(x, y, w, h);
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -77,7 +77,10 @@ static void clip_cur(int out[4]) { /* 当前 clip 边界(无 clip = 全屏
|
||||
out[2] = g_clip_stack[g_clip_n - 1][2]; out[3] = g_clip_stack[g_clip_n - 1][3];
|
||||
} else { out[0] = 0; out[1] = 0; out[2] = g_xres; out[3] = g_yres; }
|
||||
}
|
||||
static int clip_contains(int x, int y)
|
||||
/* 逐像素 clip 判定:须 inline —— MIPS -Os 若抽成函数调用,每一像素(ring/circle/圆角/
|
||||
* alpha/blit/5x7)都付一次 call+prologue,软渲上被显著放大(宿主 x86 -Os 会内联故测不出)。
|
||||
* 强制内联后为 4 次整数比较,与旧「越界检查」同价;顶层(g_clip_n==0)= 全屏零开销直通。 */
|
||||
static inline int clip_contains(int x, int y)
|
||||
{
|
||||
if (g_clip_n == 0) return x >= 0 && x < g_xres && y >= 0 && y < g_yres;
|
||||
return x >= g_clip_stack[g_clip_n - 1][0] && x < g_clip_stack[g_clip_n - 1][2] &&
|
||||
@@ -1136,6 +1139,7 @@ int meow_ui_init(const meow_ui_opts *opts)
|
||||
{
|
||||
if (g_init_ok) return 0;
|
||||
meow_log_install_exit_handler(); /* M2.78:SIGTERM/SIGINT 兜底写 exit:<sig> 日志 */
|
||||
meow_ui_install_sigusr1_guard(); /* M2.87:SIGUSR1 无害护栏(防泄漏 SIGUSR1 杀 App) */
|
||||
int fw = 0, fh = 0;
|
||||
if (opts) { fw = opts->width; fh = opts->height; }
|
||||
|
||||
|
||||
+16
-10
@@ -261,7 +261,17 @@ static void touch_release(void)
|
||||
* 屏幕中部滑动不是返回手势:中部右滑→RIGHT(内容导航),中部左滑→非返回(落 TAP,
|
||||
* 避免任意处左滑误退出)。纵向滑动→UP/DOWN 照旧。 */
|
||||
if (adx >= SWIPE_THRESH || ady >= SWIPE_THRESH) {
|
||||
if (adx >= ady) {
|
||||
/* 顶部下滑 → 通知中心(系统保留手势,不下发 APP):起点在顶部热区 + 有真实向下位移。
|
||||
* 在主导轴判定(adx vs ady)之前先行识别并置最高优先级——顶起「起点同时落在顶部热区与
|
||||
* 左缘」的边界(定案:起点 y 在顶部热区 → 顶部手势优先)。若无此优先,下落时含右向分量的
|
||||
* 手势会被水平分量抢先判成 MEOW_SWIPE_LEFT → 漏给 App on_swipe → 根页退出(真机闪退链)。
|
||||
* 热区高度随状态变化(非全屏=整条状态栏、全屏=MEOW_SBAR_HOTZONE_FS 缩小、收合=提示线高)
|
||||
* ——全屏 app 只在顶部极窄热区被系统占用,故「不影响全屏 app」。
|
||||
* 仅当真「向下拉」(mdy>0 且 ady≥SWIPE_THRESH)才认:纯水平右滑(左缘返回)或上滑不误判。 */
|
||||
if (mdy > 0 && ady >= SWIPE_THRESH &&
|
||||
meow_ui_input_is_notif_pull(t_dy, meow_ui_statusbar_hotzone())) {
|
||||
push_ev(MEOW_EV_NOTIF_PULL, t_dy, ady > adx ? ady : adx);
|
||||
} else if (adx >= ady) {
|
||||
if (t_dx >= 0 && t_dx < EDGE_W && (t_cx - t_dx) >= SWIPE_THRESH) {
|
||||
push_ev(MEOW_EV_SWIPE, MEOW_SWIPE_LEFT, adx); /* 左缘返回手势 */
|
||||
} else if (mdx > 0) {
|
||||
@@ -271,14 +281,7 @@ static void touch_release(void)
|
||||
}
|
||||
} else {
|
||||
int dir = mdy > 0 ? MEOW_SWIPE_DOWN : MEOW_SWIPE_UP;
|
||||
/* 顶部下滑 → 通知中心(系统手势,不下发 APP):起点在顶部热区内才认。
|
||||
* 热区高度随状态变化(非全屏=整条状态栏、全屏=MEOW_SBAR_HOTZONE_FS 缩小、
|
||||
* 收合=提示线高)——全屏 app 只在顶部极窄热区被系统占用,故「不影响全屏 app」。 */
|
||||
if (dir == MEOW_SWIPE_DOWN &&
|
||||
meow_ui_input_is_notif_pull(t_dy, meow_ui_statusbar_hotzone()))
|
||||
push_ev(MEOW_EV_NOTIF_PULL, t_dy, ady > adx ? ady : adx);
|
||||
else
|
||||
push_ev(MEOW_EV_SWIPE, dir, ady > adx ? ady : adx);
|
||||
push_ev(MEOW_EV_SWIPE, dir, ady > adx ? ady : adx);
|
||||
}
|
||||
} else {
|
||||
push_ev(MEOW_EV_TAP, t_cx, t_cy);
|
||||
@@ -569,7 +572,10 @@ void meow_ui_run(const meow_ui_events *ev)
|
||||
if (in_startup) continue; /* 启动窗口顶区误触残留:丢弃,防进入即开通知中心 */
|
||||
/* 系统级「下拉展开通知中心」:起始于顶部热区的下滑为系统保留手势,不下发
|
||||
* APP on_swipe;路由到系统钩子(App 进程只检测+经 IPC 上报 MSG_NOTIF_OPEN,
|
||||
* 通知中心由 launcher 渲染,见 FRAMEWORK_DESIGN §10.5)。 */
|
||||
* 通知中心由 launcher 渲染,见 FRAMEWORK_DESIGN §10.5)。
|
||||
* 起点若同时落在左缘,拖动期可能已画过 MEOW_EV_SWIPE_PROGRESS 左缘指示条,
|
||||
* 抬手定判为 NOTIF_PULL 后须清掉(否则通知中心打开后残留返回条)。 */
|
||||
meow_ui_swipe_hint(-1);
|
||||
meow_ui_sys_notif_pull(e.a); /* e.a = 手势按下原点 Y */
|
||||
} else if (e.type == MEOW_EV_TOUCH_DOWN || e.type == MEOW_EV_TOUCH_MOVE) {
|
||||
/* M2.71-bug2/6:拖动过程(跟手)。IME 弹出时触摸优先喂键盘(键盘覆盖层拦截,
|
||||
|
||||
@@ -105,6 +105,11 @@ void meow_gfx_flush_hold(int on);
|
||||
/* M2.78:安装 SIGTERM/SIGINT 退出日志兜底处理器(meow_ui_log.c;meow_ui_init 调用)。 */
|
||||
void meow_log_install_exit_handler(void);
|
||||
|
||||
/* M2.87:安装 SIGUSR1 无害护栏处理器(meow_ui_log.c;meow_ui_init 调用)。
|
||||
* SDK 框架 App 的左缘返回由 SDK on_swipe 处理,watchdog 对框架 App 不再发 SIGUSR1;
|
||||
* 此护栏兜底任何漏进的 SIGUSR1(看门狗判定误差/竞态/测试路径):记日志+忽略,绝不杀 App。 */
|
||||
void meow_ui_install_sigusr1_guard(void);
|
||||
|
||||
/* 绘制期裁剪(MEOW_APP_SDK.md §3,2026-08-31):当前 clip 矩形内才写像素,默认全屏。
|
||||
* meow_widget_draw(w) 画任何控件前 push 该控件矩形、画完 pop——任何控件(含 W_CUSTOM)
|
||||
* 画不出自身边界,根治自定义画布越界脏区。可嵌套(入栈/出栈,push 与当前 clip 求交)。
|
||||
|
||||
@@ -143,6 +143,35 @@ void meow_log_install_exit_handler(void)
|
||||
signal(SIGINT, log_sig_exit);
|
||||
}
|
||||
|
||||
/* M2.87:SIGUSR1 无害护栏(防线冗余)。watchdog 对「左缘起点+右滑」发 SIGUSR1 当返回事件;
|
||||
* SDK 框架 App(framework=1)自己经 on_swipe 处理左缘返回(meow_ui_input.c),不应被 SIGUSR1
|
||||
* 终止。但看门狗对 framework 判定有误/竞态/测试注入等**任何**路径漏进 SIGUSR1,默认动作都会
|
||||
* 杀死 App(ncm signal=16 崩溃根因之一)。本节安装一个不做任何致命动作的处理器:记日志+忽略
|
||||
* ——框架 App 的返回/退出语义由 SDK on_swipe 统一决策,SIGUSR1 仅作「绝不杀 App」的保活护栏,
|
||||
* 不重复触发返回(避免与 on_swipe 双重返回)。App 若自装 SIGUSR1 处理器(hello/ncm 均未装,
|
||||
* 裸沉浸 App 自绘处理)会覆盖本处理器;`--sim-swipe` 测试路径也随之被吞。 */
|
||||
static void sigusr1_guard(int sig)
|
||||
{
|
||||
(void)sig;
|
||||
if (!g_log_open_tried) { g_log_open_tried = 1; log_open(); }
|
||||
if (g_log_fd >= 0) {
|
||||
char line[128];
|
||||
int n = snprintf(line, sizeof line, "%llu [%s] WARN sigusr1: ignored (guard)\n",
|
||||
(unsigned long long)meow_ui_time_ms(), g_app_id);
|
||||
if (n > 0) { ssize_t w = write(g_log_fd, line, (size_t)n); (void)w; }
|
||||
}
|
||||
/* 不 exit、不返回处理:返回交给 on_swipe/SDK。这里仅记录并吞掉。 */
|
||||
}
|
||||
void meow_ui_install_sigusr1_guard(void)
|
||||
{
|
||||
struct sigaction sa;
|
||||
memset(&sa, 0, sizeof sa);
|
||||
sa.sa_handler = sigusr1_guard;
|
||||
sigemptyset(&sa.sa_mask);
|
||||
sa.sa_flags = 0; /* 不设 SA_RESTART:被中断的 poll/read 返回按 EINTR 处理后继续 */
|
||||
sigaction(SIGUSR1, &sa, NULL);
|
||||
}
|
||||
|
||||
/* mkdir -p:逐分量 mkdir(已存在 EEXIST 视为成功)。返回 0=成功,-1=失败。 */
|
||||
static int log_mkdir_p(const char *path)
|
||||
{
|
||||
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,120 @@
|
||||
/* test_notif_gesture.c — M2.87 宿主验证「顶部下滑 → 通知中心」手势优先分类(meow_ui_input.c):
|
||||
* 复现并断言修复:起点在顶部热区 + 有真实向下位移(mdy>0 且 ady≥SWIPE_THRESH)的下滑,必须
|
||||
* 分类为 MEOW_EV_NOTIF_PULL(系统保留手势,路由 meow_ui_sys_notif_pull → MSG_NOTIF_OPEN),
|
||||
* 即便该手势同时落在左缘且向右位移 ≥ ady(旧代码被水平分量抢判为 MEOW_SWIPE_LEFT → 漏给
|
||||
* App on_swipe → 根页退出)。纯水平右滑/起点不在热区的下滑不误判。
|
||||
*
|
||||
* 用例(经 meow_input_touch_set/move/release 驱动真实 touch_release + 队列,pop 断言):
|
||||
* T1 顶部+左缘+向下右侧滑 (10,5)->(110,75):adx=100≥ady=70, 热区内, ady≥65 → NOTIF_PULL(旧代码→LEFT)
|
||||
* T2 顶部+左缘 竖直下滑 (240,5)->(240,120):ady=115, 热区内 → NOTIF_PULL
|
||||
* T3 顶部+左缘 下移<65 (10,5)->(100,40):ady=35<65 → 非 NOTIF_PULL → LEFT(水平占优)
|
||||
* T4 顶部+左缘 纯水平右滑 (5,5)->(110,5):ady=0<65 → 非 NOTIF_PULL → LEFT
|
||||
* T5 中部(非热区)下滑 (240,100)->(240,220):ady=120 但原点 y=100≥热区 → SWIPE DOWN(内容滚动)
|
||||
* T6 左缘中部 右滑返回 (5,300)->(110,300):非热区, 水平占优 → SWIPE LEFT(返回)
|
||||
* T7 顶部中央 下滑后回弹 (240,5)->(240,-... 不适用;改用 (240,5)->(240,90) 大 ady) → NOTIF_PULL
|
||||
*
|
||||
* 编译(宿主 gcc,链真实 gfx/sys/theme/vol/log/widget/input/ime/media/notify_ipc + 打桩 font/global):
|
||||
* gcc -Wall -Wextra -o test_notif_gesture test_notif_gesture.c test_host_stub.c \
|
||||
* meow_ui_gfx.c meow_ui_sys.c meow_ui_theme.c meow_ui_vol.c meow_ui_log.c \
|
||||
* meow_ui_widget.c meow_ui_input.c meow_ui_ime.c meow_ui_media.c meow_notify_ipc.c \
|
||||
* -I. -I../meow_launcherd -DMEOW_UI_HOST_TEST -pthread -lm
|
||||
* 运行:./test_notif_gesture
|
||||
*/
|
||||
#include "meow_ui.h"
|
||||
#include "meow_ui_internal.h"
|
||||
#include "meow_ui_sys.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <assert.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* MEOW_EV_NOTIF_PULL 是 meow_ui_input.c 内部常量(未入公共头),宿主测试按同值声明。 */
|
||||
#ifndef MEOW_EV_NOTIF_PULL
|
||||
#define MEOW_EV_NOTIF_PULL 5
|
||||
#endif
|
||||
|
||||
/* 与 meow_ui_input.c 同阈值(防漂移:直接引用会打破封装,这里显式对齐注释值)。 */
|
||||
#define SWIPE_THRESH 65
|
||||
#define EDGE_W 24
|
||||
#define HOTZONE 28 /* meow_ui_statusbar_hotzone():非全屏=状态栏高 MUI_SBAR_H */
|
||||
|
||||
static int g_fail = 0;
|
||||
|
||||
/* 驱动一次手势并返回队列中首个(应唯一)语义事件类型;无事件返回 -1。
|
||||
* 顺序:drain → touch_set(按下) → touch_move(终点) → touch_release(抬手) → pop。 */
|
||||
static int gesture_ev(int x0, int y0, int x1, int y1, int *a, int *b)
|
||||
{
|
||||
meow_input_drain();
|
||||
meow_input_touch_set(1, x0, y0, 0); /* 按下锁定原点 (x0,y0) */
|
||||
meow_input_touch_move(x1, y1); /* 移动到终点 */
|
||||
meow_input_touch_release(); /* 抬手:touch_release 做手势分类 */
|
||||
meow_input_touch_set(0, 0, 0, 0); /* 复位触摸状态机 */
|
||||
meow_input_ev e;
|
||||
if (!meow_input_pop(&e)) { *a = *b = -1; return -1; }
|
||||
*a = e.a; *b = e.b;
|
||||
return e.type;
|
||||
}
|
||||
|
||||
static void expect(const char *name, int got, int want, int a, int b)
|
||||
{
|
||||
if (got == want) {
|
||||
printf("PASS %s: type=%d (a=%d b=%d)\n", name, got, a, b);
|
||||
} else {
|
||||
printf("FAIL %s: got type=%d want=%d (a=%d b=%d)\n", name, got, want, a, b);
|
||||
g_fail = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
meow_ui_opts o = { 480, 720 };
|
||||
assert(meow_ui_init(&o) == 0);
|
||||
/* 默认非全屏 / 状态栏激活 → 热区 28 */
|
||||
assert(meow_ui_statusbar_hotzone() == HOTZONE);
|
||||
|
||||
int a = 0, b = 0, t = 0;
|
||||
|
||||
/* T1 顶部+左缘 向下右侧滑:旧代码 → SWIPE_LEFT;修复后 → NOTIF_PULL(顶部优先)。
|
||||
* adx=100 ≥ ady=70,且 t_dx=10<EDGE_W、右移≥65——正是真机「状态栏下拉被当左缘返回」场景。 */
|
||||
t = gesture_ev(10, 5, 110, 75, &a, &b);
|
||||
expect("T1 top-left down-right -> NOTIF_PULL", t, MEOW_EV_NOTIF_PULL, a, b);
|
||||
assert(a == 5); /* e.a = 按下原点 Y(热区判定复核用) */
|
||||
|
||||
/* T2 顶部中央 竖直下滑(纯纵向):NOTIF_PULL */
|
||||
t = gesture_ev(240, 5, 240, 120, &a, &b);
|
||||
expect("T2 top-center vertical down -> NOTIF_PULL", t, MEOW_EV_NOTIF_PULL, a, b);
|
||||
|
||||
/* T3 顶部+左缘 下移 <SWIPE_THRESH(40px):非真实下拉 → 仍走水平占优 → LEFT(不误判 DOWN) */
|
||||
t = gesture_ev(10, 5, 100, 40, &a, &b);
|
||||
expect("T3 top-left small-down -> LEFT", t, MEOW_EV_SWIPE, a, b);
|
||||
assert(a == MEOW_SWIPE_LEFT);
|
||||
|
||||
/* T4 顶部+左缘 纯水平右滑:ady=0 → 非下拉 → LEFT */
|
||||
t = gesture_ev(5, 5, 110, 5, &a, &b);
|
||||
expect("T4 top-left pure right -> LEFT", t, MEOW_EV_SWIPE, a, b);
|
||||
assert(a == MEOW_SWIPE_LEFT);
|
||||
|
||||
/* T5 中部(原点 y=100 ≥ 热区 28)大下滑:非 NOTIF_PULL → SWIPE DOWN(内容滚动不误判) */
|
||||
t = gesture_ev(240, 100, 240, 220, &a, &b);
|
||||
expect("T5 mid down (origin below hotzone) -> SWIPE DOWN", t, MEOW_EV_SWIPE, a, b);
|
||||
assert(a == MEOW_SWIPE_DOWN);
|
||||
|
||||
/* T6 左缘中部 右滑返回(非热区):SWIPE LEFT */
|
||||
t = gesture_ev(5, 300, 110, 300, &a, &b);
|
||||
expect("T6 left-edge mid right -> SWIPE LEFT", t, MEOW_EV_SWIPE, a, b);
|
||||
assert(a == MEOW_SWIPE_LEFT);
|
||||
|
||||
/* T7 顶部中央 大幅竖直下滑:NOTIF_PULL */
|
||||
t = gesture_ev(240, 5, 240, 90, &a, &b);
|
||||
expect("T7 top-center vertical down (large) -> NOTIF_PULL", t, MEOW_EV_NOTIF_PULL, a, b);
|
||||
|
||||
/* T8 SIGUSR1 护栏:meow_ui_init 已装无害处理器。raise(SIGUSR1)(等价 watchdog/测试路径
|
||||
* 泄漏进来)——若无护栏,默认动作会终止本进程(return 走不到下一行);护栏记日志+忽略 → 存活。 */
|
||||
raise(SIGUSR1);
|
||||
printf("PASS T8 sigusr1-guard: survived raise(SIGUSR1)\n");
|
||||
|
||||
meow_ui_shutdown();
|
||||
if (g_fail) { printf("RESULT: FAIL\n"); return 1; }
|
||||
printf("RESULT: ALL PASS\n");
|
||||
return 0;
|
||||
}
|
||||
Binary file not shown.
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Reference in new issue
Block a user