Files
MeowOS-For-R3P2/patch_v2/meow_launcherd/fb_compositor.c
T
MeowOS e6f0b665e3 refactor(launcher): 拆分 meow_launcherd.c(9778行)→meow_launcherd/ 多文件(agent-149)
- 14 模块 .c + meow_launcher_internal.h(642): util/font_theme/fb_compositor/settings_page/power_menu/notif_center/app_launcher/main_menu/audio/backlight/net/keys/about/statusbar; meow_launcherd.c 已删
- 按行逐字搬移(0-diff), 118函数提升非static+47组extern(内部头), #if0退役区不编入(源码留.pre_split.bak)
- 关于本机小改: 作者 AskaEth/17 + info/item[13]防越界 + s_dd_modal 超高滚动(clamp/触摸滚动/音量键跟随/裁剪, 仅超高启用)
- build_launcher.sh 编译列表→模块; 文档/注释引用 meow_launcherd.c→模块/目录
- 编译0warning/-Wall -Wextra告警集与原文一致(77条无新增)/qemu-mipsel冒烟一致(--scan/--sysdump); launcher=c0799357(505KB; md5因多文件重编译与原文异,非回归依据)
- 备份 meow_launcherd.c.pre_split.bak/meow_launcherd.pre_split.bak; M2.69(0.7.64)已推卡并用户验证OK
2026-08-30 21:38:30 +08:00

223 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.
/* fb_compositor.c — MeowOS launcher 拆分模块(LAUNCHER_SPLIT_PLAN 拆分)。*/
/* 本文件由拆分工具从 meow_launcherd.c 按职责搬移;逻辑与原文 0-diff(仅 static 可见性按跨模块需求调整)。*/
#include "meow_launcher_internal.h"
unsigned char *fb; /* 绘制目标指针 = SDK 的 back_buf(A-1/A-2:launcher 与 app 统一渲染栈) */
static unsigned char *fb_mem = NULL; /* 真实 mmap 帧缓冲(播放器帧备份/恢复源 + app 帧读回 + 双缓冲 yoffset 用) */
int fb_xres, fb_yres, fb_line, fb_bpp;
int fb_fd = -1;
static unsigned int fb_yres_virtual = 0; /* virtual height (double-buffer = 2*yres) */
static unsigned int fb_active_yoffset = 0; /* scanline of the displayed buffer (0 or yres) */
static int fb_orig_yoffset = -1; /* player's yoffset at menu open (saved) */
static unsigned char *fb_player_backup = NULL;
static size_t fb_player_backup_len = 0;
/* ---- A-1/A-2:launcher 用 SDK 渲染栈 ----
* fb 指向 SDK 的 back_buf(meow_ui_back_buf()),所有绘制(put_px/meow_font/dim_screen/
* draw_menu/statusbar/电源菜单/确认框)画进 SDK 的离屏;呈现统一走 meow_ui_flush()(= SDK present,
* 内含 vsync + 帧率门控 + 原子 blit 脏区)。launcher 不再自建 back_buf/meow_present/帧率门控。
* fb_mem 仍保留 launcher 自有的全虚拟 mmap:仅用于播放器帧备份/恢复/双缓冲 yoffset 切换
* (fb_backup_player/fb_norm_buf0/fb_restore_player/fb_pan_buf0_force)与 app 帧读回
* (pm_capture_app_frame)——这些是「显示 buffer/页码切换」,与「present 上屏」不同层,不动。 */
int back_buf_ok = 0; /* fb 是否指向 SDK back_buf(1=可经 SDK present 上屏) */
int fb_open(void)
{
int fd = open(FB_DEV, O_RDWR);
if (fd < 0) { logmsg("fb open failed: %s", strerror(errno)); return -1; }
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) {
logmsg("fb ioctl failed"); close(fd); return -1;
}
fb_xres = *(uint32_t *)(vinfo + 0);
fb_yres = *(uint32_t *)(vinfo + 4);
fb_yres_virtual = *(uint32_t *)(vinfo + 12);
fb_active_yoffset = *(uint32_t *)(vinfo + 20);
fb_bpp = *(uint32_t *)(vinfo + 24);
fb_line = *(uint32_t *)(finfo + 44);
if (!fb_line) fb_line = fb_xres * (fb_bpp / 8);
/* v6.6: fb0 is DOUBLE-BUFFERED (verified yres_virtual=1440=2*720,
* smem_len=2 frames, ypanstep=1). The displayed buffer is selected by
* yoffset (FBIOPAN_DISPLAY 0x4606 verified to flip it). Map the FULL
* virtual framebuffer so we can read/write both buffers for the
* backup/restore below; mmapping only one frame would let the menu
* land on the buffer the panel is NOT showing. */
if (fb_yres_virtual < fb_yres) fb_yres_virtual = fb_yres; /* single buffer */
fb_mem = mmap(NULL, (size_t)fb_line * fb_yres_virtual,
PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
if (fb_mem == MAP_FAILED) {
logmsg("fb mmap failed: %s (xres=%d yres=%d bpp=%d line=%d size=%d)",
strerror(errno), fb_xres, fb_yres, fb_bpp, fb_line,
fb_line * fb_yres_virtual);
close(fd);
return -1;
}
fb_fd = fd;
/* A-1/A-2:launcher 用 SDK 渲染栈 —— fb 指向 SDK 的 back_buf(统一离屏),present 统一走
* meow_ui_flush()(SDK present:vsync + 帧率门控 + 原子 blit 脏区)。不再自建 back_buf。
* fb_mem(本函数 mmap 的全虚拟 fb)仅保留给播放器帧备份/恢复 + yoffset 切换 + app 帧读回。 */
fb = meow_ui_back_buf();
if (fb) {
back_buf_ok = 1;
meow_dirty_full();
logmsg("fb: %dx%d bpp=%d line=%d vres=%u yoffset=%u (A-1/A-2 SDK back_buf, meow_ui_flush)",
fb_xres, fb_yres, fb_bpp, fb_line, fb_yres_virtual, fb_active_yoffset);
} else {
fb = fb_mem; back_buf_ok = 0;
logmsg("fb: no SDK back_buf -> direct fb write (present disabled)");
}
/* NOTE: the panel is NOT unblanked here anymore — the player may still
* be running at this point (stop_player() comes after fb_open) and its
* screen-off state machine would re-blank the panel behind the menu.
* unblank_panel() runs after stop_player() (v6.2 hotfix) so the frozen
* player cannot re-blank and the menu stays visible. */
return fd;
}
/* ---- double-buffer handling (v6.6) --------------------------------
* fb0 is double-buffered and the DISPLAYED buffer is chosen by fb_var's
* yoffset (FBIOPAN_DISPLAY flips it). The player page-flips via yoffset (its
* power/screen-off redraw leaves yoffset on the back buffer), so the menu
* MUST be drawn to the buffer the panel actually shows — otherwise the menu
* is invisible and the screen freezes on the player's last frame (the v6.5
* bug). Strategy: snapshot the player's displayed frame into a heap backup,
* pan the display to buffer0 (what the launcher and all apps draw to — apps
* like hello.c mmap exactly one frame at offset 0 and never pan), draw the
* menu/app-list to buffer0, and on session end copy the backup back to the
* player's buffer and pan to the original yoffset so a SIGSTOPped player that
* does NOT redraw (event-driven, ANALYSIS §18.15) still shows its own frame. */
/* Snapshot the player's currently-displayed frame into a heap backup. */
void fb_backup_player(void)
{
if (fb_yres_virtual <= fb_yres) { fb_orig_yoffset = -1; return; } /* single buffer */
if (fb_active_yoffset >= fb_yres_virtual) fb_active_yoffset = 0;
fb_orig_yoffset = (int)fb_active_yoffset;
fb_player_backup_len = (size_t)fb_line * fb_yres;
fb_player_backup = malloc(fb_player_backup_len);
if (!fb_player_backup) {
logmsg("fb backup malloc failed (len=%zu)", fb_player_backup_len);
fb_orig_yoffset = -1;
fb_player_backup_len = 0;
return;
}
memcpy(fb_player_backup, fb_mem + (size_t)fb_active_yoffset * fb_line,
fb_player_backup_len);
logmsg("fb backup: player frame @ yoffset=%u (buffer %u) -> heap",
fb_active_yoffset,
fb_yres ? fb_active_yoffset / fb_yres : 0);
}
/* Pan the display to buffer0 (yoffset=0). buffer0 is what this launcher and
* every app draw to (apps mmap one frame at offset 0 and never pan), so the
* panel must show buffer0 for the menu and apps to be visible. */
void fb_norm_buf0(void)
{
if (fb_fd < 0 || fb_yres_virtual <= fb_yres) { fb_active_yoffset = 0; return; }
if (fb_active_yoffset == 0) return; /* already showing buffer0 */
unsigned char v[160];
memset(v, 0, sizeof v);
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, v) < 0) return;
*(uint32_t *)(v + 16) = 0; /* xoffset */
*(uint32_t *)(v + 20) = 0; /* yoffset -> buffer0 */
ioctl(fb_fd, FBIOPAN_DISPLAY, v);
fb_active_yoffset = 0;
logmsg("fb pan -> buffer0 (yoffset=0)");
}
/* M2.8 Bug 3-residual 根因:fb_norm_buf0 在缓存 fb_active_yoffset==0 时短路提前返回、
* 不再重发 FBIOPAN_DISPLAY。但 FBIOBLANK(4) 下电 -> FBIOBLANK(0) 上电后,LCDC 的扫描
* 地址可能已复位到非 buffer0 / 失效位置,而缓存仍是 0 —— 于是真正的 re-pan 从未发出,
* 唤醒后仍是黑屏/旧帧。本函数无条件重发 pan 到 buffer0(同 yoffset 的重复 pan 是硬件
* no-op,无频闪),供唤醒/重绘路径调用,确保重画的界面一定落在被显示的缓冲。 */
void fb_pan_buf0_force(void)
{
if (fb_fd < 0 || fb_yres_virtual <= fb_yres) { fb_active_yoffset = 0; return; }
unsigned char v[160];
memset(v, 0, sizeof v);
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, v) < 0) return; /* read current vinfo */
*(uint32_t *)(v + 16) = 0; /* xoffset */
*(uint32_t *)(v + 20) = 0; /* yoffset -> buffer0 */
ioctl(fb_fd, FBIOPAN_DISPLAY, v); /* force-issue, even if cached 0 */
fb_active_yoffset = 0;
logmsg("fb force-pan -> buffer0 (yoffset=0)");
}
/* Restore the player's frame to its buffer and pan back to the original
* yoffset. Called at menu/session end (BACK TO PLAYER / SIGTERM) so a frozen
* player that never redraws shows its own frame again. Idempotent. */
void fb_restore_player(void)
{
int restore_y = fb_orig_yoffset;
if (fb_fd >= 0 && restore_y >= 0 && fb_player_backup) {
memcpy(fb_mem + (size_t)restore_y * fb_line, fb_player_backup,
fb_player_backup_len);
unsigned char v[160];
memset(v, 0, sizeof v);
if (ioctl(fb_fd, FBIOGET_VSCREENINFO, v) == 0) {
*(uint32_t *)(v + 16) = 0;
*(uint32_t *)(v + 20) = (uint32_t)restore_y;
if (ioctl(fb_fd, FBIOPAN_DISPLAY, v) < 0)
logmsg("fb restore pan failed: %s", strerror(errno));
}
logmsg("fb restore: player frame -> yoffset=%d (buffer %d)",
restore_y, fb_yres ? restore_y / fb_yres : 0);
}
if (fb_player_backup) { free(fb_player_backup); fb_player_backup = NULL; }
fb_orig_yoffset = -1;
fb_player_backup_len = 0;
}
/* 把当前显示 buffer(buffer0)逐像素压暗(半透明遮罩模拟) */
void dim_screen(void)
{
for (int y = 0; y < fb_yres; y++) {
unsigned char *row = fb + (size_t)y * fb_line;
for (int x = 0; x < fb_xres; x++) {
unsigned char *p = row + x * (fb_bpp / 8);
if (fb_bpp == 16) {
uint16_t c = *(uint16_t *)p;
int r = (((c >> 11) & 0x1F) * 3) / 5;
int g = (((c >> 5) & 0x3F) * 3) / 5;
int b = ((c & 0x1F) * 3) / 5;
*(uint16_t *)p = (uint16_t)((r << 11) | (g << 5) | b);
}
}
}
}
/* A-0:电源菜单背景 = app 当前帧。从 fb0 buffer0(yoffset=0,app 永不 pan)读回 app
* SIGSTOP 后的最后一帧 → back_buf,再经 dim_screen 压暗作菜单背景,替代旧的「压暗
* launcher 应用列表底」(Bug:应用内长按电源,背景错显示成应用列表)。
* 参考 fb_backup_player 的读回思路,但目标是 app 帧而非播放器帧(播放器帧在其自身
* yoffset,app 帧固定 buffer0)。MeowOS 主界面(无 app 运行)时 buffer0 = launcher
* 列表帧,读回即现有「压暗列表底」效果,无回归。 */
void pm_capture_app_frame(void)
{
if (!fb_mem || !back_buf_ok || !fb) return; /* 无真实 fb / 直写模式:维持现状 */
memcpy(fb, fb_mem, (size_t)fb_line * (size_t)fb_yres); /* fb = SDK back_buf */
meow_dirty_full(); /* 新背景整帧呈现 */
logmsg("pm: menu bg = app frame (fb0 buffer0)");
}