Files
MeowOS-For-R3P2/patch_v2/sdk/meow_ui_gfx.c
T

698 lines
29 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.
/*
* meow_ui_gfx.c — meow_ui 渲染后端(B1.0+B1.1)。
*
* 独立实现:自建 fb 上下文(自 mmap /dev/fb0、自建 back_buf、自实现脏区+present),
* 不复用 launcher 的全局 g_dirty/fb(launcher 与 app 是不同进程)。机制参考
* MEOW_APP_SDK.md §1/§2 与 launcher 的 M2.16 无闪烁地基,代码全部独立书写。
*
* 抽象:所有绘制目标 = 离屏 back_buf(g_fb);meow_ui_flush() 时单次原子
* vsync+blit 到 fb0(真实设备),或写出 PPM 截图(宿主/qemu 离屏,MEOW_UI_DUMP)。
*/
#include "meow_ui.h"
#include "meow_ui_internal.h"
#include "meow_ui_sys.h"
#include "../meow_launcherd/meow_font.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <time.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
/* 本设备实测 ioctl 号(勿与通用头交换) */
#define FBIOGET_VSCREENINFO 0x4600
#define FBIOGET_FSCREENINFO 0x4602
#define FBIO_WAITFORVSYNC 0x4620
#define FB_DEV "/dev/fb0"
#define MEOW_FONT_PNG_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.png"
#define MEOW_FONT_IDX_CARD "/usr/data/mnt/sd_0/MeowOS/fonts/default/font.idx"
#define MEOW_FONT_PNG_SYS "/usr/share/meowos/font/font.png"
#define MEOW_FONT_IDX_SYS "/usr/share/meowos/font/font.idx"
/* 默认字库(卡 MeowOS/fonts/default)与固件内置之外,launcher 会把「当前生效字体」
* 经环境变量下发给 app(见 meow_launcherd.c launch_app / meow_font_effective_paths):
* MEOW_FONT_PNG / MEOW_FONT_IDX = 绝对路径;MEOW_FONT_APP = <family> 或 "default"。
* SDK load_font 优先读 env(匹配系统 font.file),无则回退卡默认 → 固件。 */
#define MUI_DEFAULT_W 480
#define MUI_DEFAULT_H 720
/* 脏矩形上限(超出合并为全屏) */
#define MUI_DIRTY_MAX 12
struct mu_rect { int x, y, w, h; };
/* ---- fb 上下文 ---- */
static unsigned char *g_back_buf = NULL; /* 离屏(所有绘制目标) */
static unsigned char *g_fb_mem = NULL; /* 真实 mmap 帧缓冲(present 落点;NULL=离屏) */
static unsigned char *g_fb = NULL; /* 绘制指针 = g_back_buf */
static int g_fb_fd = -1;
static int g_xres = MUI_DEFAULT_W, g_yres = MUI_DEFAULT_H;
static int g_bpp = 16;
static int g_line = 0; /* 每行字节数 */
static int g_init_ok = 0;
static int g_mfont_ready = 0;
static int g_dirty[/*MUI_DIRTY_MAX*/12][4]; /* [x,y,w,h] */
static int g_ndirty = 0;
static int g_dirty_full = 1;
static char *g_dump_path = NULL;
/* ---- 帧率调度(A-1/A-2:从 launcher M2.16 迁入,刷新率调度归口 SDK)----
* meow_ui_flush()/present() 的 fps 档位与自适应降档 governor。launcher 不再自带
* 一套帧率门控;launcher 与 app 统一走这里 → 全局观感/刷新率一致。
* 设置接口:meow_ui_set_refresh_rate()/meow_ui_set_refresh_always_max()。 */
static int g_refresh_rate_cfg = 60; /* 配置目标档 60/30/15(上限) */
static int g_eff_rate = 60; /* 当前有效帧率(governor 可降档) */
static int g_refresh_always_max = 0; /* 1=恒 max 不降档 */
static unsigned long long g_last_present_ms = 0;
static unsigned long long g_frames_presented = 0;
static unsigned long long g_present_cost_sum = 0;
static int g_present_cost_n = 0;
static int g_gov_low = 0;
/* 每档上限(refresh_always_max=1 时恒 max;0 时 governor 可在 [15,30,60] 内降/升档)。 */
static int rate_bump(int r) { return (r == 15) ? 30 : (r == 30) ? 60 : 60; }
static int rate_drop(int r) { return (r == 60) ? 30 : (r == 30) ? 15 : 15; }
/* 自适应降档 governor:统计每帧 present 耗时;连续 12 帧平均超预算 → 降一档;
* 连续 4 批(≈48 帧)平均远低于预算 → 升一档。refresh_always_max=1 时恒 max 不降。 */
static void refresh_governor(unsigned long long cost_ms, int period)
{
if (g_refresh_always_max || g_eff_rate <= 0) return;
g_present_cost_sum += cost_ms; g_present_cost_n++;
if (g_present_cost_n < 12) return;
unsigned long long avg = g_present_cost_sum / (unsigned int)g_present_cost_n;
if (avg > (unsigned long long)period) {
if (g_eff_rate > 15) g_eff_rate = rate_drop(g_eff_rate);
g_gov_low = 0;
} else if (avg * 2 < (unsigned long long)period) {
if (++g_gov_low >= 4) {
int nr = rate_bump(g_eff_rate);
if (nr <= g_refresh_rate_cfg) g_eff_rate = nr;
g_gov_low = 0;
}
} else g_gov_low = 0;
g_present_cost_sum = 0; g_present_cost_n = 0;
}
/* ---- 工具 ---- */
static uint64_t now_ms(void)
{
struct timespec ts;
clock_gettime(CLOCK_MONOTONIC, &ts);
return (uint64_t)ts.tv_sec * 1000u + (uint64_t)ts.tv_nsec / 1000000u;
}
uint64_t meow_ui_time_ms(void) { return now_ms(); }
static uint16_t rgb565(uint32_t c)
{
return (uint16_t)(((c >> 8) & 0xF800) | ((c >> 5) & 0x07E0) | ((c >> 3) & 0x001F));
}
static uint8_t ex5(uint16_t v) { return (uint8_t)(((v) << 3) | ((v) >> 2)); } /* 5→8 */
static uint8_t ex6(uint16_t v) { return (uint8_t)(((v) << 2) | ((v) >> 4)); } /* 6→8 */
static int rect_hit(int ax, int ay, int aw, int ah, int bx, int by, int bw, int bh)
{
return ax < bx + bw && ax + aw > bx && ay < by + bh && ay + ah > by;
}
/* ================= 脏区追踪 ================= */
static void dirty_full(void) { g_dirty_full = 1; g_ndirty = 0; }
static void dirty_reset(void) { g_ndirty = 0; g_dirty_full = 0; }
static void dirty_add(int x, int y, int w, int h)
{
if (g_dirty_full) return;
if (w <= 0 || h <= 0) return;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= g_xres || y >= g_yres) return;
if (x + w > g_xres) w = g_xres - x;
if (y + h > g_yres) h = g_yres - y;
if (w <= 0 || h <= 0) return;
for (int i = 0; i < g_ndirty; i++) {
if (rect_hit(g_dirty[i][0], g_dirty[i][1], g_dirty[i][2], g_dirty[i][3], x, y, w, h)) {
int nx1 = g_dirty[i][0] < x ? g_dirty[i][0] : x;
int ny1 = g_dirty[i][1] < y ? g_dirty[i][1] : y;
int nx2 = (g_dirty[i][0] + g_dirty[i][2]) > (x + w) ? (g_dirty[i][0] + g_dirty[i][2]) : (x + w);
int ny2 = (g_dirty[i][1] + g_dirty[i][3]) > (y + h) ? (g_dirty[i][1] + g_dirty[i][3]) : (y + h);
g_dirty[i][0] = nx1; g_dirty[i][1] = ny1;
g_dirty[i][2] = nx2 - nx1; g_dirty[i][3] = ny2 - ny1;
return;
}
}
if (g_ndirty >= MUI_DIRTY_MAX) { dirty_full(); return; }
g_dirty[g_ndirty][0] = x; g_dirty[g_ndirty][1] = y;
g_dirty[g_ndirty][2] = w; g_dirty[g_ndirty][3] = h;
g_ndirty++;
}
void meow_gfx_mark_dirty(int x, int y, int w, int h) { dirty_add(x, y, w, h); }
void meow_gfx_mark_full(void) { dirty_full(); }
/* launcher(系统组件)与 SDK 共用同一 back_buf:launcher 的 fb 绘制指针指向它,
* 从而 launcher 与 app 统一渲染栈(同一离屏 + 同一 present)。 */
unsigned char *meow_ui_back_buf(void) { return g_back_buf; }
/* ================= 像素写入 ================= */
static void px_write_rgb565(int x, int y, uint16_t c16)
{
*(uint16_t *)(g_fb + (size_t)y * g_line + (size_t)x * 2) = c16;
}
static uint16_t px_read_rgb565(int x, int y)
{
return *(uint16_t *)(g_fb + (size_t)y * g_line + (size_t)x * 2);
}
static void put_px(int x, int y, uint32_t color)
{
if (x < 0 || x >= g_xres || y < 0 || y >= g_yres) return;
if (g_bpp == 16) {
px_write_rgb565(x, y, rgb565(color));
} else {
unsigned char *p = g_fb + (size_t)y * g_line + (size_t)x * 4;
*(uint32_t *)p = (color << 8) | 0xFF;
}
}
static void put_px_blend(int x, int y, uint32_t color, int alpha)
{
if (x < 0 || x >= g_xres || y < 0 || y >= g_yres) return;
uint16_t fg = rgb565(color);
if (g_bpp == 16) {
uint16_t bg = px_read_rgb565(x, y);
int ia = 255 - alpha;
unsigned r = ((((fg >> 11) & 0x1F) * alpha + ((bg >> 11) & 0x1F) * ia) / 255);
unsigned gg = ((((fg >> 5) & 0x3F) * alpha + ((bg >> 5) & 0x3F) * ia) / 255);
unsigned b = (((fg & 0x1F) * alpha + (bg & 0x1F) * ia) / 255);
px_write_rgb565(x, y, (uint16_t)((r << 11) | (gg << 5) | b));
} else {
unsigned char *p = g_fb + (size_t)y * g_line + (size_t)x * 4;
uint32_t bg = (*(uint32_t *)p) >> 8;
int ia = 255 - alpha;
unsigned r = ((((color >> 16) & 0xFF) * alpha + ((bg >> 16) & 0xFF) * ia) / 255);
unsigned gg = ((((color >> 8) & 0xFF) * alpha + ((bg >> 8) & 0xFF) * ia) / 255);
unsigned b = (((color & 0xFF) * alpha + (bg & 0xFF) * ia) / 255);
*(uint32_t *)p = (r << 24) | (gg << 16) | (b << 8) | 0xFF;
}
}
/* ================= 基本绘制 ================= */
void meow_ui_clear(uint32_t color)
{
if (!g_init_ok) return;
/* 快速整刷(16bit 用半字填充,可 memset 当色值由 32bit 拼接掩码) */
if (g_bpp == 16) {
uint16_t c16 = rgb565(color);
uint32_t c32 = (uint32_t)c16 | ((uint32_t)c16 << 16);
for (int y = 0; y < g_yres; y++) {
uint32_t *row = (uint32_t *)(g_fb + (size_t)y * g_line);
int n = g_xres / 2;
for (int i = 0; i < n; i++) row[i] = c32;
if (g_xres & 1) px_write_rgb565(g_xres - 1, y, c16);
}
} else {
for (int y = 0; y < g_yres; y++)
for (int x = 0; x < g_xres; x++) put_px(x, y, color);
}
dirty_full();
}
void meow_ui_fill_rect(int x, int y, int w, int h, uint32_t color)
{
if (!g_init_ok) return;
if (w <= 0 || h <= 0) return;
int x0 = x, y0 = y;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= g_xres || y >= g_yres) return;
if (x + w > g_xres) w = g_xres - x;
if (y + h > g_yres) h = g_yres - y;
if (w <= 0 || h <= 0) return;
if (g_bpp == 16) {
uint16_t c16 = rgb565(color);
for (int ry = y; ry < y + h; ry++) {
uint16_t *rp = (uint16_t *)(g_fb + (size_t)ry * g_line + (size_t)x * 2);
for (int rx = 0; rx < w; rx++) rp[rx] = c16;
}
} else {
for (int ry = y; ry < y + h; ry++)
for (int rx = x; rx < x + w; rx++) put_px(rx, ry, color);
}
dirty_add(x, y, w, h);
}
void meow_ui_put_px(int x, int y, uint32_t color)
{
if (!g_init_ok) return;
put_px(x, y, color);
dirty_add(x, y, 1, 1);
}
void meow_ui_draw_rect(int x, int y, int w, int h, uint32_t color)
{
if (w <= 0 || h <= 0) return;
meow_ui_fill_rect(x, y, w, 1, color);
meow_ui_fill_rect(x, y + h - 1, w, 1, color);
meow_ui_fill_rect(x, y, 1, h, color);
meow_ui_fill_rect(x + w - 1, y, 1, h, color);
}
/* 圆角矩形区域判定(含内缩) */
static int rr_inside(int px, int py, int x, int y, int w, int h, int r)
{
if (px < x || px >= x + w || py < y || py >= y + h) return 0;
if (r <= 0) return 1;
if (w < 2 * r || h < 2 * r) return 1; /* 半径过大退化为矩形 */
int cx1 = x + r, cy1 = y + r;
int cx2 = x + w - 1 - r, cy2 = y + h - 1 - r;
if (px < cx1 && py < cy1) return ((px - cx1) * (px - cx1) + (py - cy1) * (py - cy1)) <= r * r;
if (px > cx2 && py < cy1) return ((px - cx2) * (px - cx2) + (py - cy1) * (py - cy1)) <= r * r;
if (px < cx1 && py > cy2) return ((px - cx1) * (px - cx1) + (py - cy2) * (py - cy2)) <= r * r;
if (px > cx2 && py > cy2) return ((px - cx2) * (px - cx2) + (py - cy2) * (py - cy2)) <= r * r;
return 1;
}
static int rr_border(int px, int py, int x, int y, int w, int h, int r)
{
if (!rr_inside(px, py, x, y, w, h, r)) return 0;
int ir = r - 1; if (ir < 0) ir = 0;
if (w > 2 && h > 2 && rr_inside(px, py, x + 1, y + 1, w - 2, h - 2, ir)) return 0;
return 1;
}
void meow_gfx_fill_round(int x, int y, int w, int h, int r, uint32_t color)
{
if (!g_init_ok) return;
int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0;
if (x2 > g_xres) x2 = g_xres; if (y2 > g_yres) y2 = g_yres;
for (int py = y1; py < y2; py++)
for (int px = x1; px < x2; px++)
if (rr_inside(px, py, x, y, w, h, r)) put_px(px, py, color);
dirty_add(x1, y1, x2 - x1, y2 - y1);
}
void meow_ui_draw_rect_round(int x, int y, int w, int h, int r, uint32_t color)
{
if (!g_init_ok) return;
int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0;
if (x2 > g_xres) x2 = g_xres; if (y2 > g_yres) y2 = g_yres;
for (int py = y1; py < y2; py++)
for (int px = x1; px < x2; px++)
if (rr_border(px, py, x, y, w, h, r)) put_px(px, py, color);
dirty_add(x1, y1, x2 - x1, y2 - y1);
}
void meow_ui_fill_rect_alpha(int x, int y, int w, int h, uint32_t color, int alpha)
{
if (!g_init_ok) return;
if (alpha <= 0) return;
if (alpha >= 255) { meow_ui_fill_rect(x, y, w, h, color); return; }
if (w <= 0 || h <= 0) return;
if (x < 0) { w += x; x = 0; }
if (y < 0) { h += y; y = 0; }
if (x >= g_xres || y >= g_yres) return;
if (x + w > g_xres) w = g_xres - x;
if (y + h > g_yres) h = g_yres - y;
if (w <= 0 || h <= 0) return;
for (int ry = y; ry < y + h; ry++)
for (int rx = x; rx < x + w; rx++) put_px_blend(rx, ry, color, alpha);
dirty_add(x, y, w, h);
}
/* ================= 左滑返回视觉条(GESTURE_BACK §2.3) ================= */
#define MUI_HINT_H 5
static int g_hint_last = 0; /* 上次画过的条长(清条时只清到它,避免残留) */
void meow_ui_swipe_hint(int x)
{
if (!g_init_ok) return;
const meow_theme *th = meow_theme_get();
/* 进度条画在状态栏下沿(z 在状态栏之下,不被全局状态栏覆盖)。
* 有状态栏(非全屏)→ y = 状态栏高度;无状态栏(全屏/隐藏)→ y=0。 */
int hy = meow_ui_statusbar_height();
if (x > 0) {
if (x > g_xres) x = g_xres;
/* 往回滑(新长度 < 上次)时,先清掉「上次画到旧长度、但本次超出的部分」,
* 否则条只增不减(用户要求:右滑变长、左滑回缩实时可见)。 */
if (g_hint_last > x) meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg);
meow_ui_fill_rect(0, hy, x, MUI_HINT_H, th->accent);
g_hint_last = x;
} else if (g_hint_last > 0) {
/* 手指回原点/抬手未达标/抬手达标:清掉此前画的条(填回背景色) */
meow_ui_fill_rect(0, hy, g_hint_last, MUI_HINT_H, th->bg);
g_hint_last = 0;
}
/* 不发 flush:由调用方 meow_ui_flush_region(0,0,宽,6) 只刷顶部条 */
}
/* ================= 5x7 回退点阵字(与 launcher/hello 一致的表,独立携带) ================= */
static const unsigned char mfont5x7[][8] = {
{0x0E,0x11,0x13,0x15,0x19,0x11,0x0E,0x00},{0x04,0x0C,0x04,0x04,0x04,0x04,0x0E,0x00},
{0x0E,0x11,0x01,0x02,0x04,0x08,0x1F,0x00},{0x1F,0x02,0x04,0x02,0x01,0x11,0x0E,0x00},
{0x02,0x06,0x0A,0x12,0x1F,0x02,0x02,0x00},{0x1F,0x10,0x1E,0x01,0x01,0x11,0x0E,0x00},
{0x06,0x08,0x10,0x1E,0x11,0x11,0x0E,0x00},{0x1F,0x01,0x02,0x04,0x08,0x08,0x08,0x00},
{0x0E,0x11,0x11,0x0E,0x11,0x11,0x0E,0x00},{0x0E,0x11,0x11,0x0F,0x01,0x02,0x0C,0x00},
{0x0E,0x11,0x11,0x1F,0x11,0x11,0x11,0x00},{0x1E,0x11,0x11,0x1E,0x11,0x11,0x1E,0x00},
{0x0E,0x11,0x10,0x10,0x10,0x11,0x0E,0x00},{0x1C,0x12,0x11,0x11,0x11,0x12,0x1C,0x00},
{0x1F,0x10,0x10,0x1E,0x10,0x10,0x1F,0x00},{0x1F,0x10,0x10,0x1E,0x10,0x10,0x10,0x00},
{0x0E,0x11,0x10,0x17,0x11,0x11,0x0F,0x00},{0x11,0x11,0x11,0x1F,0x11,0x11,0x11,0x00},
{0x0E,0x04,0x04,0x04,0x04,0x04,0x0E,0x00},{0x07,0x02,0x02,0x02,0x02,0x12,0x0C,0x00},
{0x11,0x12,0x14,0x18,0x14,0x12,0x11,0x00},{0x10,0x10,0x10,0x10,0x10,0x10,0x1F,0x00},
{0x11,0x1B,0x15,0x15,0x11,0x11,0x11,0x00},{0x11,0x19,0x15,0x13,0x11,0x11,0x11,0x00},
{0x0E,0x11,0x11,0x11,0x11,0x11,0x0E,0x00},{0x1E,0x11,0x11,0x1E,0x10,0x10,0x10,0x00},
{0x0E,0x11,0x11,0x11,0x15,0x12,0x0D,0x00},{0x1E,0x11,0x11,0x1E,0x14,0x12,0x11,0x00},
{0x0F,0x10,0x10,0x0E,0x01,0x01,0x1E,0x00},{0x1F,0x04,0x04,0x04,0x04,0x04,0x04,0x00},
{0x11,0x11,0x11,0x11,0x11,0x11,0x0E,0x00},{0x11,0x11,0x11,0x11,0x11,0x0A,0x04,0x00},
{0x11,0x11,0x11,0x15,0x15,0x1B,0x11,0x00},{0x11,0x11,0x0A,0x04,0x0A,0x11,0x11,0x00},
{0x11,0x11,0x11,0x0A,0x04,0x04,0x04,0x00},{0x1F,0x01,0x02,0x04,0x08,0x10,0x1F,0x00},
{0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00},{0x00,0x00,0x00,0x00,0x00,0x00,0x04,0x00},
{0x04,0x04,0x04,0x04,0x04,0x00,0x04,0x00},{0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00},
{0x00,0x00,0x00,0x1F,0x00,0x00,0x00,0x00},
};
#define FONT5X7_N (sizeof(mfont5x7)/sizeof(mfont5x7[0]))
static int glyph_index(char c)
{
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'A' && c <= 'Z') return 10 + (c - 'A');
if (c >= 'a' && c <= 'z') return 10 + (c - 'a');
if (c == ' ') return 36;
if (c == '.') return 37;
if (c == '!') return 38;
if (c == '-' || c == '_') return 39;
return 36;
}
static void draw_glyph5x7(int x, int y, char c, int scale, uint32_t color)
{
const unsigned char *g = mfont5x7[glyph_index(c)];
for (int row = 0; row < 7; row++) {
unsigned char bits = g[row];
for (int col = 0; col < 5; col++) {
if (bits & (0x10 >> col)) {
for (int dy = 0; dy < scale; dy++)
for (int dx = 0; dx < scale; dx++)
put_px(x + col * scale + dx, y + row * scale + dy, color);
}
}
}
}
static void draw_text5x7(int x, int y, const char *s, int scale, uint32_t color)
{
while (*s) { draw_glyph5x7(x, y, *s, scale, color); x += 6 * scale; s++; }
}
/* ================= 文本(字库优先,回退点阵) ================= */
static int fallback_scale(int size) { return (size >= 16) ? 2 : 1; }
static void text_draw(int x, int y, const char *s, int size, uint32_t color)
{
if (!g_init_ok || !s) return;
if (g_mfont_ready && g_bpp == 16) {
meow_font_drawtext((uint16_t *)g_fb, x, y, s, rgb565(color), size);
int w = meow_font_width_text(s, size);
int hh = meow_font_line_height(size);
dirty_add(x, y, w, hh);
} else {
int sc = fallback_scale(size);
draw_text5x7(x, y, s, sc, color);
dirty_add(x, y, (int)strlen(s) * 6 * sc, 7 * sc);
}
}
void meow_ui_text(int x, int y, const char *utf8, int size, uint32_t color)
{ text_draw(x, y, utf8, size, color); }
void meow_ui_text_right(int right_x, int y, const char *utf8, int size, uint32_t color)
{ text_draw(right_x - meow_ui_text_width(utf8, size), y, utf8, size, color); }
void meow_ui_text_center(int cx, int y, const char *utf8, int size, uint32_t color)
{ text_draw(cx - meow_ui_text_width(utf8, size) / 2, y, utf8, size, color); }
int meow_ui_text_width(const char *utf8, int size)
{
if (!utf8) return 0;
if (g_mfont_ready) return meow_font_width_text(utf8, size);
return (int)strlen(utf8) * 6 * fallback_scale(size);
}
int meow_ui_text_height(int size)
{
if (g_mfont_ready) return meow_font_line_height(size);
return 7 * fallback_scale(size);
}
/* ================= RGBA blit(图片层用) ================= */
void meow_gfx_blit_rgba(int x, int y, int w, int h, const uint8_t *rgba, int alpha)
{
if (!g_init_ok || !rgba || w <= 0 || h <= 0 || alpha <= 0) return;
int x1 = x, y1 = y, x2 = x + w, y2 = y + h;
if (x1 < 0) x1 = 0; if (y1 < 0) y1 = 0;
if (x2 > g_xres) x2 = g_xres; if (y2 > g_yres) y2 = g_yres;
for (int py = y1; py < y2; py++) {
int sy = py - y;
for (int px = x1; px < x2; px++) {
int sx = px - x;
const uint8_t *p = rgba + ((size_t)sy * w + sx) * 4;
int a = (alpha * p[3]) / 255;
if (a <= 0) continue;
uint32_t c = ((uint32_t)p[0] << 16) | ((uint32_t)p[1] << 8) | (uint32_t)p[2];
if (a >= 255) put_px(px, py, c);
else put_px_blend(px, py, c, a);
}
}
dirty_add(x1, y1, x2 - x1, y2 - y1);
}
/* ================= present / 上屏 ================= */
static void dump_ppm(void)
{
if (!g_dump_path || !g_back_buf) return;
FILE *f = fopen(g_dump_path, "wb");
if (!f) return;
fprintf(f, "P6\n%d %d\n255\n", g_xres, g_yres);
for (int y = 0; y < g_yres; y++) {
for (int x = 0; x < g_xres; x++) {
unsigned char r, g, b;
if (g_bpp == 16) {
uint16_t px = *(uint16_t *)(g_back_buf + (size_t)y * g_line + (size_t)x * 2);
r = ex5((px >> 11) & 0x1F); g = ex6((px >> 5) & 0x3F); b = ex5(px & 0x1F);
} else {
uint32_t px = *(uint32_t *)(g_back_buf + (size_t)y * g_line + (size_t)x * 4);
r = (px >> 16) & 0xFF; g = (px >> 8) & 0xFF; b = px & 0xFF;
}
fputc(r, f); fputc(g, f); fputc(b, f);
}
}
fclose(f);
}
/* 顶部状态栏条是否被本次脏区触及(含整帧):触及或数据已变(force)才需要重画顶条。 */
static int statusbar_touched(void)
{
if (g_dirty_full) return 1;
for (int i = 0; i < g_ndirty; i++)
if (g_dirty[i][1] < MUI_SBAR_H) return 1; /* 脏矩形与顶条(前 MUI_SBAR_H 行)相交 */
return 0;
}
static void present(void)
{
if (!g_init_ok || !g_back_buf) return;
if (!(g_dirty_full || g_ndirty > 0)) return;
/* 帧率门控(A-1/A-2,从 launcher 迁入):present 距上次 ≥ period(1000/eff_rate) 才呈现;
* 未满周期则保留脏区(不 dirty_reset),下一轮再试。refresh_always_max=1 时跳过门控(恒满速)。 */
unsigned long long now = now_ms();
int period = (g_eff_rate > 0) ? (1000 / g_eff_rate) : 16;
if (!g_refresh_always_max) {
if (g_last_present_ms && now - g_last_present_ms < (unsigned long long)period) return;
}
/* 全局状态栏(A-0):上屏前把状态栏画进 back_buf 顶部 MUI_SBAR_H 行(z 最顶)。
* 全屏/隐藏时 active()==0 不画(沉浸),app 内容可占满整屏。触发条件:
* 数据已变(1s 轻刷/显隐切换) 或 顶条被 app 内容触及(含整帧清+绘,未 inset 时被顶条覆盖)。
* 无需每帧重画——app 局部刷新不触及顶条时,back_buf/screen 顶条保持不变,省一次 blit。 */
if (meow_ui_sys_statusbar_active() &&
(meow_ui_sys_statusbar_force() || statusbar_touched()))
meow_ui_sys_statusbar_render();
if (g_fb_mem) {
ioctl(g_fb_fd, FBIO_WAITFORVSYNC, 0); /* 等垂直同步 */
unsigned long long tbg = now_ms();
if (g_dirty_full) {
memcpy(g_fb_mem, g_back_buf, (size_t)g_line * (size_t)g_yres);
} else {
int bppb = g_bpp / 8;
for (int i = 0; i < g_ndirty; i++) {
int rw = g_dirty[i][2], ry = g_dirty[i][1];
for (int yy = 0; yy < g_dirty[i][3]; yy++) {
size_t off = (size_t)(ry + yy) * g_line + (size_t)g_dirty[i][0] * bppb;
memcpy(g_fb_mem + off, g_back_buf + off, (size_t)rw * bppb);
}
}
}
refresh_governor(now_ms() - tbg, period);
}
g_last_present_ms = now;
g_frames_presented++;
if (g_dump_path) dump_ppm();
dirty_reset();
}
void meow_ui_flush(void) { present(); }
void meow_ui_flush_region(int x, int y, int w, int h)
{ dirty_add(x, y, w, h); present(); }
/* ---- 帧率调度设置接口(launcher 与 app 共用;归口 PERF_SCHEDULER 显示刷新率子域)---- */
/* 设目标档位(60/30/15):同时把当前有效档对齐到目标(与 launcher settings_load 语义一致)。 */
void meow_ui_set_refresh_rate(int r)
{
if (r != 60 && r != 30 && r != 15) return;
g_refresh_rate_cfg = r;
g_eff_rate = r;
}
/* 1=恒 max 不降档(性能优先);0=允许 governor 在 [15,30,60] 内自适应降/升档。 */
void meow_ui_set_refresh_always_max(int on) { g_refresh_always_max = on ? 1 : 0; }
/* 当前有效帧率(governor 可能已降档;验证/诊断用)。 */
int meow_ui_effective_rate(void) { return g_eff_rate; }
/* 已呈现帧计数(验证 present 频率用)。 */
unsigned long long meow_ui_frames_presented(void) { return g_frames_presented; }
int meow_gfx_is_device(void) { return g_fb_mem != NULL; }
int meow_gfx_mfont_ready(void) { return g_mfont_ready; }
/* ================= 初始化/销毁 ================= */
/* 字库加载优先级(M2.x):
* 1) 环境变量 MEOW_FONT_PNG/MEOW_FONT_IDX(launcher 注入的「当前生效字体」= 系统 font.file)
* —— app 未自带字库(§5.7 load_handle 未实现)时跟随系统 font.file;
* 2) 卡默认 MeowOS/fonts/default/font.png(不再硬编码 MeowAPPs/fonts);
* 3) 固件 /usr/share/meowos/font/font.png。
* 全失败 → g_mfont_ready=0,text_draw 回退 5x7 点阵(系统不死)。 */
static int load_font(void)
{
const char *ep = getenv("MEOW_FONT_PNG");
const char *ei = getenv("MEOW_FONT_IDX");
if (ep && ep[0] && ei && ei[0] && meow_font_load(ep, ei) == 0) return 1;
if (meow_font_load(MEOW_FONT_PNG_CARD, MEOW_FONT_IDX_CARD) == 0) return 1;
if (meow_font_load(MEOW_FONT_PNG_SYS, MEOW_FONT_IDX_SYS) == 0) return 1;
return 0;
}
/* M2.5x 多字体:读 launcher 注入的环境变量,构建 6 字符类 → 字库的映射(同 family 去重、
* ≤4 套上限由 meow_font_config_multifont 统一处理)。launcher 注入(见 meow_launcherd.c
* launch_app):MEOW_MULTI_FONT=0/1;MEOW_FONT_PNG/IDX=全局(保留);每类
* MEOW_FONT_CLS_<X>_PNG/IDX(命中则覆盖全局;未注入 = 该类跟随全局)。
* 仅当全局字库已装载(g_mfont_ready)才配置;关/无 env → 单字体,零额外开销。 */
static void meow_gfx_load_multifont(void)
{
static const char *cls_env[MEOW_FCLS_COUNT][2] = {
{ "MEOW_FONT_CLS_CJK_PNG", "MEOW_FONT_CLS_CJK_IDX" },
{ "MEOW_FONT_CLS_CJK_SYM_PNG", "MEOW_FONT_CLS_CJK_SYM_IDX" },
{ "MEOW_FONT_CLS_NUM_PNG", "MEOW_FONT_CLS_NUM_IDX" },
{ "MEOW_FONT_CLS_LATIN_PNG", "MEOW_FONT_CLS_LATIN_IDX" },
{ "MEOW_FONT_CLS_LATIN_SYM_PNG", "MEOW_FONT_CLS_LATIN_SYM_IDX" },
{ "MEOW_FONT_CLS_OTHER_PNG", "MEOW_FONT_CLS_OTHER_IDX" },
};
const char *emf = getenv("MEOW_MULTI_FONT");
int on = emf && emf[0] == '1';
const char *gp = getenv("MEOW_FONT_PNG");
const char *gi = getenv("MEOW_FONT_IDX");
meow_font_class_cfg cfg[MEOW_FCLS_COUNT];
memset(cfg, 0, sizeof cfg);
for (int c = 0; c < MEOW_FCLS_COUNT; c++) {
const char *p = getenv(cls_env[c][0]);
const char *idx = getenv(cls_env[c][1]);
if (p && p[0] && idx && idx[0]) { cfg[c].png = p; cfg[c].idx = idx; }
}
int distinct = 0, truncated = 0;
if (meow_font_config_multifont(on, gp, gi, cfg, &distinct, &truncated) != 0)
meow_log(MEOW_LOG_WARN, "multi_font: config failed, single-font fallback");
else if (truncated)
meow_log(MEOW_LOG_WARN, "multi_font: >%d distinct fonts -> capped (more memory!)", distinct);
}
int meow_ui_init(const meow_ui_opts *opts)
{
if (g_init_ok) return 0;
int fw = 0, fh = 0;
if (opts) { fw = opts->width; fh = opts->height; }
int fd = open(FB_DEV, O_RDWR);
if (fd >= 0) {
unsigned char vinfo[160], finfo[68];
memset(vinfo, 0, sizeof vinfo);
memset(finfo, 0, sizeof finfo);
if (ioctl(fd, FBIOGET_VSCREENINFO, vinfo) == 0 &&
ioctl(fd, FBIOGET_FSCREENINFO, finfo) == 0) {
g_xres = *(uint32_t *)(vinfo + 0);
g_yres = *(uint32_t *)(vinfo + 4);
g_bpp = *(uint32_t *)(vinfo + 24);
g_line = *(uint32_t *)(finfo + 44);
if (!g_line) g_line = g_xres * (g_bpp / 8);
if (g_bpp != 16 && g_bpp != 32) g_bpp = 16;
size_t maplen = (size_t)g_line * (size_t)g_yres;
void *m = mmap(NULL, maplen, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (m != MAP_FAILED) { g_fb_mem = m; g_fb_fd = fd; }
else { close(fd); fd = -1; g_fb_mem = NULL; }
} else { close(fd); fd = -1; g_fb_mem = NULL; }
}
/* host/qemu 无 fb:使用缺省/覆写几何,走离屏(present 只 dump) */
if (g_fb_mem == NULL) {
if (g_xres <= 0 || g_xres > 4096) g_xres = MUI_DEFAULT_W;
if (g_yres <= 0 || g_yres > 4096) g_yres = MUI_DEFAULT_H;
if (g_bpp != 16 && g_bpp != 32) g_bpp = 16;
g_line = g_xres * (g_bpp / 8);
if (fd >= 0) { close(fd); fd = -1; }
}
if (fw > 0 && fw <= 4096) g_xres = fw;
if (fh > 0 && fh <= 4096) g_yres = fh;
g_line = g_xres * (g_bpp / 8);
g_back_buf = malloc((size_t)g_line * (size_t)g_yres);
if (!g_back_buf) { if (g_fb_mem) munmap(g_fb_mem, 0); if (fd >= 0) close(fd); return -1; }
g_fb = g_back_buf;
meow_font_set_fb((unsigned)g_xres, (unsigned)g_yres, (unsigned)g_line);
g_mfont_ready = load_font();
if (g_mfont_ready) meow_gfx_load_multifont(); /* M2.5x 多字体(读 env;关/无 env = 单字体) */
const char *dp = getenv("MEOW_UI_DUMP");
if (dp && *dp) g_dump_path = strdup(dp);
g_init_ok = 1;
dirty_full();
meow_ui_sys_init(); /* A-0:系统状态读取器首刷(电量/WiFi/蓝牙/音量),状态栏就绪 */
return 0;
}
void meow_ui_shutdown(void)
{
if (!g_init_ok) return;
meow_font_unload();
if (g_fb_mem) munmap(g_fb_mem, (size_t)g_line * (size_t)g_yres);
if (g_fb_fd >= 0) close(g_fb_fd);
if (g_back_buf) free(g_back_buf);
if (g_dump_path) { free(g_dump_path); g_dump_path = NULL; }
g_back_buf = NULL; g_fb = NULL; g_fb_mem = NULL;
g_fb_fd = -1; g_init_ok = 0; g_mfont_ready = 0;
dirty_reset();
}
int meow_ui_width(void) { return g_xres; }
int meow_ui_height(void) { return g_yres; }