• 全新单提交:清除含敏感信息的旧历史(sudo 口令、内网 IP、SSH 密钥路径、热点 SSID、设备 MAC) • 本地开发文档/测试记录/构建脚本全部移入 gitignored 的 devdocs/ 与 scripts/build/(不入库) • 保留:MeowOS 源码(launcher/watchdog/bootmenu/meow_svcd/SDK)、项目规范文档、mips-repo 增补组件:SDK 统一日志+通知 API、meow_svcd 服务管理器、mips-repo 软件仓库
198 lines
7.1 KiB
C
198 lines
7.1 KiB
C
/*
|
||
* meow_ui_theme.c — 主题/配置(B1.5 / M2.46)。
|
||
*
|
||
* 内置日间/夜间两套色板;模式跟随系统设置键 display.theme_mode + display.theme
|
||
* (/usr/data/meow/settings.conf,与 launcher meow_settings 同源)。
|
||
* 判定与 launcher 的 s_ui_theme() 完全对齐(避免两套逻辑漂移):
|
||
* - display.theme_mode = manual:直读 display.theme(dark/light)决定深浅。
|
||
* - display.theme_mode = auto:按深色时段 display.theme_dark_start/end(分钟 0-1439),
|
||
* 半开区间 [start,end);start>end 跨午夜;start==end 视为未配置 → 回退 manual(display.theme)。
|
||
* 读取缺失/非法一律回退深色(与 launcher 默认一致)。
|
||
* 切换即广播 listener。AUTO 解析只在查询时按当前系统时间计算(不缓存)。
|
||
*/
|
||
#include "meow_ui.h"
|
||
#include <stdio.h>
|
||
#include <string.h>
|
||
#include <stdlib.h>
|
||
#include <time.h>
|
||
|
||
#define MEOW_SETTINGS_FILE "/usr/data/meow/settings.conf"
|
||
|
||
static meow_theme g_light = {
|
||
0xE8EAF0, 0x1A1A2A, 0x2A5A8A, 0x4A4F5A, 28, 20
|
||
};
|
||
static meow_theme g_dark = {
|
||
0x141824, 0xFFFFFF, 0x4A6A9A, 0x9AA4C8, 28, 20
|
||
};
|
||
|
||
static meow_theme_mode g_mode = MEOW_THEME_DARK; /* 配置生效的判定模式(默认深色,与 launcher 默认一致) */
|
||
static meow_theme_mode g_manual = MEOW_THEME_DARK; /* display.theme 解析出的手动深浅;auto 未配置时段时回退用之 */
|
||
static int g_dark_start = 0; /* 深色时段开始(分钟 0-1439) */
|
||
static int g_dark_end = 0; /* 深色时段结束(分钟 0-1439) */
|
||
static int g_loaded = 0;
|
||
|
||
static meow_theme_listener g_listeners[8];
|
||
static int g_nlisteners = 0;
|
||
|
||
/* ---- 读 /usr/data/meow/settings.conf 的 key=value(兼容 "key = value" 带空格形式)----
|
||
* 与 launcher meow_settings_import 同款解析:按 '=' 切分,key/value 各自去首尾空白,
|
||
* 精确匹配 key(避免 display.theme 误匹配 display.theme_mode)。返回 0=命中,-1=未命中。 */
|
||
static int get_setting_str(const char *key, char *buf, int buflen)
|
||
{
|
||
FILE *f = fopen(MEOW_SETTINGS_FILE, "r");
|
||
if (!f || buflen <= 0) return -1;
|
||
char line[160];
|
||
int kl = (int)strlen(key);
|
||
int found = 0;
|
||
while (fgets(line, sizeof line, f)) {
|
||
char *eq = strchr(line, '=');
|
||
if (!eq) continue;
|
||
/* key = 行首到 '=',去尾空白 */
|
||
char *k = line;
|
||
while (*k == ' ' || *k == '\t') k++;
|
||
int n = (int)(eq - k);
|
||
while (n > 0 && (k[n-1] == ' ' || k[n-1] == '\t')) n--;
|
||
if (n != kl || strncmp(k, key, (size_t)kl) != 0) continue;
|
||
/* value = '=' 后,去首尾空白 / 行尾 CRLF */
|
||
const char *v = eq + 1;
|
||
while (*v == ' ' || *v == '\t') v++;
|
||
int m = 0;
|
||
while (*v && *v != '\n' && *v != '\r' && m < buflen - 1) buf[m++] = *v++;
|
||
while (m > 0 && (buf[m-1] == ' ' || buf[m-1] == '\t')) m--;
|
||
buf[m] = 0;
|
||
found = 1;
|
||
break;
|
||
}
|
||
fclose(f);
|
||
return found ? 0 : -1;
|
||
}
|
||
|
||
static int get_setting_int(const char *key, int *out)
|
||
{
|
||
char buf[32];
|
||
if (get_setting_str(key, buf, sizeof buf) != 0) return -1;
|
||
*out = atoi(buf);
|
||
return 0;
|
||
}
|
||
|
||
/* 解析 display.theme 的值(dark/light,非索引)→ LIGHT/DARK;未知/缺失回退 DARK(launcher 默认)。 */
|
||
static meow_theme_mode parse_manual(const char *v)
|
||
{
|
||
while (*v == ' ' || *v == '\t') v++;
|
||
if (!strncmp(v, "light", 5)) return MEOW_THEME_LIGHT;
|
||
return MEOW_THEME_DARK; /* "dark" / 未知 → 深色 */
|
||
}
|
||
|
||
static void load_settings(void)
|
||
{
|
||
char buf[64];
|
||
|
||
g_manual = MEOW_THEME_DARK;
|
||
g_mode = MEOW_THEME_DARK;
|
||
g_dark_start = 0;
|
||
g_dark_end = 0;
|
||
|
||
/* display.theme:手动深浅(auto 未配置时段时也回退这里) */
|
||
if (get_setting_str("display.theme", buf, sizeof buf) == 0)
|
||
g_manual = parse_manual(buf);
|
||
|
||
/* display.theme_mode:manual=读 display.theme;auto=时段。缺失/非法 → manual */
|
||
if (get_setting_str("display.theme_mode", buf, sizeof buf) == 0) {
|
||
if (!strcmp(buf, "auto")) g_mode = MEOW_THEME_AUTO;
|
||
else g_mode = g_manual; /* manual(含 "light"/"dark"/未知)→ 读 display.theme */
|
||
} else {
|
||
g_mode = g_manual; /* 无 theme_mode 键 → 默认 manual → display.theme */
|
||
}
|
||
|
||
get_setting_int("display.theme_dark_start", &g_dark_start);
|
||
get_setting_int("display.theme_dark_end", &g_dark_end);
|
||
}
|
||
|
||
/* 当前本地时刻的「当天第几分钟」(tm_hour*60+tm_min);供时段判定用(与 launcher s_now_min_of_day 一致)。 */
|
||
static int now_min_of_day(void)
|
||
{
|
||
time_t t = time(NULL);
|
||
struct tm tm;
|
||
localtime_r(&t, &tm);
|
||
return tm.tm_hour * 60 + tm.tm_min;
|
||
}
|
||
|
||
/* 深色时段判定(与 launcher theme_should_dark 完全一致):now_min ∈ [start,end) → 深色。
|
||
* - start==end 视为未配置,返回 -1(由调用方决定回退策略)。
|
||
* - start<end:非跨天(如 720→1080 = 12:00→18:00)。
|
||
* - start>end:跨午夜(如 1140→420 = 19:00→次日07:00)。
|
||
* - 边界:end 半开(恰好 end 分回到浅色),start 闭(恰好 start 分进入深色)。 */
|
||
static int theme_should_dark(int now_min, int start, int end)
|
||
{
|
||
if (start == end) return -1; /* 未配置 */
|
||
if (start < end)
|
||
return (now_min >= start && now_min < end) ? 1 : 0;
|
||
return (now_min >= start || now_min < end) ? 1 : 0; /* 跨午夜 */
|
||
}
|
||
|
||
static meow_theme_mode resolve_mode(meow_theme_mode m)
|
||
{
|
||
if (m != MEOW_THEME_AUTO) return m;
|
||
int d = theme_should_dark(now_min_of_day(), g_dark_start, g_dark_end);
|
||
if (d < 0) return g_manual; /* 时段未配置:回退 display.theme 手动深浅 */
|
||
return d ? MEOW_THEME_DARK : MEOW_THEME_LIGHT;
|
||
}
|
||
|
||
static void broadcast(void)
|
||
{
|
||
for (int i = 0; i < g_nlisteners; i++)
|
||
if (g_listeners[i]) g_listeners[i](g_mode);
|
||
}
|
||
|
||
const meow_theme *meow_theme_get(void)
|
||
{
|
||
if (!g_loaded) {
|
||
load_settings();
|
||
g_loaded = 1;
|
||
}
|
||
return resolve_mode(g_mode) == MEOW_THEME_DARK ? &g_dark : &g_light;
|
||
}
|
||
|
||
meow_theme_mode meow_theme_get_mode(void)
|
||
{
|
||
if (!g_loaded) { load_settings(); g_loaded = 1; }
|
||
return g_mode;
|
||
}
|
||
|
||
void meow_theme_set_mode(meow_theme_mode m)
|
||
{
|
||
if (!g_loaded) { load_settings(); g_loaded = 1; }
|
||
if (m < MEOW_THEME_LIGHT || m > MEOW_THEME_AUTO) return;
|
||
if (m == g_mode) return;
|
||
g_mode = m;
|
||
broadcast();
|
||
}
|
||
|
||
meow_theme meow_theme_defaults(meow_theme_mode m)
|
||
{
|
||
switch (resolve_mode(m)) {
|
||
case MEOW_THEME_DARK: return g_dark;
|
||
case MEOW_THEME_LIGHT:
|
||
default: return g_light;
|
||
}
|
||
}
|
||
|
||
void meow_theme_add_listener(meow_theme_listener cb)
|
||
{
|
||
if (!cb) return;
|
||
if (g_nlisteners >= (int)(sizeof g_listeners / sizeof g_listeners[0])) return;
|
||
for (int i = 0; i < g_nlisteners; i++) if (g_listeners[i] == cb) return;
|
||
g_listeners[g_nlisteners++] = cb;
|
||
}
|
||
|
||
void meow_theme_remove_listener(meow_theme_listener cb)
|
||
{
|
||
for (int i = 0; i < g_nlisteners; i++) {
|
||
if (g_listeners[i] == cb) {
|
||
g_listeners[i] = g_listeners[g_nlisteners - 1];
|
||
g_nlisteners--;
|
||
return;
|
||
}
|
||
}
|
||
}
|