Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/ui/settings.c
T
MeowOS f23720f934 feat(ncm): 三期第二批 — 中文搜索接拼音IME+设置页做好(音质全档/缓存上限可设数字+清理缓存/关于)+缓存LRU淘汰接生产路径(写前写后+手动调低上限)
- 搜索页输入框 MEOW_IME_ASCII→MEOW_IME_PINYIN: 候选上屏UTF-8经 on_change 实时回写 g_st.search_query, 键盘「拼/英」「中」切换, 英文零回归; 搜索仍仅在点「搜索」时发 cloudsearch(风控)
- 设置页 5 行: 音质(Hi-Res 默认,Lossless/Exhigh/Higher/Standard 全档,core 既有降级链)/缓存上限(1~100GB 预设+「自定义」NUM IME 数字输入,默认10上限100)/清理缓存(显示当前占用,ncm_cache_clear)/退出登录/关于行
- 缓存淘汰接线: ncm_cache_evict 进生产路径 — player_manager(下载写前/写后)+cover(写前/写后)+appstate_save_settings(手动调低上限立即淘汰); 封面与 mp3 同池; 上限读设置值
- core 新增 ncm_cache_clear; 设置持久化走 meow_config_* → MeowData/dev.player.ncm/data
- bench/cache_evict_sim.c 离线回归(0网络) 6 组 PASS; pagination_sim/core(host+mipsel/qemu)/6页宿主渲染 全过; mipsel -Wall -Wextra 0 warning
(未动 sdk/、meow_launcherd/、vendor/;0 真实 NCM 请求;真机部署留用户)
2026-08-31 20:15:31 +08:00

286 lines
11 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.
/* ui/settings.c — 设置页(ACT_SETTINGS)。
*
* 行项:音质(Hi-res 默认,单曲不可用时 core ncm_api_song_url_auto 逐档降级)+ 缓存上限
* (默认 10GB,上限 100GB,支持预设与「自定义」数字输入)+ 清理缓存(显示当前占用)+
* 退出登录 + 关于/版本。设置持久化到 MeowData/dev.player.ncm/data(meow_config_*,与 core
* ncm_store_read_cfg_int 同目录同键,ncm.cache_limit_gb/ncm.level)。超上限触发 ncm_cache LRU
* 淘汰(写缓存前后 + 手动调低上限时,见 player_manager.c/cover.c/appstate.c)。
* 空态/绘制用 custom 绘制物 + 手动行命中(仅 5 行,无需列表滚动)。
*/
#include "../ncm_app_internal.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct { meow_view *view; int active; } settings_ctrl;
static settings_ctrl g_sc;
static meow_widget *g_tb;
static ncm_rect_t r_cont;
static int g_custom_id;
static int g_row_h; /* 行高 */
static uint64_t g_cache_used; /* 缓存占用字节(进设置页时算一次,清理后清零) */
#define SETTINGS_ROWS 5
static const char *level_name(int lv)
{
switch (lv) {
case NCM_LEVEL_HIRES: return "Hi-Res";
case NCM_LEVEL_LOSSLESS:return "Lossless";
case NCM_LEVEL_EXHIGH: return "Exhigh";
case NCM_LEVEL_HIGHER: return "Higher";
default: return "Standard";
}
}
/* 缓存占用字节 → 人类可读(整数运算,不用浮点/软浮点)。buf ≥ 24。 */
static void fmt_bytes(uint64_t b, char *buf, size_t sz)
{
const uint64_t GB = 1024ULL * 1024 * 1024;
const uint64_t MB = 1024ULL * 1024;
if (b >= GB) {
uint64_t g = b / GB, fr = (b % GB) / (GB / 10);
snprintf(buf, sz, "%llu.%llu GB", (unsigned long long)g, (unsigned long long)fr);
} else if (b >= MB) {
uint64_t m = b / MB, fr = (b % MB) / (MB / 10);
snprintf(buf, sz, "%llu.%llu MB", (unsigned long long)m, (unsigned long long)fr);
} else if (b >= 1024) {
snprintf(buf, sz, "%llu KB", (unsigned long long)(b / 1024));
} else {
snprintf(buf, sz, "%llu B", (unsigned long long)b);
}
}
/* 刷新缓存占用缓存(进设置页/清理后调用)。 */
static void refresh_cache_used(void)
{
g_cache_used = ncm_cache_total_bytes(&g_st.ctx.cache);
}
static void settings_draw(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_SETTINGS) return;
const meow_theme *th = meow_theme_get();
const ncm_pal_t *pa = ncm_palette(g_dark);
g_row_h = 60;
int y = r_cont.y + 16;
int w = r_cont.w - 2 * APP_PAD;
int x = APP_PAD;
char usetxt[24];
fmt_bytes(g_cache_used, usetxt, sizeof usetxt);
/* 行:音质 / 缓存上限 / 清理缓存 / 退出登录 / 关于。 */
struct { const char *title; char sub[160]; int red; } rows[SETTINGS_ROWS];
rows[0].title = "音质"; rows[0].red = 0;
rows[1].title = "缓存上限"; rows[1].red = 0;
rows[2].title = "清理缓存"; rows[2].red = 0;
rows[3].title = "退出登录"; rows[3].red = 1;
rows[4].title = "关于网易云"; rows[4].red = 0;
snprintf(rows[0].sub, sizeof rows[0].sub, "当前: %s", level_name(g_st.level));
snprintf(rows[1].sub, sizeof rows[1].sub, "当前: %d GB · 上限 100", g_st.cache_limit_gb);
snprintf(rows[2].sub, sizeof rows[2].sub, "占用: %s", usetxt);
snprintf(rows[3].sub, sizeof rows[3].sub, "%s", g_st.nickname[0] ? g_st.nickname : "退出");
snprintf(rows[4].sub, sizeof rows[4].sub, "%s", "v0.1 · MeowOS");
for (int i = 0; i < SETTINGS_ROWS; i++) {
int yy = y + i * (g_row_h + 8);
ncm_fill_round(x, yy, w, g_row_h, 12, th->surfaceContainer);
uint32_t tcol = rows[i].red ? th->error : th->onSurface;
uint32_t scol = rows[i].red ? th->error : th->onSurfaceVariant;
meow_ui_text(x + APP_PAD, yy + 12, rows[i].title, 20, tcol);
meow_ui_text(x + APP_PAD, yy + 34, rows[i].sub, 20, scol);
meow_ui_text_right(x + w - APP_PAD, yy + 18, ">", 28, rows[i].red ? th->error : th->onSurfaceVariant);
}
/* 品牌红页脚小字。 */
int ny = y + SETTINGS_ROWS * (g_row_h + 8) + 4;
meow_ui_text(x, ny, "解锁完整音质请登录网易云黑胶会员", 20, th->onSurfaceVariant);
meow_ui_text(x, ny + 24, "品牌红 © MeowOS · NCM", 20, pa->red);
}
static int row_at(int x, int y)
{
(void)x;
int r = r_cont.y + 16;
for (int i = 0; i < SETTINGS_ROWS; i++) {
int yy = r + i * (g_row_h + 8);
if (y >= yy && y <= yy + g_row_h) return i;
}
return -1;
}
/* 「自定义」缓存上限:INPUT 弹窗 + NUM IME(数字键盘输入 1~100,越界钳制)。
* 回车/ESC 关闭后 d->ime.buf 已同步(dialog INPUT 关闭时回写 ime_orig),读回即可。 */
static int input_cache_limit_gb(void)
{
char buf[8];
snprintf(buf, sizeof buf, "%d", g_st.cache_limit_gb);
meow_ime_ctx ime;
memset(&ime, 0, sizeof ime);
ime.mode = MEOW_IME_NUM;
ime.maxlen = sizeof buf;
ncm_strlcpy(ime.buf, buf, sizeof ime.buf);
meow_dialog *d = meow_dialog_create(MEOW_DLG_INPUT, "缓存上限 (GB, 1~100)");
meow_dialog_set_ime(d, &ime);
meow_dialog_show(d);
meow_dialog_free(d);
int gb = atoi(ime.buf);
if (gb < 1) gb = 1;
if (gb > 100) gb = 100;
return gb;
}
int settings_is_active(void) { return g_sc.active; }
int settings_on_tap(int x, int y)
{
if (g_tb) {
int ah = meow_topappbar_hit(g_tb, x, y);
if (ah == MEOW_TOPBAR_BACK) { meow_view_page_pop(); return 1; }
if (ah >= 0) { return 1; }
}
if (x < APP_PAD || x > r_cont.w - APP_PAD) return 0;
int row = row_at(x, y);
if (row < 0) return 0;
if (row == 0) { /* 音质:Top 档选择,单曲不可用时 core 逐档降级 */
static const char *lbls[5] = { "Hi-Res", "Lossless", "Exhigh", "Higher", "Standard" };
static const int lvls[5] = { NCM_LEVEL_HIRES, NCM_LEVEL_LOSSLESS,
NCM_LEVEL_EXHIGH, NCM_LEVEL_HIGHER, NCM_LEVEL_STANDARD };
meow_sheet *s = meow_sheet_create("Music Quality", 0);
for (int i = 0; i < 5; i++) meow_sheet_add_item(s, lbls[i]);
int op = meow_sheet_open(s);
meow_sheet_free(s);
if (op >= 0 && op < 5) {
g_st.level = lvls[op];
player_manager_set_quality(g_st.level, g_st.cache_limit_gb);
appstate_save_settings();
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
}
return 1;
}
if (row == 1) { /* 缓存上限:预设 + 自定义(数字输入 1~100) */
static const int gbs[] = { 1, 2, 5, 10, 20, 30, 50, 100 };
static const char *labels[] = { "1 GB", "2 GB", "5 GB", "10 GB",
"20 GB", "30 GB", "50 GB", "100 GB" };
meow_sheet *s = meow_sheet_create("Cache Limit", 0);
for (int i = 0; i < 8; i++) meow_sheet_add_item(s, labels[i]);
meow_sheet_add_item(s, "自定义...");
int op = meow_sheet_open(s);
meow_sheet_free(s);
if (op == 8) g_st.cache_limit_gb = input_cache_limit_gb();
else if (op >= 0 && op < 8) g_st.cache_limit_gb = gbs[op];
if (op >= 0) {
player_manager_set_quality(g_st.level, g_st.cache_limit_gb);
appstate_save_settings(); /* 内含手动调低上限后的 LRU 淘汰 */
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
}
return 1;
}
if (row == 2) { /* 清理缓存 */
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Clear Cache?");
char msg[160];
char usetxt[24];
fmt_bytes(g_cache_used, usetxt, sizeof usetxt);
snprintf(msg, sizeof msg, "将删除全部缓存 (%s)。\n已播放的歌曲需重新下载。", usetxt);
const char *msgs[1];
msgs[0] = msg;
meow_dialog_set_items(d, msgs, 1, -1);
int yes = meow_dialog_confirm(d);
meow_dialog_free(d);
if (yes) {
ncm_cache_clear(&g_st.ctx.cache);
refresh_cache_used();
ncm_snack("缓存已清理");
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
}
return 1;
}
if (row == 3) { /* 退出登录 */
meow_dialog *d = meow_dialog_create(MEOW_DLG_CONFIRM, "Sign out?");
const char *msgs[1];
msgs[0] = "Playback will stop.\nYou'll need to scan a QR code again.";
meow_dialog_set_items(d, msgs, 1, -1);
int yes = meow_dialog_confirm(d);
meow_dialog_free(d);
if (yes) {
appstate_logout();
meow_view_page_replace(ACT_LOGIN);
}
return 1;
}
if (row == 4) { /* 关于 */
ncm_snack("网易云音乐 v0.1 · MeowOS 移植版");
return 1;
}
return 0;
}
void settings_swipe(int dir, int dist) { (void)dir; (void)dist; }
void settings_touch_down(int x, int y) { (void)x; (void)y; }
void settings_touch_move(int x, int y) { (void)x; (void)y; }
void settings_open(void)
{
g_st.playing_song = -1;
meow_view_page_push(ACT_SETTINGS);
}
void settings_reload(void)
{
refresh_cache_used();
const meow_theme *th = meow_theme_get();
meow_ui_set_page_bg(th->bg);
meow_ui_paint_full();
}
static void st_page_create(meow_view *v, void *ctx)
{
(void)v;
((settings_ctrl *)ctx)->view = v;
r_cont = ncm_content_rect();
meow_ui_activity_push(ACT_SETTINGS); /* owner=ACT_SETTINGS */
g_tb = ncm_topbar_create("Settings", 1, NULL);
meow_ui_activity_pop();
g_row_h = 60;
g_custom_id = meow_draw_custom_register(1, r_cont.x, r_cont.y, r_cont.w, r_cont.h,
settings_draw, NULL);
}
static void st_page_enter(meow_view *v, void *ctx)
{
(void)v;
((settings_ctrl *)ctx)->active = 1;
settings_reload();
}
static void st_page_leave(meow_view *v, void *ctx)
{
(void)v;
((settings_ctrl *)ctx)->active = 0;
}
static void st_page_destroy(meow_view *v, void *ctx)
{
(void)v; (void)ctx;
if (g_custom_id) { meow_draw_unregister(g_custom_id); g_custom_id = 0; }
if (g_tb) { meow_widget_free(g_tb); g_tb = NULL; }
}
meow_view *settings_view_create(void)
{
static const meow_view_hooks h = {
.on_create = st_page_create,
.on_enter = st_page_enter,
.on_leave = st_page_leave,
.on_destroy = st_page_destroy,
};
return meow_view_create(ACT_SETTINGS, MEOW_VIEW_ACTIVITY, &h, &g_sc);
}