fix(sdk+launcher): 图标不渲染真根因(agent-219) — meow_ui_img.c scale_buf cover 采样把乘写成除(÷缩放比→采到源图左上角透明区,全丢); 改乘+居中裁剪偏移修正; 宿主旧测试用纯色图掩盖了该bug。+ main_menu App退出/结构变化重绘时 fb_pan_buf0_force 无条件pan回buffer0(防App翻页留buffer1致菜单残帧)
meow_ui.a→3e0ee8f7 launcher→9d0efc2f hello→9609ea6e files→90f5cf07 ncm→be8be221(均已上卡); 真机截图证据 .tmp/meowbug/fix1.png+fix_b2_after.png
This commit is contained in:
1 parent
4166064365
commit
5999c2dfb9
8 files changed
+361
-18
No files matched your search
+12
-8
@@ -59,19 +59,23 @@ static unsigned char *scale_buf(const meow_img *im, int dw, int dh, int cover)
|
|||||||
unsigned char *out = (unsigned char *)malloc((size_t)dw * dh * 4);
|
unsigned char *out = (unsigned char *)malloc((size_t)dw * dh * 4);
|
||||||
if (!out) return NULL;
|
if (!out) return NULL;
|
||||||
float s = 1.0f;
|
float s = 1.0f;
|
||||||
float ox = 0.0f, oy = 0.0f;
|
|
||||||
if (cover) {
|
if (cover) {
|
||||||
|
/* 真正铺满(cover):每目标像素对应 s 个源像素,s = min(W/dw, H/dh)——缩放后图像
|
||||||
|
* 宽/高两维都不小于目标(可能外溢),居中裁方。居中裁剪偏移(源像素)= (W-dw*s)/2
|
||||||
|
* 恒 ≥ 0。曾误作 `s = max(sx,sy)` 且采样 `(dx+ox)/s`(除以缩放比):把整块输出映射到
|
||||||
|
* 源左上角 ~dw/s 像素的透明角——图标(透明角 logo)整块透明、不绘制,即
|
||||||
|
* 「icon: ... OK 但渲染不出(只显首字母占位)」根因。正确:源索引 = dx*s + 裁剪偏移
|
||||||
|
* (与下方非 cover 分支 `dx*im->w/dw` 同一乘向)。 */
|
||||||
float sx = (float)im->w / dw, sy = (float)im->h / dh;
|
float sx = (float)im->w / dw, sy = (float)im->h / dh;
|
||||||
s = sx > sy ? sx : sy; /* 铺满 → 可能外溢,居中裁 */
|
s = sx < sy ? sx : sy; /* 源像素/目标像素(铺满 → 取小) */
|
||||||
float sw = im->w / s, sh = im->h / s; /* 目标显示寸(sw×sh) */
|
float oxs = ((float)im->w - (float)dw * s) * 0.5f; /* 居中裁剪起点(源像素,≥0) */
|
||||||
ox = (float)(dw - sw) * 0.5f;
|
float oys = ((float)im->h - (float)dh * s) * 0.5f;
|
||||||
oy = (float)(dh - sh) * 0.5f;
|
if (oxs < 0) oxs = 0; if (oys < 0) oys = 0;
|
||||||
if (ox < 0) ox = 0; if (oy < 0) oy = 0;
|
|
||||||
for (int dy = 0; dy < dh; dy++) {
|
for (int dy = 0; dy < dh; dy++) {
|
||||||
int syi = (int)((dy + oy) / s);
|
int syi = (int)((float)dy * s + oys);
|
||||||
if (syi < 0) syi = 0; if (syi >= im->h) syi = im->h - 1;
|
if (syi < 0) syi = 0; if (syi >= im->h) syi = im->h - 1;
|
||||||
for (int dx = 0; dx < dw; dx++) {
|
for (int dx = 0; dx < dw; dx++) {
|
||||||
int sxi = (int)((dx + ox) / s);
|
int sxi = (int)((float)dx * s + oxs);
|
||||||
if (sxi < 0) sxi = 0; if (sxi >= im->w) sxi = im->w - 1;
|
if (sxi < 0) sxi = 0; if (sxi >= im->w) sxi = im->w - 1;
|
||||||
memcpy(out + ((size_t)dy * dw + dx) * 4,
|
memcpy(out + ((size_t)dy * dw + dx) * 4,
|
||||||
im->rgba + ((size_t)syi * im->w + sxi) * 4, 4);
|
im->rgba + ((size_t)syi * im->w + sxi) * 4, 4);
|
||||||
|
|||||||
+68
-10
@@ -6,12 +6,20 @@
|
|||||||
* meow_ui_run / IME 循环弹出分发。应用不再自读 event*。
|
* meow_ui_run / IME 循环弹出分发。应用不再自读 event*。
|
||||||
*
|
*
|
||||||
* 触摸:BTN_TOUCH 按下记录原点,抬起时位移 < 阈值 → TAP,否则按主导轴 → SWIPE。
|
* 触摸:BTN_TOUCH 按下记录原点,抬起时位移 < 阈值 → TAP,否则按主导轴 → SWIPE。
|
||||||
* 物理键:EV_KEY 值 1=按下 / 0=抬起(重复值 2 忽略)。M2.24 全面按键接管:电源/音量/播放/
|
* 物理键:EV_KEY 值 1=按下 / 0=抬起(重复值 2 忽略)。M2.55 起系统按键语义在 SDK 输入层消化:
|
||||||
* 上下曲全部由系统(watchdog/launcher)消费,SDK 吞噬不发给 APP(应用只收 tap/swipe/tick)。
|
* 电源 116 → 维持现状(息屏/亮屏/长按菜单由内核/sys_server/watchdog),SDK 不动、不下发 APP;
|
||||||
|
* 音量 114/115 → 调系统音量(meow_settings audio.volume + CS43198 DAC 写,volume_step/volume_max
|
||||||
|
* 约束,与 launcher 设置页/watchdog 同源);
|
||||||
|
* 播放/暂停 164、上一曲 165/19、下一曲 163 → 转发到当前媒体会话(App 是媒体源→本地回调,
|
||||||
|
* 否则经 IPC 发 launcher 转发)。
|
||||||
|
* 「App 收到的是语义事件」:上述音量/媒体键在 SDK 消化后**仍下发 MEOW_EV_KEY**(on_key),但
|
||||||
|
* App 不得把它们映射成自定义行为(APPS.md §3.4.1 语义不变)。
|
||||||
*/
|
*/
|
||||||
#include "meow_ui.h"
|
#include "meow_ui.h"
|
||||||
#include "meow_ui_internal.h"
|
#include "meow_ui_internal.h"
|
||||||
#include "meow_ui_sys.h"
|
#include "meow_ui_sys.h"
|
||||||
|
#include "meow_ui_vol.h"
|
||||||
|
#include "meow_ui_media.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
@@ -81,6 +89,45 @@ static uint64_t now_ms(void)
|
|||||||
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* settings.conf 路径(可被 env MEOW_SETTINGS_FILE 覆盖,宿主测试用;设备默认同 meow_ui_sys.c)。 */
|
||||||
|
static const char *meow_settings_path(void)
|
||||||
|
{
|
||||||
|
const char *e = getenv("MEOW_SETTINGS_FILE");
|
||||||
|
return (e && e[0]) ? e : "/usr/data/meow/settings.conf";
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 系统按键语义(M2.55:SDK 输入层消化,App 前台也生效)----
|
||||||
|
* 物理键是系统资源(APPS.md §3.4.1),语义全局统一。本函数在输入线程执行系统动作:
|
||||||
|
* 电源 116 → 息屏/亮屏/长按菜单(内核/sys_server/watchdog),SDK 不动(维持现状);
|
||||||
|
* 音量 114/115 → 调系统音量(meow_settings audio.volume + CS43198 DAC 写,与 launcher
|
||||||
|
* 设置页/watchdog 同源:volume_step/volume_max 约束),仅按下边沿(1)一步;
|
||||||
|
* 播放/上次曲 164/165/19/163 → 转发到当前媒体会话(App 是媒体源→本地回调;否则 IPC→launcher)。
|
||||||
|
* 仅处理按下边沿(val==1)触发系统动作;重复(2)/抬起(0)不重复执行。on_key 的下发由输入线程
|
||||||
|
* 在调用本函数后另行 push(音量/媒体键仍通知 App,App 不得映射)。 */
|
||||||
|
static void meow_input_handle_sys_key(int code, int val)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case MEOW_KEY_POWER: /* 电源:维持现状,SDK 不动 */
|
||||||
|
break;
|
||||||
|
case MEOW_KEY_VOLUMEDOWN:
|
||||||
|
case MEOW_KEY_VOLUMEUP:
|
||||||
|
if (val == 1) {
|
||||||
|
int dir = (code == MEOW_KEY_VOLUMEUP) ? +1 : -1;
|
||||||
|
int nv = meow_ui_vol_step(meow_settings_path(), dir); /* settings 语义+持久化 */
|
||||||
|
meow_ui_vol_dac_write(nv); /* 写真实 CS43198 DAC */
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case MEOW_KEY_PLAYPAUSE:
|
||||||
|
case MEOW_KEY_PREVIOUS:
|
||||||
|
case MEOW_KEY_NEXT:
|
||||||
|
case 19: /* KEY_PREVIOUSSONG 旧码 */
|
||||||
|
if (val == 1) meow_ui_media_sys_key(code);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void meow_input_drain(void)
|
void meow_input_drain(void)
|
||||||
{
|
{
|
||||||
pthread_mutex_lock(&g_qm);
|
pthread_mutex_lock(&g_qm);
|
||||||
@@ -335,14 +382,25 @@ static void *input_thread(void *arg)
|
|||||||
if (code == BTN_TOUCH) {
|
if (code == BTN_TOUCH) {
|
||||||
if (val && !t_down) touch_press(t_cx, t_cy);
|
if (val && !t_down) touch_press(t_cx, t_cy);
|
||||||
else if (!val && t_down) touch_release();
|
else if (!val && t_down) touch_release();
|
||||||
} else if (code == MEOW_KEY_POWER || code == MEOW_KEY_VOLUMEDOWN ||
|
} else if (code == MEOW_KEY_POWER) {
|
||||||
code == MEOW_KEY_VOLUMEUP || code == MEOW_KEY_PLAYPAUSE ||
|
/* M2.55(接 M2.24 纪律):电源=息屏/亮屏/长按菜单,由内核/sys_server/
|
||||||
code == MEOW_KEY_PREVIOUS || code == MEOW_KEY_NEXT || code == 19) {
|
* watchdog 统一处理,SDK 不动、也不下发 APP。维持现状。 */
|
||||||
/* M2.22(A)/M2.24 纪律:物理键(电源 116 / 音量 114,115 / 播放 164 /
|
} else if (code == MEOW_KEY_VOLUMEDOWN || code == MEOW_KEY_VOLUMEUP) {
|
||||||
* 上一曲 165(MEOW_KEY_PREVIOUS)/19(旧码) / 下一曲 163)全部是系统语义 —— watchdog/launcher 统一消费,
|
/* 音量键=系统音量(M2.55:SDK 输入层消化,App 前台也生效)。
|
||||||
* 绝不下发 APP。应用不碰物理键、不把任何物理键映射成确认/exit/返回/翻页
|
* 语义动作在 meow_input_handle_sys_key(仅按下边沿一步);随后仍下发
|
||||||
* (KEY_CONTROL_DESIGN §1.3 / APPS.md §3.4.1);APP 只接收 tap/swipe/tick。
|
* MEOW_EV_KEY(App on_key 收到语义事件,但不得映射成自定义行为)。 */
|
||||||
* 忽略该键,不发 MEOW_EV_KEY。 */
|
if (val == 1 || val == 0) {
|
||||||
|
meow_input_handle_sys_key(code, val);
|
||||||
|
push_ev(MEOW_EV_KEY, code, (val == 1) ? 1 : 0);
|
||||||
|
}
|
||||||
|
} else if (code == MEOW_KEY_PLAYPAUSE || code == MEOW_KEY_PREVIOUS ||
|
||||||
|
code == MEOW_KEY_NEXT || code == 19) {
|
||||||
|
/* 媒体键=转发到当前媒体会话(M2.55:SDK 输入层消化)。语义动作在
|
||||||
|
* meow_input_handle_sys_key;随后仍下发 MEOW_EV_KEY(App 不得映射)。 */
|
||||||
|
if (val == 1 || val == 0) {
|
||||||
|
meow_input_handle_sys_key(code, val);
|
||||||
|
push_ev(MEOW_EV_KEY, code, (val == 1) ? 1 : 0);
|
||||||
|
}
|
||||||
} else if (val == 1) { /* 按下边沿(忽略重复=2)——仅未知键(本设备无) */
|
} else if (val == 1) { /* 按下边沿(忽略重复=2)——仅未知键(本设备无) */
|
||||||
push_ev(MEOW_EV_KEY, code, 1);
|
push_ev(MEOW_EV_KEY, code, 1);
|
||||||
} else if (val == 0) { /* 抬起 */
|
} else if (val == 0) { /* 抬起 */
|
||||||
|
|||||||
@@ -30,6 +30,11 @@ static pthread_t g_thr;
|
|||||||
static int g_thr_on = 0;
|
static int g_thr_on = 0;
|
||||||
static volatile int g_quit = 0;
|
static volatile int g_quit = 0;
|
||||||
static meow_media_playctl_fn g_cb = NULL;
|
static meow_media_playctl_fn g_cb = NULL;
|
||||||
|
/* 本进程最后一次经 meow_ui_media_meta 上报的 state(0=停 1=播放 2=暂停 3=缓冲)。
|
||||||
|
* 供「物理播放/暂停键」做切换判据:播放中→暂停(2),否则→播放(1)。与 launcher
|
||||||
|
* now_playing.c 的同款判定(state==PLAYING ? 2 : 1)对齐。 */
|
||||||
|
static int g_last_state = 0;
|
||||||
|
#define MEOW_UI_MEDIA_STATE_PLAYING 1
|
||||||
|
|
||||||
/* 后台线程主体(定义在文件后段;on_playctl 懒起时引用)。 */
|
/* 后台线程主体(定义在文件后段;on_playctl 懒起时引用)。 */
|
||||||
static void *media_thread_main(void *arg);
|
static void *media_thread_main(void *arg);
|
||||||
@@ -177,6 +182,7 @@ void meow_ui_media_meta(uint32_t state, uint32_t pos_ms, uint32_t dur_ms,
|
|||||||
memset(&mm, 0, sizeof mm);
|
memset(&mm, 0, sizeof mm);
|
||||||
pthread_mutex_lock(&g_mu);
|
pthread_mutex_lock(&g_mu);
|
||||||
media_app_id();
|
media_app_id();
|
||||||
|
g_last_state = (int)state; /* 记录最近上报 state:供物理播放/暂停键切换判据(见 sys_key) */
|
||||||
snprintf(mm.app_id, sizeof mm.app_id, "%s", g_app);
|
snprintf(mm.app_id, sizeof mm.app_id, "%s", g_app);
|
||||||
mm.state = state;
|
mm.state = state;
|
||||||
mm.pos_ms = pos_ms;
|
mm.pos_ms = pos_ms;
|
||||||
@@ -207,6 +213,64 @@ void meow_ui_media_stop(void)
|
|||||||
pthread_mutex_unlock(&g_mu);
|
pthread_mutex_unlock(&g_mu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ---- 物理媒体键 → 系统语义(M2.xx:SDK 输入层接管,App 前台也生效)----
|
||||||
|
* 物理键是系统资源(APPS.md §3.4.1):播放/暂停/上一曲/下一曲 → 转发到当前媒体会话。
|
||||||
|
* 本进程注册过 playctl 回调(= 本 App 是媒体源)→ 直接本地回调(最简,无 IPC 往返);
|
||||||
|
* 否则经 IPC 发 MSG_PLAYCTL 给 launcher 转发到「最近活跃媒体会话」。best-effort 绝不崩。 */
|
||||||
|
|
||||||
|
/* 物理媒体键 keycode → MSG_PLAYCTL cmd(1=播放 2=暂停 3=上一首 4=下一首 5=seek)。
|
||||||
|
* 播放/暂停(164)按最近上报 state 切换(播放中→暂停,否则→播放);上一曲 165/19→3,下一曲 163→4。
|
||||||
|
* 非媒体键返回 -1。last_state 由调用方在持锁下传入(读 g_last_state 快照)。 */
|
||||||
|
static int media_key_cmd(int code, int last_state)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case 164: /* KEY_PLAYPAUSE:切换(依最近上报 state) */
|
||||||
|
return (last_state == MEOW_UI_MEDIA_STATE_PLAYING) ? 2 : 1;
|
||||||
|
case 165: case 19: /* KEY_PREVIOUSSONG / 旧码 */
|
||||||
|
return 3;
|
||||||
|
case 163: /* KEY_NEXTSONG */
|
||||||
|
return 4;
|
||||||
|
default:
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 控制器路径:发 MSG_PLAYCTL 给 launcher 转发到最近活跃媒体会话(launcher ipc_dispatch
|
||||||
|
* MSG_PLAYCTL → meow_media_playctl → 下发到当前源 fd)。
|
||||||
|
* 专门开一条临时连接(不复用媒体源持久连接 g_fd,避免与后台线程 poll/读竞争);
|
||||||
|
* 无 launcher/断线 → 静默降级(绝不崩)。 */
|
||||||
|
static void send_playctl_upstream(int cmd, int64_t seek_ms)
|
||||||
|
{
|
||||||
|
int fd = connect_path(sock_path());
|
||||||
|
if (fd < 0) return;
|
||||||
|
m_playctl_t c;
|
||||||
|
memset(&c, 0, sizeof c);
|
||||||
|
c.cmd = (uint32_t)cmd;
|
||||||
|
c.seek_ms = seek_ms;
|
||||||
|
if (send_frame(fd, MSG_PLAYCTL, 1, &c, sizeof c) != 0) { close(fd); return; }
|
||||||
|
/* 读 ACK(best-effort):带 200ms poll,防 launcher 挂死导致输入线程阻塞。 */
|
||||||
|
struct pollfd p;
|
||||||
|
p.fd = fd; p.events = POLLIN; p.revents = 0;
|
||||||
|
if (poll(&p, 1, 200) > 0 && (p.revents & POLLIN)) {
|
||||||
|
uint8_t rbuf[128];
|
||||||
|
uint16_t rt;
|
||||||
|
(void)read_frame(fd, &rt, rbuf, sizeof rbuf);
|
||||||
|
}
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 物理媒体键按下边沿(val==1 时由输入线程调用;重复/抬起不重复触发)。 */
|
||||||
|
void meow_ui_media_sys_key(int code)
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&g_mu);
|
||||||
|
meow_media_playctl_fn cb = g_cb;
|
||||||
|
int cmd = media_key_cmd(code, g_last_state);
|
||||||
|
pthread_mutex_unlock(&g_mu);
|
||||||
|
if (cmd <= 0) return;
|
||||||
|
if (cb) { cb(cmd, 0); return; } /* 本 App 是媒体源(注册过回调)→ 本地回调 */
|
||||||
|
send_playctl_upstream(cmd, 0); /* 否则经 IPC 发 launcher 转发到最近活跃会话 */
|
||||||
|
}
|
||||||
|
|
||||||
/* 后台线程主体:懒连 + poll 读 launcher 下发 MSG_PLAYCTL → 调 cb。 */
|
/* 后台线程主体:懒连 + poll 读 launcher 下发 MSG_PLAYCTL → 调 cb。 */
|
||||||
static void *media_thread_main(void *arg)
|
static void *media_thread_main(void *arg)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -39,6 +39,13 @@ void meow_ui_media_meta(uint32_t state, uint32_t pos_ms, uint32_t dur_ms,
|
|||||||
/* 停止后台接收线程并断开连接(App 退出时调;幂等)。 */
|
/* 停止后台接收线程并断开连接(App 退出时调;幂等)。 */
|
||||||
void meow_ui_media_stop(void);
|
void meow_ui_media_stop(void);
|
||||||
|
|
||||||
|
/* 物理媒体键 → 系统语义(SDK 输入层调用;App 前台也生效)。
|
||||||
|
* code:164=播放/暂停、165/19=上一曲、163=下一曲。
|
||||||
|
* 本 App 注册过 playctl 回调(媒体源)→ 本地直接回调 cb(cmd,seek_ms);
|
||||||
|
* 否则经 IPC 发 MSG_PLAYCTL 给 launcher 转发到最近活跃媒体会话。非媒体键/无源静默。
|
||||||
|
* best-effort:无 launcher/socket → 优雅降级,绝不崩。 */
|
||||||
|
void meow_ui_media_sys_key(int code);
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -50,3 +50,72 @@ int meow_ui_vol_read(const char *path, const char *key, int def)
|
|||||||
while (*found == ' ' || *found == '\t') found++; /* value 左去空白;atoi 天然停于非数字 */
|
while (*found == ' ' || *found == '\t') found++; /* value 左去空白;atoi 天然停于非数字 */
|
||||||
return atoi(found);
|
return atoi(found);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 改 settings.conf 一个 int 键:流式拷贝原文件到 <path>.tmp,命中键行改写为
|
||||||
|
* "key = value"(launcher 落盘格式,带空格),键缺失则末尾追加;rename 原子替换。
|
||||||
|
* 保留其余行(注释/其它设置)——绝不 O_TRUNC 整文件。 */
|
||||||
|
int meow_ui_vol_set(const char *path, const char *key, int val)
|
||||||
|
{
|
||||||
|
if (!path || !path[0] || !key || !key[0]) return -1;
|
||||||
|
FILE *in = fopen(path, "r");
|
||||||
|
char tmp[512];
|
||||||
|
int kl = (int)strlen(key);
|
||||||
|
snprintf(tmp, sizeof tmp, "%s.tmp", path);
|
||||||
|
FILE *out = fopen(tmp, "w");
|
||||||
|
if (!out) { if (in) fclose(in); return -1; }
|
||||||
|
|
||||||
|
int found = 0;
|
||||||
|
char line[256];
|
||||||
|
while (in && fgets(line, sizeof line, in)) {
|
||||||
|
char *eq = strchr(line, '=');
|
||||||
|
char *k = line;
|
||||||
|
while (*k == ' ' || *k == '\t') k++;
|
||||||
|
int n = eq ? (int)(eq - k) : 0;
|
||||||
|
while (n > 0 && (k[n-1] == ' ' || k[n-1] == '\t')) n--;
|
||||||
|
if (eq && n == kl && strncmp(k, key, (size_t)kl) == 0) {
|
||||||
|
fprintf(out, "%s = %d\n", key, val); /* 命中:改写为规范格式 */
|
||||||
|
found = 1;
|
||||||
|
} else {
|
||||||
|
fputs(line, out); /* 其它行原样保留 */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (in) fclose(in);
|
||||||
|
if (!found) fprintf(out, "%s = %d\n", key, val);
|
||||||
|
fclose(out);
|
||||||
|
|
||||||
|
if (rename(tmp, path) != 0) { remove(tmp); return -1; }
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 音量步进(系统语义,与 launcher 设置页/watchdog 同源:volume_step/volume_max 约束)。
|
||||||
|
* 只做 settings 逻辑 + 持久化;DAC 写入由调用方(meow_ui_vol_dac_write)负责。 */
|
||||||
|
int meow_ui_vol_step(const char *path, int dir)
|
||||||
|
{
|
||||||
|
int vol = meow_ui_vol_read(path, "audio.volume", MEOW_UI_VOL_DEFAULT);
|
||||||
|
int vol_max= meow_ui_vol_read(path, "audio.volume_max", MEOW_UI_VOL_MAX_DEF);
|
||||||
|
int step = meow_ui_vol_read(path, "audio.volume_step", 1);
|
||||||
|
if (step < 1) step = 1;
|
||||||
|
if (step > 5) step = 5; /* 与 watchdog 读取口径一致(1..5) */
|
||||||
|
if (dir < 0) dir = -1;
|
||||||
|
else if (dir > 0) dir = 1;
|
||||||
|
else return vol;
|
||||||
|
|
||||||
|
int v = meow_ui_vol_clamp(vol + dir * step, vol_max);
|
||||||
|
if (v == vol) return vol; /* 已在上下限:无进展,不写 */
|
||||||
|
meow_ui_vol_set(path, "audio.volume", v);
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 真实 DAC 音量(CS43198, numid 1/2, 0-255),pct→255 映射直写两声道。
|
||||||
|
* 与 launcher meow_dac_vol_write / watchdog wd_vol_write 完全同路径(语义同源)。 */
|
||||||
|
void meow_ui_vol_dac_write(int pct)
|
||||||
|
{
|
||||||
|
int dac = pct * 255 / 100;
|
||||||
|
if (dac < 0) dac = 0;
|
||||||
|
if (dac > 255) dac = 255;
|
||||||
|
char cmd[160];
|
||||||
|
snprintf(cmd, sizeof cmd,
|
||||||
|
"amixer -c 0 cset numid=1 %d >/dev/null 2>&1; amixer -c 0 cset numid=2 %d >/dev/null 2>&1",
|
||||||
|
dac, dac);
|
||||||
|
system(cmd);
|
||||||
|
}
|
||||||
@@ -29,4 +29,22 @@ int meow_ui_vol_clamp(int vol, int vol_max);
|
|||||||
* path 由调用方传入(设备传导 MEOW_SETTINGS_FILE;宿主单测指向临时文件)。 */
|
* path 由调用方传入(设备传导 MEOW_SETTINGS_FILE;宿主单测指向临时文件)。 */
|
||||||
int meow_ui_vol_read(const char *path, const char *key, int def);
|
int meow_ui_vol_read(const char *path, const char *key, int def);
|
||||||
|
|
||||||
|
/* 改 settings.conf 里一个 int 键(地址更新/新增;**保留其余行**——与 launcher 落盘格式
|
||||||
|
* "key = value"(带空格)一致;键缺失则追加)。用临时文件 + rename 原子替换,失败返回 -1。
|
||||||
|
* 用途:SDK 输入层调系统音量时写 audio.volume(绝不像 watchdog wd_vol_save 那样整文件
|
||||||
|
* O_TRUNC,避免删掉 display.* 等其它设置)。 */
|
||||||
|
int meow_ui_vol_set(const char *path, const char *key, int val);
|
||||||
|
|
||||||
|
/* 读 audio.volume + audio.volume_max + audio.volume_step,按 dir(±1)迈一步(绝对跨度
|
||||||
|
* = volume_step),clamp 到 [0, MEOW_UI_VOL_MAX] 再 clamp 到 volume_max(同 meow_ui_vol_clamp
|
||||||
|
* 语义,与 launcher 设置页/watchdog 一致),经 meow_ui_vol_set 持久化到 audio.volume。
|
||||||
|
* 返回写入 DAC 的映射值(= 夹后新卷值);已在上下限未变则返回当前值(不写)。
|
||||||
|
* 仅做 settings 逻辑(不改 DAC);DAC 写入由调用方用 meow_ui_vol_dac_write 做。 */
|
||||||
|
int meow_ui_vol_step(const char *path, int dir);
|
||||||
|
|
||||||
|
/* 写真实 DAC(CS43198, numid 1/2, pct→0-255),与 launcher meow_dac_vol_write / watchdog
|
||||||
|
* wd_vol_write 同路径(amixer cset = SNDRV_CTL_IOCTL_ELEM_WRITE)。无副作用前提:宿主上
|
||||||
|
* amixer 不存在 → system() 静默失败(拼音量的宿主单测只测 meow_ui_vol_step,不测写 DAC)。 */
|
||||||
|
void meow_ui_vol_dac_write(int pct);
|
||||||
|
|
||||||
#endif /* MEOW_UI_VOL_H */
|
#endif /* MEOW_UI_VOL_H */
|
||||||
Executable
BIN
Binary file not shown.
@@ -0,0 +1,123 @@
|
|||||||
|
/*
|
||||||
|
* test_sdk_input_keys.c — M2.55 宿主验证:SDK 输入层接管系统按键语义(App 前台也生效)。
|
||||||
|
*
|
||||||
|
* 覆盖(直接调输入层语义函数,等效"模拟按键事件"):
|
||||||
|
* K1 音量 +/step:audio.volume 迈一步(volume_step 约束),settings.conf 落到值改变;
|
||||||
|
* K2 音量 ± clamp:volume_max 软上限、0 下限、硬上限 100;到限不再写;
|
||||||
|
* K3 保留其它键:改 audio.volume 不整文件 O_TRUNC(display.brightness/注释保留);
|
||||||
|
* K4 媒体键本地回调:注册 playctl 回调后,sys_key(上一曲/下一曲/播放暂停) → 回调 cmd 正确
|
||||||
|
* (播放/暂停按最近上报 state 切换:播放中→暂停(2),暂停/停止→播放(1));
|
||||||
|
* K5 媒体键 IPC 兜底:未注册回调(g_cb==NULL)→ 走 IPC 发 launcher(无 socket → 静默不崩)。
|
||||||
|
*
|
||||||
|
* 编译(宿主 gcc,仅链 vol/media 两个 TU;无需 meow_ui 其它子系统):
|
||||||
|
* gcc -Wall -Wextra -pthread -o test_sdk_input_keys test_sdk_input_keys.c \
|
||||||
|
* meow_ui_vol.c meow_ui_media.c -I. -I../meow_launcherd
|
||||||
|
*/
|
||||||
|
#include "meow_ui_vol.h"
|
||||||
|
#include "meow_ui_media.h"
|
||||||
|
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
static int failures = 0;
|
||||||
|
static void check(const char *name, int cond)
|
||||||
|
{
|
||||||
|
printf("%s %s\n", cond ? "PASS" : "FAIL", name);
|
||||||
|
if (!cond) failures++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static char g_path[64];
|
||||||
|
static void wfile(const char *content)
|
||||||
|
{
|
||||||
|
FILE *f = fopen(g_path, "w");
|
||||||
|
if (!f) { perror("fopen"); exit(2); }
|
||||||
|
fputs(content, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
static int vol_in_file(void)
|
||||||
|
{
|
||||||
|
return meow_ui_vol_read(g_path, "audio.volume", -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* —— 媒体回调捕获 —— */
|
||||||
|
static int cb_calls = 0, cb_cmd = 0;
|
||||||
|
static int64_t cb_seek = 0;
|
||||||
|
static void playctl_cb(int cmd, int64_t seek_ms)
|
||||||
|
{ cb_calls++; cb_cmd = cmd; cb_seek = seek_ms; }
|
||||||
|
|
||||||
|
int main(void)
|
||||||
|
{
|
||||||
|
snprintf(g_path, sizeof g_path, "/tmp/meow_kb_%d.conf", (int)getpid());
|
||||||
|
char sock[64]; /* 指向不存在路径:让 IPC 兜底路径确定性静默失败 */
|
||||||
|
snprintf(sock, sizeof sock, "/tmp/meow_kb_%d.sock", (int)getpid());
|
||||||
|
setenv("MEOW_IPC_SOCK", sock, 1);
|
||||||
|
|
||||||
|
/* ---- K1 音量迈一步(volume_step=1 默认)---- */
|
||||||
|
wfile("audio.volume = 60\naudio.volume_max = 100\naudio.volume_step = 1\n");
|
||||||
|
check("K1 vol 60 +1 -> 61", meow_ui_vol_step(g_path, +1) == 61);
|
||||||
|
check("K1 settings.conf 落到 61", vol_in_file() == 61);
|
||||||
|
check("K1 vol 61 -1 -> 60", meow_ui_vol_step(g_path, -1) == 60);
|
||||||
|
check("K1 settings.conf 回到 60", vol_in_file() == 60);
|
||||||
|
|
||||||
|
/* ---- K2 步进 volume_step=4 + clamp ---- */
|
||||||
|
wfile("audio.volume = 60\naudio.volume_max = 100\naudio.volume_step = 4\n");
|
||||||
|
check("K2 step=4: 60 +1 -> 64", meow_ui_vol_step(g_path, +1) == 64);
|
||||||
|
wfile("audio.volume = 99\naudio.volume_max = 100\naudio.volume_step = 4\n");
|
||||||
|
check("K2 step=4: 99 +1 -> 100 (硬上限)", meow_ui_vol_step(g_path, +1) == 100);
|
||||||
|
|
||||||
|
/* volume_max 软上限 */
|
||||||
|
wfile("audio.volume = 60\naudio.volume_max = 55\naudio.volume_step = 1\n");
|
||||||
|
meow_ui_vol_step(g_path, +1);
|
||||||
|
check("K2 volume_max=55: vol -> 55 (clamp 上限)", vol_in_file() == 55);
|
||||||
|
|
||||||
|
/* 到上限不再写 */
|
||||||
|
wfile("audio.volume = 100\naudio.volume_max = 100\naudio.volume_step = 1\n");
|
||||||
|
check("K2 at hard max: +1 -> 100 (no write)", meow_ui_vol_step(g_path, +1) == 100);
|
||||||
|
wfile("audio.volume = 0\naudio.volume_max = 100\naudio.volume_step = 1\n");
|
||||||
|
check("K2 at min: -1 -> 0 (no write)", meow_ui_vol_step(g_path, -1) == 0);
|
||||||
|
check("K2 at min settings 保持 0", vol_in_file() == 0);
|
||||||
|
|
||||||
|
/* ---- K3 保留其它键(不整文件 O_TRUNC)---- */
|
||||||
|
wfile("# meow-settings 1\ndisplay.brightness = 100\naudio.volume = 60\n");
|
||||||
|
meow_ui_vol_step(g_path, +1);
|
||||||
|
check("K3 vol 60 -> 61", vol_in_file() == 61);
|
||||||
|
check("K3 保留其它键 display.brightness=100",
|
||||||
|
meow_ui_vol_read(g_path, "display.brightness", -1) == 100);
|
||||||
|
|
||||||
|
/* ---- K5 媒体键 IPC 兜底:未注册回调 → 走 IPC(无 socket 静默不崩)---- */
|
||||||
|
cb_calls = 0;
|
||||||
|
meow_ui_media_sys_key(165); /* 上一曲:g_cb==NULL 走 IPC 兜底,不崩 */
|
||||||
|
check("K5 未注册回调不崩", cb_calls == 0);
|
||||||
|
|
||||||
|
/* ---- K4 媒体键本地回调:注册后按 cmd 分发 ---- */
|
||||||
|
meow_ui_media_on_playctl(playctl_cb);
|
||||||
|
meow_ui_media_sys_key(165); /* 上一曲 */
|
||||||
|
check("K4 上一曲(165) -> cb(3)", cb_calls == 1 && cb_cmd == 3 && cb_seek == 0);
|
||||||
|
meow_ui_media_sys_key(163); /* 下一曲 */
|
||||||
|
check("K4 下一曲(163) -> cb(4)", cb_calls == 2 && cb_cmd == 4);
|
||||||
|
meow_ui_media_sys_key(19); /* 旧码上一曲 */
|
||||||
|
check("K4 旧码(19) -> cb(3)", cb_calls == 3 && cb_cmd == 3);
|
||||||
|
|
||||||
|
/* 播放/暂停按最近上报 state 切换 */
|
||||||
|
meow_ui_media_meta(1, 0, 0, "t", "a", "al", NULL, NULL); /* state=PLAYING */
|
||||||
|
meow_ui_media_sys_key(164);
|
||||||
|
check("K4 播放中按播放/暂停 -> cb(2) 暂停", cb_calls == 4 && cb_cmd == 2);
|
||||||
|
meow_ui_media_meta(2, 0, 0, "t", "a", "al", NULL, NULL); /* state=PAUSED */
|
||||||
|
meow_ui_media_sys_key(164);
|
||||||
|
check("K4 暂停中按播放/暂停 -> cb(1) 播放", cb_calls == 5 && cb_cmd == 1);
|
||||||
|
meow_ui_media_meta(0, 0, 0, "t", "a", "al", NULL, NULL); /* state=STOPPED */
|
||||||
|
meow_ui_media_sys_key(164);
|
||||||
|
check("K4 停止中按播放/暂停 -> cb(1) 播放", cb_calls == 6 && cb_cmd == 1);
|
||||||
|
|
||||||
|
meow_ui_media_stop();
|
||||||
|
|
||||||
|
unlink(g_path);
|
||||||
|
unlink(sock);
|
||||||
|
|
||||||
|
printf("\n%s (%d failure%s)\n", failures ? "RESULT: FAIL" : "RESULT: PASS",
|
||||||
|
failures, failures == 1 ? "" : "s");
|
||||||
|
return failures ? 1 : 0;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user