Files
MeowOS-For-R3P2/patch_v2/ncm_app/app/ui/login.c
T

226 lines
7.7 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_cont;
/* ---- 绘制 ---- */
static void login_draw(void *ctx)
{
(void)ctx;
if (meow_ui_activity_active() != ACT_LOGIN) return;
const meow_theme *th = meow_theme_get();
/* 标题 */
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 24, "NetEase Cloud Music", 28, th->fg);
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 64, "scan to sign in", 20, th->dim);
/* QR 位图 */
int sz = g_st.qr_size;
if (sz > 0) {
int qs = sz * 6; /* 每模块 6px → 41x41=246,33x33=198 */
if (qs > 320) qs = sz * 4;
int ox = r_cont.x + (r_cont.w - qs) / 2;
int oy = r_cont.y + 108;
/* 白色底 + 边框 */
meow_ui_fill_rect(ox - 8, oy - 8, qs + 16, qs + 16, 0xFFFFFF);
meow_ui_draw_rect(ox - 8, oy - 8, qs + 16, qs + 16, th->outline);
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*6, oy + y*6, 6, 6, 0x000000);
} else {
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 180,
g_err ? "no network - tap to retry" : "fetching QR key...", 20, th->dim);
}
/* 状态文字 */
const char *st = login_status_str(g_st.login_state);
if (g_st.login_state == NCM_LOGIN_OK && g_done) st = "success";
meow_ui_text_center(r_cont.x + r_cont.w/2, r_cont.y + 108 + 320 + 44,
st, 20, (g_st.login_state == NCM_LOGIN_OK) ? th->primary : th->dim);
}
/* 重绘 login 内容(QR+状态变化时)。 */
static void login_repaint(void)
{
meow_ui_invalidate(r_cont.x, r_cont.y, r_cont.w, r_cont.h);
}
const char *login_status_str(int state)
{
switch (state) {
case NCM_LOGIN_PENDING: return "waiting for scan...";
case NCM_LOGIN_SCANNED: return "scanned - confirm on phone";
case NCM_LOGIN_OK: return "signed in!";
case NCM_LOGIN_EXPIRED: return "QR expired - refreshing";
default: return "error, tap to retry";
}
}
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);
/* 先落登录态并放行跳页(真机联调 2026-08-31:status_check 在 weapi 读响应时
* 挂死(HTTP 读循环超时未生效),把跳页卡在登录页。昵称是装饰信息,
* 先跳 home(歌单请求会独立验证 cookie),status_check 留待修复后恢复。) */
appstate_set_logged_in("", "");
g_st.logged_in = 1;
g_done = 1;
meow_log(MEOW_LOG_INFO, "login: -> home (status_check deferred)");
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 (pthread_create(&g_thread, NULL, 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_cont = ncm_content_rect();
g_custom_id = meow_draw_custom_register(1, r_cont.x, r_cont.y, r_cont.w, r_cont.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);
}