- 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
223 lines
11 KiB
C
223 lines
11 KiB
C
/* 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)");
|
||
}
|
||
|