Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/ui/login.c
T
AskaEth 68ce46a5d8 fix(ncm §32): 内存瘦身修 OOM 被杀 —— 封面降采样(8.28MB→0.78MB) + 瞬态 rbuf 缩减 + 线程栈显式
真机 dmesg 实锤:OOM Kill ncm (score 460, anon-rss 26.9MB, 设备 56MB)。
画像:封面全分辨率 RGBA 8.28MB(1448x1448 原生解码驻留) 是最大可控块。

瘦身(按收益排序):
1. sdk meow_ui_img 新增纯增量 meow_img_downscale + cover.c cover_cap(≤480px)
   → 封面 8.28MB→0.78MB,-7.65MB(mem_sim 实测 PASS)。
2. ncm_api.c 瞬态响应 rbuf:song_url→64KB、lyric/song_detail/cloudsearch/user_playlist→256KB
   (detail/track_all 保 1MB)。
3. ncm_app_internal.h 新增 ncm_thread_create(显式 128KB 栈),替换全部 14 处 pthread_create。
4. 滑动窗口/meta 复核零回退(window_sim:608B/首,6000 首 280KB 恒定)。

验证:mem_sim 封面收益 PASS + host home/player 渲染 OK + stream_sim/player_flow_sim/
window_sim/search_sim/cache_evict_sim 全 PASS + test_ncm_core host+mipsel(qemu) PASS
+ build_ncm.sh -Wall -Wextra 0 warning。真机 scp 播放在跑确认(缓存曲,0 播放请求)。

新 ncm md5 44fa16fe(text 1015150/bss 1362500)。正式部署留主会话。
2026-09-02 03:43:42 +08:00

254 lines
9.5 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/login.c — 扫码登录页(ACT_LOGIN,根页)。
*
* 流程:进入即起后台 worker(login_worker):
* unikey(eapi) → 本地二维码矩阵(qrcodegen) → 轮询(801/802/803) → 803 捕获 set-cookie 加密落盘
* → login_status 拿 uid/nickname → 置 g_login_done。
* UI 线程只在 tick 读 g_st.qr 状态与 login_state 重绘并检查 g_login_done → 切入 home。
* 800(过期)自动刷新二维码。UI 不卡:网络全在 worker 线程。
*/
#include "../ncm_app_internal.h"
#include <stdio.h>
#include <string.h>
#include <unistd.h>
static pthread_t g_thread;
static volatile int g_polling; /* worker 运行中 */
static volatile int g_done; /* 登录成功(UI tick 切入 home) */
static volatile int g_err; /* 最后一次错误(网络不可达等) */
static int g_active;
static int g_custom_id; /* 绘制物 */
static ncm_rect_t r_scr; /* 整屏(沉浸式,hero 从 y=0 起) */
/* ---- 绘制 ---- */
static void login_draw(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_LOGIN) return;
const meow_theme *th = meow_theme_get();
const ncm_pal_t *pa = ncm_palette(g_dark);
int W = meow_ui_width(), H = meow_ui_height();
/* 品牌红 hero 带(沉浸,从 y=0 到 264;day=red / night=hero 深红压场)。 */
meow_ui_fill_rect(0, 0, W, 264, pa->hero);
/* 标题「网易云音乐」+ 红点 logo(用字体大小近似排版,host 字库用 ASCII 替文本)。 */
const char *title = "网易云音乐";
int tw = meow_ui_text_width(title, 34);
meow_ui_text_center(W/2, 112, title, 34, pa->on_red);
/* 24px 红点 logo(标题左前) */
ncm_fill_round(W/2 - tw/2 - 36, 112 + 4, 24, 24, 12, pa->red_lit);
meow_ui_text_center(W/2, 160, "扫码登录 / 音樂的力量", 22, pa->on_red_dim);
/* 弱分隔(hero 与内容衔接) */
meow_ui_fill_rect(0, 264, W, 1, th->outline);
/* QR 卡(128,228,224,224):day 纯白 / night surfaceContainer;圆角 16 + 内描边。 */
int qc = 224, qx = (W - qc) / 2, qy = 228;
ncm_fill_round(qx, qy, qc, qc, 16, th->surfaceContainer);
meow_ui_draw_rect_round(qx, qy, qc, qc, 16, th->outlineVariant);
int sz = g_st.qr_size;
if (sz > 0) {
/* 模块尺寸随内容缩放(二维码版本随内容长度变化,尺寸不固定——
* 按容器内边距算模块边长,绝不溢出卡片) */
int m = (qc - 32) / sz;
if (m < 1) m = 1;
int qs = m * sz;
int ox = qx + (qc - qs) / 2, oy = qy + (qc - qs) / 2;
/* 夜间:深灰底 + 浅灰码;白天:白底黑码 */
uint32_t qr_bg = g_dark ? 0x2A2B30 : 0xFFFFFF;
uint32_t qr_fg = g_dark ? 0xC9CAD1 : 0x000000;
meow_ui_fill_rect(ox, oy, qs, qs, qr_bg);
for (int y = 0; y < sz; y++)
for (int x = 0; x < sz; x++)
if (g_st.qr_mods[y*sz + x])
meow_ui_fill_rect(ox + x*m, oy + y*m, m, m, qr_fg);
} else {
meow_ui_text_center(W/2, qy + qc/2 - 10,
g_err ? "网络异常,点按重试" : "正在获取二维码...", 20, th->dim);
}
/* 状态文字:成功/等待用红(day red / night red_lit),失败红字提示。 */
const char *st = login_status_str(g_st.login_state);
if (g_st.login_state == NCM_LOGIN_OK && g_done) st = "登录成功";
uint32_t scol = (g_st.login_state == NCM_LOGIN_OK) ? pa->red : th->dim;
meow_ui_text_center(W/2, 476, st, 20, scol);
/* 底部提示 */
meow_ui_text_center(W/2, H - 56, "由 MeowOS 驱动 / 仅本地解码", 18, th->dim);
}
/* 重绘 login 内容(QR+状态变化时)。 */
static void login_repaint(void)
{
meow_ui_invalidate(r_scr.x, r_scr.y, r_scr.w, r_scr.h);
}
const char *login_status_str(int state)
{
switch (state) {
case NCM_LOGIN_PENDING: return "等待扫码...";
case NCM_LOGIN_SCANNED: return "已扫码,请在手机上确认";
case NCM_LOGIN_OK: return "登录成功";
case NCM_LOGIN_EXPIRED: return "二维码已过期,正在刷新";
default: return "出错了,点按重试";
}
}
static int get_unikey_and_qr(char *unikey, size_t usz)
{
int rc = ncm_login_unikey(g_st.ctx.device_id, unikey, usz);
if (rc != NCM_OK) { meow_log(MEOW_LOG_ERROR, "login: unikey fetch failed rc=%d", rc); return rc; }
meow_log(MEOW_LOG_INFO, "login: unikey ok, qr ready");
ncm_strlcpy(g_st.unikey, unikey, sizeof g_st.unikey);
ncm_login_qr_url(unikey, g_st.qr_url, sizeof g_st.qr_url);
int size = 0;
if (ncm_login_qr_matrix(g_st.qr_url, g_st.qr_mods, sizeof g_st.qr_mods / 177, &size) != NCM_OK)
g_st.qr_size = 0;
else
g_st.qr_size = size;
g_st.login_state = NCM_LOGIN_PENDING;
g_err = 0;
return NCM_OK;
}
static void *login_worker(void *arg)
{
(void)arg;
char unikey[64];
/* 自动重试:unikey 失败(网络未就绪/抖动)时 5s 退避重试,不再要求用户点重试 */
int retry = 0;
while (get_unikey_and_qr(unikey, sizeof unikey) != NCM_OK) {
g_err = 1;
if (g_app_quit) return NULL;
meow_log(MEOW_LOG_WARN, "login: unikey retry in 5s (attempt %d)", ++retry);
usleep(5 * 1000 * 1000);
}
meow_log(MEOW_LOG_INFO, "login: poll worker start");
char cookie[NCM_COOKIE_MAX];
ncm_strlcpy(cookie, g_st.ctx.cookie, sizeof cookie);
while (!g_app_quit) {
ncm_login_poll_t r;
char out_cookie[NCM_COOKIE_MAX];
int rc = ncm_login_poll(g_st.ctx.device_id, unikey, cookie, &r,
out_cookie, sizeof out_cookie);
if (rc != NCM_OK) { meow_log(MEOW_LOG_WARN, "login: poll failed rc=%d", rc); g_err = 1; usleep(2 * 1000 * 1000); continue; }
if (r.state != g_st.login_state)
meow_log(MEOW_LOG_INFO, "login: state -> %d", (int)r.state);
g_st.login_state = r.state;
if (r.state == NCM_LOGIN_OK) {
meow_log(MEOW_LOG_INFO, "login: 803 confirmed, saving cookie");
ncm_strlcpy(cookie, out_cookie, sizeof cookie);
ncm_ctx_set_cookie(&g_st.ctx, cookie);
/* 803 后同步取 uid/nickname 并落盘(weapi 已修复:不再挂死、cookie 全量且去重发送,
* 服务端认可会话)。此处在 worker 线程执行(不卡 UI);失败不阻断跳页——home 加载
* 时 appstate_ensure_uid 会再验一次(也设了硬超时上限,不阻塞歌单页)。 */
char uid[32] = "", nick[128] = "";
int src = ncm_login_status_check(g_st.ctx.device_id, cookie,
uid, sizeof uid, nick, sizeof nick);
meow_log(MEOW_LOG_INFO, "login: status_check rc=%d uid=%s nick=%s", src, uid, nick);
appstate_set_logged_in(src == NCM_OK ? uid : "", src == NCM_OK ? nick : "");
g_st.logged_in = 1;
account_vip_maybe_refresh(1); /* §20:登录后查一次会员态(本 worker 线程,不卡 UI) */
g_done = 1;
meow_log(MEOW_LOG_INFO, "login: -> home");
break;
}
if (r.state == NCM_LOGIN_EXPIRED) {
/* 刷新二维码 */
usleep(500 * 1000);
get_unikey_and_qr(unikey, sizeof unikey);
continue;
}
usleep(1500 * 1000);
}
return NULL;
}
int login_start(void)
{
if (g_polling) return 0;
g_polling = 1;
g_done = 0;
g_err = 0;
if (ncm_thread_create(&g_thread, login_worker, NULL) != 0) {
g_polling = 0;
return -1;
}
pthread_detach(g_thread);
return 0;
}
int login_is_active(void) { return g_active; }
void login_on_tap(int x, int y)
{
(void)x; (void)y;
if (g_st.login_state == NCM_LOGIN_OK && g_done) return;
if (g_err) { login_start(); login_repaint(); } /* 网络失败重试 */
}
/* UI tick:重绘 QR/状态变化;登录成功 → 切 home。 */
void login_tick(uint64_t now)
{
(void)now;
chrome_tick();
static int last_state = -1, last_qr = 0;
if (g_st.login_state != last_state || g_st.qr_size != last_qr) {
last_state = g_st.login_state;
last_qr = g_st.qr_size;
login_repaint();
}
if (g_done)
meow_view_page_replace(ACT_HOME);
}
/* ---- 生命周期 ---- */
static void login_page_create(meow_view *v, void *ctx)
{
(void)v; (void)ctx;
r_scr = NR(0, 0, meow_ui_width(), meow_ui_height());
/* 沉浸式:登录页整屏自绘(hero 从 y=0 起,覆盖状态栏区)。只画一次并监听绘制物重绘。 */
g_custom_id = meow_draw_custom_register(1, r_scr.x, r_scr.y, r_scr.w, r_scr.h,
login_draw, NULL);
g_active = 0;
}
static void login_page_enter(meow_view *v, void *ctx)
{
(void)v; (void)ctx;
g_active = 1;
const meow_theme *th = meow_theme_get();
meow_ui_set_page_bg(th->bg);
meow_ui_paint_full();
if (!g_host_mock) login_start(); /* 进入即获取二维码 + 轮询 */
}
static void login_page_leave(meow_view *v, void *ctx)
{
(void)v; (void)ctx;
g_active = 0;
}
static void login_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; }
}
meow_view *login_view_create(void)
{
static const meow_view_hooks h = {
.on_create = login_page_create,
.on_enter = login_page_enter,
.on_leave = login_page_leave,
.on_destroy = login_page_destroy,
};
return meow_view_create(ACT_LOGIN, MEOW_VIEW_ACTIVITY, &h, NULL);
}