- UTSM panic_full returns in fallback mode (logs [PANIC:FALLBACK] + code + hit count); panic_halt keeps always-halt sites (CPU exception, VMM-E11 LA57, VMM-E15 DSK jump, UTSM-E42 test hook). Call sites given safe continuations. - Mode passed to DSK/FirstInit/login/desktop via DSK_BOOT_FLAG_PANIC_FALLBACK; DKM kernel_api.panic / kern.log.panic return to driver; ext4 fatal aborts the op via setjmp/longjmp and DSK falls back to FAT32 ESP. - build.ps1/Makefile: dev=strict, release=fallback, new realtest (release options + strict); default -Variant all; serialized iso packing under -j. - Fix '*/' inside comments (dkm.h, kernel_api.h, bus.c) that broke the build. - Includes pending work: dkm_bus service bus, mt7920/wlan drivers, desktop files/startmenu/prank, pe-tools, diskinfo, docs. Co-authored-by: Cursor <cursoragent@cursor.com>
525 lines
20 KiB
C
525 lines
20 KiB
C
/* prank.c — Deshab 整蛊系统("致力于打造极差的用户体验")
|
||
*
|
||
* 设计目标:在用户正常使用桌面时,不定时播放一段"恶作剧"叠加层,
|
||
* 制造"这系统是不是坏了"的错觉,随后若无其事地消失。
|
||
*
|
||
* 已实现整蛊(全部由 FUCK [prank] 段控制,见 build/configs/<variant>/FUCK):
|
||
* PK_FUCK — 屏幕压暗,特大 "FUCK" 跟着鼠标走,最后淡出(用户点名要的)
|
||
* PK_BSOD — 假蓝屏(Win 风格 + 哭泣脸 + 伪造 Stop code)
|
||
* PK_INVERT — 全屏反色闪烁
|
||
* PK_HANG — 假"程序未响应"对话框(带等待点点动画)
|
||
* PK_GARBLE — 打字乱码:按键被随机映射到别的字母/数字
|
||
* PK_FLY — 任务栏图标乱飞(任务栏溶解,应用图标四散飞出)
|
||
* PK_SHUTDOWN — 假"系统即将关机"倒计时(数到 0 什么也不发生)
|
||
*
|
||
* 触发方式:
|
||
* 1) 开机进桌面后 boot_demo_sec 秒的"串烧演示"(用于验证效果)
|
||
* 2) 使用中每隔 interval_sec 秒随机播放一个已启用的整蛊
|
||
*
|
||
* 渲染:只往 sprite buffer(g_fb.fb) 上叠画,绝不直接写真实 fb;
|
||
* 由 compositor 在光标之前调用 prank_draw()。运行时用 TSC 计时,
|
||
* 遵守 -mno-sse/-msoft-float,全程整数运算。
|
||
*/
|
||
#include "desktop.h"
|
||
#include "zhfont.h" /* PK_SHUTDOWN 的中文文案 */
|
||
|
||
/* ============================================================
|
||
* 配置(FUCK [prank];extern 声明见 desktop.h)
|
||
* 默认全部打开:无 FUCK 或缺键时也整蛊。FUCK 里写 0 才单独关掉。
|
||
* ============================================================ */
|
||
int g_prank_enabled = 1; /* 总开关,enable=0 关闭 */
|
||
int g_prank_boot_demo = 5; /* 进桌面后演示秒数,0=关 */
|
||
int g_prank_interval = 60; /* 使用中自动整蛊间隔(秒),0=关 */
|
||
int g_prank_fuck = 1; /* FUCK 跟随鼠标遮屏 */
|
||
int g_prank_bsod = 1; /* 假蓝屏 */
|
||
int g_prank_invert = 1; /* 反色闪屏 */
|
||
int g_prank_hang = 1; /* 假"未响应"对话框 */
|
||
int g_prank_garble = 1; /* 打字乱码 */
|
||
int g_prank_fly = 1; /* 任务栏图标乱飞 */
|
||
int g_prank_shutdown = 1; /* 假"系统即将关机"倒计时 */
|
||
|
||
/* ============================================================
|
||
* 运行时状态
|
||
* ============================================================ */
|
||
enum { PK_NONE = 0, PK_FUCK, PK_BSOD, PK_INVERT, PK_HANG,
|
||
PK_GARBLE, PK_FLY, PK_SHUTDOWN };
|
||
|
||
#define PK_KIND_MAX 7
|
||
|
||
#define PK_MS_FUCK 2600u
|
||
#define PK_MS_BSOD 3000u
|
||
#define PK_MS_INVERT 1500u
|
||
#define PK_MS_HANG 2600u
|
||
#define PK_MS_GARBLE 5000u
|
||
#define PK_MS_FLY 2600u
|
||
#define PK_MS_SHUTDOWN 6000u
|
||
|
||
static int s_kind = PK_NONE;
|
||
static u64 s_start = 0;
|
||
static u64 s_dur = 0;
|
||
|
||
static u64 s_next = 0; /* 下次自动整蛊的 TSC 时刻 */
|
||
static int s_armed = 0;
|
||
|
||
static int s_demo = 0; /* 1 = 开机演示串烧进行中 */
|
||
static int s_q[PK_KIND_MAX];
|
||
static int s_qn = 0;
|
||
static int s_qi = 0;
|
||
static int s_demo_ms = 0;
|
||
|
||
static u32 s_rng = 0x12345678u;
|
||
static u32 s_seed = 0; /* 本次整蛊的随机种子(图标飞散用) */
|
||
|
||
/* ============================================================
|
||
* 小工具
|
||
* ============================================================ */
|
||
|
||
static u64 ms_to_tsc(u32 ms) {
|
||
if (g_tsc_per_sec) return g_tsc_per_sec * (u64)ms / 1000u;
|
||
return (u64)ms * 1000000u; /* 未校准兜底(约数) */
|
||
}
|
||
|
||
static unsigned int rng_next(void) {
|
||
s_rng = s_rng * 1664525u + 1013904223u;
|
||
return s_rng;
|
||
}
|
||
|
||
static int key_is(const char *k, int kl, const char *name) {
|
||
int i = 0;
|
||
while (name[i]) i++;
|
||
if (i != kl) return 0;
|
||
for (i = 0; i < kl; i++) if (k[i] != name[i]) return 0;
|
||
return 1;
|
||
}
|
||
|
||
static int parse_int(const char *s, int len) {
|
||
int v = 0, neg = 0, i = 0;
|
||
if (len > 0 && s[0] == '-') { neg = 1; i = 1; }
|
||
for (; i < len && s[i] >= '0' && s[i] <= '9'; i++) v = v * 10 + (s[i] - '0');
|
||
return neg ? -v : v;
|
||
}
|
||
|
||
static u32 kind_ms(int k) {
|
||
switch (k) {
|
||
case PK_FUCK: return PK_MS_FUCK;
|
||
case PK_BSOD: return PK_MS_BSOD;
|
||
case PK_INVERT: return PK_MS_INVERT;
|
||
case PK_HANG: return PK_MS_HANG;
|
||
case PK_GARBLE: return PK_MS_GARBLE;
|
||
case PK_FLY: return PK_MS_FLY;
|
||
case PK_SHUTDOWN: return PK_MS_SHUTDOWN;
|
||
default: return 1000u;
|
||
}
|
||
}
|
||
|
||
static void start_prank(int kind, u32 ms) {
|
||
s_kind = kind;
|
||
s_start = rdtsc();
|
||
s_dur = ms_to_tsc(ms);
|
||
if (s_dur == 0) s_dur = 1;
|
||
s_seed = (u32)rdtsc() ^ (rng_next() << 8);
|
||
}
|
||
|
||
/* ============================================================
|
||
* FUCK [prank] 单行解析("key=value",注释/空行由 main 过滤)
|
||
* ============================================================ */
|
||
void prank_config_line(const char *line, int len) {
|
||
int eq = -1;
|
||
for (int i = 0; i < len; i++) if (line[i] == '=') { eq = i; break; }
|
||
if (eq <= 0) return;
|
||
|
||
const char *k = line;
|
||
int kl = eq;
|
||
const char *v = line + eq + 1;
|
||
int vl = len - eq - 1;
|
||
while (vl > 0 && (v[vl - 1] == ' ' || v[vl - 1] == '\t' || v[vl - 1] == '\r')) vl--;
|
||
int val = parse_int(v, vl);
|
||
|
||
if (key_is(k, kl, "enable")) g_prank_enabled = val ? 1 : 0;
|
||
else if (key_is(k, kl, "boot_demo_sec")) g_prank_boot_demo = val;
|
||
else if (key_is(k, kl, "interval_sec")) g_prank_interval = val;
|
||
else if (key_is(k, kl, "fuck_cursor")) g_prank_fuck = val ? 1 : 0;
|
||
else if (key_is(k, kl, "fake_bsod")) g_prank_bsod = val ? 1 : 0;
|
||
else if (key_is(k, kl, "invert_flash")) g_prank_invert = val ? 1 : 0;
|
||
else if (key_is(k, kl, "fake_hang")) g_prank_hang = val ? 1 : 0;
|
||
else if (key_is(k, kl, "garble_typing")) g_prank_garble = val ? 1 : 0;
|
||
else if (key_is(k, kl, "taskbar_fly")) g_prank_fly = val ? 1 : 0;
|
||
else if (key_is(k, kl, "fake_shutdown")) g_prank_shutdown = val ? 1 : 0;
|
||
}
|
||
|
||
/* ============================================================
|
||
* 调度
|
||
* ============================================================ */
|
||
|
||
/* 收集当前启用的整蛊种类(写入 out,返回个数,≤ PK_KIND_MAX)。 */
|
||
static int collect_kinds(int *out) {
|
||
int n = 0;
|
||
if (g_prank_fuck) out[n++] = PK_FUCK;
|
||
if (g_prank_bsod) out[n++] = PK_BSOD;
|
||
if (g_prank_invert) out[n++] = PK_INVERT;
|
||
if (g_prank_hang) out[n++] = PK_HANG;
|
||
if (g_prank_garble) out[n++] = PK_GARBLE;
|
||
if (g_prank_fly) out[n++] = PK_FLY;
|
||
if (g_prank_shutdown) out[n++] = PK_SHUTDOWN;
|
||
return n;
|
||
}
|
||
|
||
/* 开机进桌面后的串烧演示:把已启用的整蛊平分 boot_demo_sec 秒依次播放。 */
|
||
void prank_boot_demo_start(void) {
|
||
if (!g_prank_enabled || g_prank_boot_demo <= 0) return;
|
||
|
||
s_qn = collect_kinds(s_q);
|
||
if (s_qn == 0) return;
|
||
|
||
u32 each = (u32)g_prank_boot_demo * 1000u / (u32)s_qn;
|
||
if (each < 300u) each = 300u;
|
||
|
||
s_rng ^= (u32)rdtsc();
|
||
s_demo = 1;
|
||
s_qi = 0;
|
||
s_demo_ms = (int)each;
|
||
s_armed = 0;
|
||
start_prank(s_q[0], each);
|
||
slog("[prank] boot demo start");
|
||
}
|
||
|
||
void prank_tick(void) {
|
||
if (!g_prank_enabled) {
|
||
s_kind = PK_NONE;
|
||
s_demo = 0;
|
||
s_armed = 0;
|
||
return;
|
||
}
|
||
if (!g_tsc_per_sec) return; /* TSC 未校准前不动 */
|
||
|
||
u64 now = rdtsc();
|
||
|
||
if (s_kind != PK_NONE) {
|
||
if (now - s_start >= s_dur) {
|
||
s_kind = PK_NONE;
|
||
if (s_demo) {
|
||
s_qi++;
|
||
if (s_qi < s_qn) start_prank(s_q[s_qi], (u32)s_demo_ms);
|
||
else { s_demo = 0; s_armed = 0; slog("[prank] boot demo done"); }
|
||
} else {
|
||
s_armed = 0;
|
||
}
|
||
}
|
||
return;
|
||
}
|
||
|
||
if (s_demo) return;
|
||
if (g_prank_interval <= 0) return;
|
||
|
||
if (!s_armed) {
|
||
s_next = now + ms_to_tsc((u32)g_prank_interval * 1000u);
|
||
s_armed = 1;
|
||
return;
|
||
}
|
||
if (now < s_next) return;
|
||
|
||
int cand[PK_KIND_MAX];
|
||
int n = collect_kinds(cand);
|
||
if (n == 0) { s_armed = 0; return; }
|
||
|
||
int k = cand[(rng_next() >> 16) % (u32)n];
|
||
start_prank(k, kind_ms(k));
|
||
s_armed = 0;
|
||
}
|
||
|
||
int prank_active(void) { return s_kind != PK_NONE; }
|
||
|
||
/* 是否需要在整蛊期间强制全屏重绘(打字乱码纯作用于输入,无需重绘)。 */
|
||
int prank_wants_redraw(void) {
|
||
return s_kind != PK_NONE && s_kind != PK_GARBLE;
|
||
}
|
||
|
||
/* 打字乱码:把可打印键的按下码随机映射到另一个可打印码。
|
||
* 释放码与特殊键(Esc/回车/退格/Tab/方向键/修饰键/E0 前缀)原样放行。 */
|
||
u8 prank_filter_key(u8 sc) {
|
||
if (s_kind != PK_GARBLE) return sc;
|
||
if (sc & 0x80) return sc; /* 释放码不动 */
|
||
|
||
static const u8 keys[] = {
|
||
0x02,0x03,0x04,0x05,0x06,0x07,0x08,0x09,0x0A,0x0B, /* 1..0 */
|
||
0x10,0x11,0x12,0x13,0x14,0x15,0x16,0x17,0x18,0x19, /* qwertyuiop */
|
||
0x1E,0x1F,0x20,0x21,0x22,0x23,0x24,0x25,0x26, /* asdfghjkl */
|
||
0x2C,0x2D,0x2E,0x2F,0x30,0x31,0x32, /* zxcvbnm */
|
||
0x39 /* space */
|
||
};
|
||
int n = (int)(sizeof(keys) / sizeof(keys[0]));
|
||
int idx = -1;
|
||
for (int i = 0; i < n; i++) if (keys[i] == sc) { idx = i; break; }
|
||
if (idx < 0) return sc; /* 非可打印键原样 */
|
||
|
||
return keys[rng_next() % (unsigned int)n];
|
||
}
|
||
|
||
/* ============================================================
|
||
* 绘制原语(全部只作用于 sprite buffer)
|
||
* ============================================================ */
|
||
|
||
/* 全屏压暗:每通道 * (256-a)/256 */
|
||
static void blend_dark(du_context *ctx, u32 a) {
|
||
if (a == 0) return;
|
||
if (a > 255) a = 255;
|
||
u32 ia = 256u - a;
|
||
for (u64 y = 0; y < ctx->height; y++) {
|
||
u32 *line = (u32 *)((u8 *)ctx->fb + y * ctx->pitch);
|
||
for (u64 x = 0; x < ctx->width; x++) {
|
||
u32 c = line[x];
|
||
u32 r = ((c & 0xFF) * ia) >> 8;
|
||
u32 g = (((c >> 8) & 0xFF) * ia) >> 8;
|
||
u32 b = (((c >> 16) & 0xFF) * ia) >> 8;
|
||
line[x] = 0xFF000000u | (b << 16) | (g << 8) | r;
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 全屏反色(保留 alpha) */
|
||
static void invert_all(du_context *ctx) {
|
||
for (u64 y = 0; y < ctx->height; y++) {
|
||
u32 *line = (u32 *)((u8 *)ctx->fb + y * ctx->pitch);
|
||
for (u64 x = 0; x < ctx->width; x++) line[x] ^= 0x00FFFFFFu;
|
||
}
|
||
}
|
||
|
||
/* 放大版 ASCII 字符串:以 (cx,cy) 为中心,每字形像素放大 scale 倍。
|
||
* alpha 0..255 控制整体不透明度。 */
|
||
static void draw_scale_text(du_context *ctx, const char *s, int cx, int cy,
|
||
int scale, u32 fg, int alpha) {
|
||
if (scale < 1) scale = 1;
|
||
if (alpha < 0) alpha = 0;
|
||
if (alpha > 255) alpha = 255;
|
||
if (alpha == 0) return;
|
||
|
||
int n = kstrlen(s);
|
||
if (n <= 0) return;
|
||
int gw = g_ascii_w, gh = g_ascii_h;
|
||
int step = (gw + 1) * scale;
|
||
int total_w = n * step - scale;
|
||
int x0 = cx - total_w / 2;
|
||
int y0 = cy - gh * scale / 2;
|
||
|
||
for (int i = 0; i < n; i++) {
|
||
unsigned char ch = (unsigned char)s[i];
|
||
if (ch < 32 || ch > 126) continue;
|
||
const unsigned char *g = g_ascii[ch - 32];
|
||
int gx = x0 + i * step;
|
||
for (int r = 0; r < gh; r++) {
|
||
for (int c = 0; c < gw; c++) {
|
||
unsigned char ga = g[r * gw + c];
|
||
if (!ga) continue;
|
||
int aa = (int)ga * alpha / 255;
|
||
if (aa <= 0) continue;
|
||
for (int sy = 0; sy < scale; sy++) {
|
||
int py = y0 + r * scale + sy;
|
||
if (py < 0 || (u64)py >= ctx->height) continue;
|
||
u32 *line = (u32 *)((u8 *)ctx->fb + (u64)py * ctx->pitch);
|
||
for (int sx = 0; sx < scale; sx++) {
|
||
int px = gx + c * scale + sx;
|
||
if (px < 0 || (u64)px >= ctx->width) continue;
|
||
line[px] = du_blend(line[px], fg, (u32)aa);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* 各整蛊的绘制
|
||
* ============================================================ */
|
||
|
||
/* PK_FUCK — 屏幕压暗 + 特大 "FUCK" 跟着鼠标 + 淡出 */
|
||
static void draw_fuck(du_context *ctx, int pct) {
|
||
int a;
|
||
if (pct < 15) a = pct * 170 / 15; /* 淡入压暗 */
|
||
else if (pct > 85) a = (100 - pct) * 170 / 15; /* 淡出解除 */
|
||
else a = 170;
|
||
if (a < 0) a = 0;
|
||
blend_dark(ctx, (u32)a);
|
||
|
||
int alpha = 255;
|
||
if (pct < 10) alpha = pct * 255 / 10;
|
||
else if (pct > 85) alpha = (100 - pct) * 255 / 15;
|
||
if (alpha < 0) alpha = 0;
|
||
|
||
/* 阴影 + 主体:中心跟随鼠标 */
|
||
draw_scale_text(ctx, "FUCK", g_mouse_x + 7, g_mouse_y + 7, 8, 0xFF000000u, alpha / 2);
|
||
draw_scale_text(ctx, "FUCK", g_mouse_x, g_mouse_y, 8, 0xFFFF1744u, alpha);
|
||
}
|
||
|
||
/* PK_BSOD — 假蓝屏 */
|
||
static void draw_bsod(du_context *ctx, int pct) {
|
||
int W = (int)ctx->width, H = (int)ctx->height;
|
||
du_fill_bg_solid(ctx, 0xFF0078D7u);
|
||
|
||
int x = W / 8; if (x < 80) x = 80;
|
||
int y = H / 2 - 150;
|
||
if (y < 40) y = 40;
|
||
|
||
draw_scale_text(ctx, ":(", x + 90, y + 40, 5, 0xFFFFFFFFu, 255);
|
||
|
||
const char *l1 = "Your Deshab ran into a problem and needs to restart.";
|
||
const char *l2 = "We're just collecting some error info, and then";
|
||
const char *l3 = "we'll restart for you.";
|
||
int ty = y + 110;
|
||
du_draw_string(ctx, l1, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 34;
|
||
du_draw_string(ctx, l2, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 22;
|
||
du_draw_string(ctx, l3, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 50;
|
||
|
||
/* "(NN% complete)" */
|
||
int n = pct; if (n < 0) n = 0; if (n > 100) n = 100;
|
||
char pb[40]; int i = 0;
|
||
pb[i++] = '(';
|
||
if (n >= 100) { pb[i++] = '1'; pb[i++] = '0'; pb[i++] = '0'; }
|
||
else { pb[i++] = (char)('0' + n / 10); pb[i++] = (char)('0' + n % 10); }
|
||
const char *suf = "% complete)";
|
||
while (*suf) pb[i++] = *suf++;
|
||
pb[i] = 0;
|
||
du_draw_string(ctx, pb, x, ty, 0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP); ty += 44;
|
||
|
||
du_draw_string(ctx, "Stop code: FUCKED_BY_DESIGN", x, ty,
|
||
0xFFFFFFFFu, 0xFF0078D7u, DU_ASCII_STEP);
|
||
}
|
||
|
||
/* PK_INVERT — 全屏反色 */
|
||
static void draw_invert(du_context *ctx) {
|
||
invert_all(ctx);
|
||
}
|
||
|
||
/* PK_HANG — 假"程序未响应"对话框 */
|
||
static void draw_hang(du_context *ctx, int pct) {
|
||
int W = (int)ctx->width, H = (int)ctx->height;
|
||
blend_dark(ctx, 110);
|
||
|
||
int w = 520, h = 200;
|
||
int x = (W - w) / 2, y = (H - h) / 2;
|
||
|
||
/* 阴影 + 卡片 */
|
||
du_fill_rect(ctx, x + 8, y + 8, w, h, 0xFF6B7280u);
|
||
du_fill_rect(ctx, x, y, w, h, 0xFFF3F4F6u);
|
||
du_rect_outline(ctx, x, y, w, h, 0xFF9CA3AFu, 0);
|
||
|
||
/* 标题栏 */
|
||
du_fill_rect(ctx, x, y, w, 34, 0xFFE5E7EBu);
|
||
du_draw_string(ctx, "Deshab - Not Responding", x + 12, y + 8,
|
||
0xFF111827u, 0xFFE5E7EBu, DU_ASCII_STEP);
|
||
|
||
/* 正文 */
|
||
du_draw_string(ctx, "This program is not responding.", x + 20, y + 56,
|
||
0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP);
|
||
du_draw_string(ctx, "It may respond if you wait a bit.", x + 20, y + 80,
|
||
0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP);
|
||
du_draw_string(ctx, "Do you want to close it?", x + 20, y + 104,
|
||
0xFF1F2937u, 0xFFF3F4F6u, DU_ASCII_STEP);
|
||
|
||
/* 等待点点动画(0..3 个点循环) */
|
||
int dots = (pct / 12) % 4;
|
||
char db[8]; db[0] = '.'; db[1] = '.'; db[2] = '.'; db[3] = '.'; db[4] = 0;
|
||
db[dots] = 0;
|
||
du_draw_string(ctx, "Waiting", x + 20, y + 138, 0xFF6B7280u, 0xFFF3F4F6u, DU_ASCII_STEP);
|
||
du_draw_string(ctx, db, x + 20 + 7 * (int)DU_ASCII_STEP, y + 138,
|
||
0xFF6B7280u, 0xFFF3F4F6u, DU_ASCII_STEP);
|
||
|
||
/* 按钮 */
|
||
int bw = 120, bh = 34, by = y + h - 54;
|
||
int bx2 = x + w - 20 - bw, bx1 = bx2 - bw - 12;
|
||
|
||
du_fill_rect(ctx, bx1, by, bw, bh, 0xFFE5E7EBu);
|
||
du_rect_outline(ctx, bx1, by, bw, bh, 0xFF9CA3AFu, 0);
|
||
du_draw_string(ctx, "Wait", bx1 + (bw - 4 * (int)DU_ASCII_STEP) / 2, by + 8,
|
||
0xFF111827u, 0xFFE5E7EBu, DU_ASCII_STEP);
|
||
|
||
du_fill_rect(ctx, bx2, by, bw, bh, 0xFF2563EBu);
|
||
du_draw_string(ctx, "Close", bx2 + (bw - 5 * (int)DU_ASCII_STEP) / 2, by + 8,
|
||
0xFFFFFFFFu, 0xFF2563EBu, DU_ASCII_STEP);
|
||
}
|
||
|
||
/* PK_FLY — 任务栏图标乱飞:底部任务栏"溶解",应用图标四散飞出屏幕 */
|
||
static void draw_fly(du_context *ctx, int pct) {
|
||
int W = (int)ctx->width, H = (int)ctx->height;
|
||
blend_dark(ctx, 90);
|
||
|
||
/* 任务栏条带溶解为深色(视觉上任务栏没了) */
|
||
int strip_h = 72;
|
||
int sy = H - strip_h; if (sy < 0) sy = 0;
|
||
du_fill_rect(ctx, 0, sy, W, strip_h, 0xFF14161Au);
|
||
|
||
/* 应用图标从任务栏位置四散飞出 */
|
||
int n = g_app_count; if (n < 6) n = 6; if (n > 8) n = 8;
|
||
int base_y = H - 40;
|
||
for (int i = 0; i < n; i++) {
|
||
u32 r = s_seed ^ ((u32)i * 2654435761u);
|
||
int vx = (int)(r & 0xFFu) - 128; /* -128..127 */
|
||
vx = vx * 3; /* -384..381 px */
|
||
int vy = -(int)(((r >> 8) & 0xFFu) * 3u + 120u); /* 向上飞 */
|
||
int start_x = W / 2 + (i - n / 2) * 56;
|
||
int x = start_x + vx * pct / 100;
|
||
int y = base_y + vy * pct / 100;
|
||
int appid = g_app_count > 0 ? (i % g_app_count) : 0;
|
||
du_fill_rect(ctx, x + 3, y + 3, 40, 40, 0xFF000000u); /* 阴影 */
|
||
draw_app_icon(ctx, appid, x, y);
|
||
}
|
||
}
|
||
|
||
/* PK_SHUTDOWN — 假"系统即将关机"倒计时 */
|
||
static void draw_shutdown(du_context *ctx, int pct) {
|
||
int W = (int)ctx->width, H = (int)ctx->height;
|
||
du_fill_bg_solid(ctx, 0xFF0A0E14u);
|
||
|
||
int remain = 6 - (pct * 6 / 100);
|
||
if (remain < 0) remain = 0;
|
||
|
||
/* 中文标题(16px 位图字库;24px 未懒加载前不使用) */
|
||
const char *zh_title = "系统即将关机";
|
||
int zw = zh_string_width(zh_title, 0);
|
||
zh_draw_string(ctx, zh_title, (W - zw) / 2, H / 2 - 130, 0xFFFFD24Au, 0);
|
||
|
||
/* 大号倒计时数字 */
|
||
char num[4];
|
||
num[0] = (char)('0' + remain);
|
||
num[1] = 0;
|
||
draw_scale_text(ctx, num, W / 2, H / 2 + 10, 10, 0xFFFFFFFFu, 255);
|
||
|
||
/* 中英提示 */
|
||
const char *zh_sub = "请立即保存您的工作";
|
||
int zw2 = zh_string_width(zh_sub, 0);
|
||
zh_draw_string(ctx, zh_sub, (W - zw2) / 2, H / 2 + 120, 0xFFB0B6C0u, 0);
|
||
|
||
const char *en = "System will shut down automatically.";
|
||
int ew = kstrlen(en) * (int)DU_ASCII_STEP;
|
||
du_draw_string(ctx, en, (W - ew) / 2, H / 2 + 152,
|
||
0xFF9AA0A8u, 0xFF0A0E14u, DU_ASCII_STEP);
|
||
|
||
/* 底部进度条(时间越少越红) */
|
||
int bw = W / 3, bx = (W - bw) / 2, byy = H - 80;
|
||
du_fill_rect(ctx, bx, byy, bw, 8, 0xFF2A2F38u);
|
||
int fw = bw * (100 - pct) / 100;
|
||
if (fw > 0) du_fill_rect(ctx, bx, byy, fw, 8, 0xFFE5484Du);
|
||
}
|
||
|
||
/* ============================================================
|
||
* 对外绘制入口(compositor 在光标之前调用)
|
||
* ============================================================ */
|
||
void prank_draw(du_context *ctx) {
|
||
if (s_kind == PK_NONE) return;
|
||
|
||
u64 el = rdtsc() - s_start;
|
||
int pct = (int)(el * 100u / (s_dur ? s_dur : 1u));
|
||
if (pct < 0) pct = 0;
|
||
if (pct > 100) pct = 100;
|
||
|
||
switch (s_kind) {
|
||
case PK_FUCK: draw_fuck(ctx, pct); break;
|
||
case PK_BSOD: draw_bsod(ctx, pct); break;
|
||
case PK_INVERT: draw_invert(ctx); break;
|
||
case PK_HANG: draw_hang(ctx, pct); break;
|
||
case PK_GARBLE: break; /* 纯输入效果,无叠加层 */
|
||
case PK_FLY: draw_fly(ctx, pct); break;
|
||
case PK_SHUTDOWN: draw_shutdown(ctx, pct); break;
|
||
default: break;
|
||
}
|
||
}
|