439 lines
14 KiB
Rust
439 lines
14 KiB
Rust
//! Deaicup Software — Windows PE64 entry point.
|
||
//!
|
||
//! The original application runs on eframe/egui. For the Deshab UTSM PE
|
||
//! compatibility layer we keep the egui UI logic but replace the eframe
|
||
//! runtime with a minimal hand-written Win32 backend:
|
||
//! user32 window + message pump -> egui RawInput events
|
||
//! egui tessellation -> SoftRaster (pure Rust, raster.rs)
|
||
//! 32bpp DIB backbuffer -> GDI BitBlt onto the window DC
|
||
//!
|
||
//! No CRT: the PE entry is `mainCRTStartup` (see .cargo/config.toml) and the
|
||
//! process ends via ExitProcess.
|
||
|
||
#![no_main]
|
||
#![allow(static_mut_refs)]
|
||
|
||
mod raster;
|
||
#[allow(dead_code)]
|
||
mod rt;
|
||
mod win32;
|
||
|
||
use egui::{self, Color32, Context, Event, Key, Modifiers, PointerButton, Pos2, Rect};
|
||
|
||
use raster::SoftRaster;
|
||
use win32::*;
|
||
|
||
/* ============================================================
|
||
* UI 逻辑(移植自原 eframe 应用 CODE/Deaicup Software/src)
|
||
* ========================================================== */
|
||
|
||
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
||
enum Page {
|
||
#[default]
|
||
Home,
|
||
Tool,
|
||
Game,
|
||
LinuxWare,
|
||
WindowsWare,
|
||
Set,
|
||
About,
|
||
}
|
||
|
||
#[derive(Default)]
|
||
struct DeaicupSoftware {
|
||
current_page: Page,
|
||
}
|
||
|
||
impl DeaicupSoftware {
|
||
fn ui(&mut self, ctx: &Context) {
|
||
// 原应用顶部是远程图片 https://deaicup.com/.../top.png。
|
||
// PE 环境无网络,用标题栏替代,保留 75px 高度布局。
|
||
egui::TopBottomPanel::top("top_image_panel").show(ctx, |ui| {
|
||
ui.set_min_height(75.0);
|
||
ui.vertical_centered(|ui| {
|
||
ui.add_space(15.0);
|
||
ui.label(egui::RichText::new("Deaicup Software").size(30.0).strong());
|
||
});
|
||
});
|
||
|
||
// main
|
||
egui::SidePanel::left("navigation_panel")
|
||
.default_width(200.0)
|
||
.resizable(true)
|
||
.show(ctx, |ui| {
|
||
ui.heading("Menu");
|
||
ui.separator();
|
||
ui.vertical(|ui| {
|
||
if ui.selectable_label(self.current_page == Page::Home, "HOME").clicked() {
|
||
self.current_page = Page::Home;
|
||
}
|
||
if ui.selectable_label(self.current_page == Page::Tool, "TOOL").clicked() {
|
||
self.current_page = Page::Tool;
|
||
}
|
||
if ui.selectable_label(self.current_page == Page::Game, "GAME").clicked() {
|
||
self.current_page = Page::Game;
|
||
}
|
||
if ui
|
||
.selectable_label(self.current_page == Page::LinuxWare, "LINUXWARE")
|
||
.clicked()
|
||
{
|
||
self.current_page = Page::LinuxWare;
|
||
}
|
||
if ui
|
||
.selectable_label(self.current_page == Page::WindowsWare, "WINDOWSWARE")
|
||
.clicked()
|
||
{
|
||
self.current_page = Page::WindowsWare;
|
||
}
|
||
if ui.selectable_label(self.current_page == Page::Set, "SET").clicked() {
|
||
self.current_page = Page::Set;
|
||
}
|
||
if ui
|
||
.selectable_label(self.current_page == Page::About, "ABOUT")
|
||
.clicked()
|
||
{
|
||
self.current_page = Page::About;
|
||
}
|
||
})
|
||
});
|
||
egui::CentralPanel::default().show(ctx, |ui| {
|
||
ui.separator();
|
||
match self.current_page {
|
||
Page::Home => ui.label("Home"),
|
||
Page::Tool => ui.label("Tool"),
|
||
Page::Game => ui.label("Game"),
|
||
Page::LinuxWare => ui.label("LinuxWare"),
|
||
Page::WindowsWare => ui.label("WindowsWare"),
|
||
Page::Set => ui.label("Set"),
|
||
Page::About => ui.label("About"),
|
||
}
|
||
});
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* 全局输入状态(单线程:wnd_proc 只被 DispatchMessageW 同步调用)
|
||
* ========================================================== */
|
||
|
||
static mut EVENTS: Vec<Event> = Vec::new();
|
||
static mut POINTER: Pos2 = Pos2::ZERO;
|
||
static mut MOD_ALT: bool = false;
|
||
static mut MOD_CTRL: bool = false;
|
||
static mut MOD_SHIFT: bool = false;
|
||
static mut QUIT: bool = false;
|
||
|
||
fn current_modifiers() -> Modifiers {
|
||
unsafe {
|
||
Modifiers {
|
||
alt: MOD_ALT,
|
||
ctrl: MOD_CTRL,
|
||
shift: MOD_SHIFT,
|
||
mac_cmd: false,
|
||
command: MOD_CTRL,
|
||
}
|
||
}
|
||
}
|
||
|
||
#[inline]
|
||
fn lparam_x(l: LPARAM) -> i32 {
|
||
(l as i32 & 0xFFFF) as i16 as i32
|
||
}
|
||
#[inline]
|
||
fn lparam_y(l: LPARAM) -> i32 {
|
||
((l as i32 >> 16) & 0xFFFF) as i16 as i32
|
||
}
|
||
|
||
fn push_pointer_button(button: PointerButton, pressed: bool, l: LPARAM) -> LRESULT {
|
||
unsafe {
|
||
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
|
||
EVENTS.push(Event::PointerButton {
|
||
pos: POINTER,
|
||
button,
|
||
pressed,
|
||
modifiers: current_modifiers(),
|
||
});
|
||
}
|
||
0
|
||
}
|
||
|
||
fn vk_to_egui_key(vk: usize) -> Option<Key> {
|
||
// 字符键不需要映射为 egui::Key —— 文本输入走 WM_CHAR -> Event::Text。
|
||
Some(match vk {
|
||
VK_LEFT => Key::ArrowLeft,
|
||
VK_UP => Key::ArrowUp,
|
||
VK_RIGHT => Key::ArrowRight,
|
||
VK_DOWN => Key::ArrowDown,
|
||
VK_BACK => Key::Backspace,
|
||
VK_DELETE => Key::Delete,
|
||
VK_RETURN => Key::Enter,
|
||
VK_TAB => Key::Tab,
|
||
VK_ESCAPE => Key::Escape,
|
||
VK_SPACE => Key::Space,
|
||
_ => return None,
|
||
})
|
||
}
|
||
|
||
fn on_key(vk: usize, pressed: bool) -> LRESULT {
|
||
unsafe {
|
||
match vk {
|
||
VK_SHIFT => MOD_SHIFT = pressed,
|
||
VK_CONTROL => MOD_CTRL = pressed,
|
||
VK_MENU => MOD_ALT = pressed,
|
||
_ => {}
|
||
}
|
||
if let Some(key) = vk_to_egui_key(vk) {
|
||
EVENTS.push(Event::Key {
|
||
key,
|
||
physical_key: None,
|
||
pressed,
|
||
repeat: false,
|
||
modifiers: current_modifiers(),
|
||
});
|
||
}
|
||
}
|
||
0
|
||
}
|
||
|
||
extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w: WPARAM, l: LPARAM) -> LRESULT {
|
||
unsafe {
|
||
match msg {
|
||
WM_CLOSE | WM_DESTROY => {
|
||
QUIT = true;
|
||
PostQuitMessage(0);
|
||
0
|
||
}
|
||
WM_MOUSEMOVE => {
|
||
POINTER = Pos2::new(lparam_x(l) as f32, lparam_y(l) as f32);
|
||
EVENTS.push(Event::PointerMoved(POINTER));
|
||
0
|
||
}
|
||
WM_LBUTTONDOWN => push_pointer_button(PointerButton::Primary, true, l),
|
||
WM_LBUTTONUP => push_pointer_button(PointerButton::Primary, false, l),
|
||
WM_RBUTTONDOWN => push_pointer_button(PointerButton::Secondary, true, l),
|
||
WM_RBUTTONUP => push_pointer_button(PointerButton::Secondary, false, l),
|
||
WM_MBUTTONDOWN => push_pointer_button(PointerButton::Middle, true, l),
|
||
WM_MBUTTONUP => push_pointer_button(PointerButton::Middle, false, l),
|
||
WM_KEYDOWN => on_key(w, true),
|
||
WM_KEYUP => on_key(w, false),
|
||
WM_CHAR => {
|
||
if let Some(c) = char::from_u32(w as u32) {
|
||
if c >= ' ' && c != '\x7f' {
|
||
EVENTS.push(Event::Text(c.to_string()));
|
||
}
|
||
}
|
||
0
|
||
}
|
||
WM_ERASEBKGND => 1, // 背景由 egui 全量绘制
|
||
_ => DefWindowProcW(hwnd, msg, w, l),
|
||
}
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* 计时
|
||
* ========================================================== */
|
||
|
||
fn now_secs() -> f64 {
|
||
unsafe {
|
||
static mut FREQ: i64 = 0;
|
||
if FREQ == 0 {
|
||
QueryPerformanceFrequency(&mut FREQ);
|
||
if FREQ == 0 {
|
||
FREQ = 1000;
|
||
}
|
||
}
|
||
let mut c: i64 = 0;
|
||
QueryPerformanceCounter(&mut c);
|
||
c as f64 / FREQ as f64
|
||
}
|
||
}
|
||
|
||
/* ============================================================
|
||
* PE 入口
|
||
* ========================================================== */
|
||
|
||
/// ExitProcess wrapper: restore the kernel GS base before leaving.
|
||
fn exit_process(code: u32) -> ! {
|
||
unsafe {
|
||
rt::restore_tls();
|
||
ExitProcess(code);
|
||
}
|
||
}
|
||
|
||
#[no_mangle]
|
||
pub extern "C" fn mainCRTStartup() {
|
||
unsafe {
|
||
// Fake TEB/TLS must precede any std facility that might touch
|
||
// thread locals (parking_lot, std::io, etc.).
|
||
rt::install_fake_tls();
|
||
}
|
||
log("[deaicup] mainCRTStartup\n");
|
||
|
||
unsafe {
|
||
let hinst = GetModuleHandleW(core::ptr::null());
|
||
|
||
let class_name = wide("DeaicupWindow");
|
||
let wc = WNDCLASSEXW {
|
||
cbSize: core::mem::size_of::<WNDCLASSEXW>() as u32,
|
||
style: 0,
|
||
lpfnWndProc: wnd_proc,
|
||
cbClsExtra: 0,
|
||
cbWndExtra: 0,
|
||
hInstance: hinst,
|
||
hIcon: 0,
|
||
hCursor: LoadCursorW(0, IDC_ARROW),
|
||
hbrBackground: 0,
|
||
lpszMenuName: core::ptr::null(),
|
||
lpszClassName: class_name.as_ptr(),
|
||
hIconSm: 0,
|
||
};
|
||
RegisterClassExW(&wc);
|
||
|
||
// 屏幕尺寸(shim 报 framebuffer 尺寸),窗口 800x600,超出则收缩
|
||
let sw = GetSystemMetrics(SM_CXSCREEN);
|
||
let sh = GetSystemMetrics(SM_CYSCREEN);
|
||
let mut ww = 800;
|
||
let mut wh = 600;
|
||
if sw > 0 && ww > sw {
|
||
ww = sw;
|
||
}
|
||
if sh > 0 && wh > sh {
|
||
wh = sh;
|
||
}
|
||
|
||
let title = wide("Deaicup Software");
|
||
let hwnd = CreateWindowExW(
|
||
0,
|
||
class_name.as_ptr(),
|
||
title.as_ptr(),
|
||
WS_OVERLAPPEDWINDOW | WS_VISIBLE,
|
||
CW_USEDEFAULT,
|
||
CW_USEDEFAULT,
|
||
ww,
|
||
wh,
|
||
0,
|
||
0,
|
||
hinst,
|
||
core::ptr::null_mut(),
|
||
);
|
||
if hwnd == 0 {
|
||
log("[deaicup] CreateWindowExW failed\n");
|
||
exit_process(1);
|
||
}
|
||
ShowWindow(hwnd, SW_SHOW);
|
||
UpdateWindow(hwnd);
|
||
|
||
// 32bpp top-down DIB 后台缓冲
|
||
let hdc = GetDC(hwnd);
|
||
let memdc = CreateCompatibleDC(hdc);
|
||
let mut bmi = BITMAPINFO::default();
|
||
bmi.bmiHeader.biSize = core::mem::size_of::<BITMAPINFOHEADER>() as u32;
|
||
bmi.bmiHeader.biWidth = ww;
|
||
bmi.bmiHeader.biHeight = -wh; // top-down
|
||
bmi.bmiHeader.biPlanes = 1;
|
||
bmi.bmiHeader.biBitCount = 32;
|
||
bmi.bmiHeader.biCompression = BI_RGB;
|
||
let mut bits: *mut core::ffi::c_void = core::ptr::null_mut();
|
||
let hbmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, &mut bits, 0, 0);
|
||
if hbmp == 0 || bits.is_null() {
|
||
log("[deaicup] CreateDIBSection failed\n");
|
||
exit_process(1);
|
||
}
|
||
SelectObject(memdc, hbmp);
|
||
log("[dc] SelectObject ok\n");
|
||
|
||
/* 二分探针:逐项验证 std 设施,定位 Context::default 崩溃点 */
|
||
{
|
||
let mut v: Vec<u8> = Vec::with_capacity(16);
|
||
for i in 0..64u8 { v.push(i); }
|
||
log("[dc] vec ok\n");
|
||
let s = String::from("hello");
|
||
let _ = s.len();
|
||
log("[dc] string ok\n");
|
||
let mut h: std::collections::HashMap<u32, u32> = std::collections::HashMap::new();
|
||
h.insert(1, 2); h.insert(3, 4);
|
||
let _ = h.get(&1);
|
||
log("[dc] hashmap ok\n");
|
||
let a = std::sync::Arc::new(42u32);
|
||
let b = a.clone();
|
||
let _ = *b;
|
||
log("[dc] arc ok\n");
|
||
let m = std::sync::Mutex::new(7u32);
|
||
*m.lock().unwrap() = 8;
|
||
log("[dc] mutex ok\n");
|
||
}
|
||
|
||
let ctx = Context::default();
|
||
log("[dc] Context::default ok\n");
|
||
ctx.set_pixels_per_point(1.0);
|
||
|
||
let mut app = DeaicupSoftware::default();
|
||
let mut raster = SoftRaster::new();
|
||
let mut msg = MSG::default();
|
||
let mut frame: u64 = 0;
|
||
log("[dc] app+raster ok\n");
|
||
|
||
while !QUIT {
|
||
// 消息泵:把 Win32 消息转成 egui 事件(由 wnd_proc 推入 EVENTS)
|
||
while PeekMessageW(&mut msg, 0, 0, 0, PM_REMOVE) != 0 {
|
||
if msg.message == WM_QUIT {
|
||
QUIT = true;
|
||
break;
|
||
}
|
||
TranslateMessage(&msg);
|
||
DispatchMessageW(&msg);
|
||
}
|
||
if QUIT {
|
||
break;
|
||
}
|
||
if frame < 3 { log("[dc] msgpump ok\n"); }
|
||
|
||
let events = core::mem::take(&mut *core::ptr::addr_of_mut!(EVENTS));
|
||
let had_events = !events.is_empty();
|
||
|
||
let input = egui::RawInput {
|
||
screen_rect: Some(Rect::from_min_size(
|
||
Pos2::ZERO,
|
||
egui::vec2(ww as f32, wh as f32),
|
||
)),
|
||
time: Some(now_secs()),
|
||
modifiers: current_modifiers(),
|
||
events,
|
||
..Default::default()
|
||
};
|
||
if frame < 3 { log("[dc] rawinput ok\n"); }
|
||
|
||
let full = ctx.run(input, |ctx| app.ui(&ctx));
|
||
if frame < 3 { log("[dc] ctx.run ok\n"); }
|
||
let repaint_after = full
|
||
.viewport_output
|
||
.values()
|
||
.next()
|
||
.map(|v| v.repaint_delay)
|
||
.unwrap_or(std::time::Duration::from_millis(16));
|
||
|
||
raster.apply_delta(&full.textures_delta);
|
||
if frame < 3 { log("[dc] apply_delta ok\n"); }
|
||
let prims = ctx.tessellate(full.shapes, full.pixels_per_point);
|
||
if frame < 3 { log("[dc] tessellate ok\n"); }
|
||
|
||
let dst = core::slice::from_raw_parts_mut(bits as *mut u32, (ww * wh) as usize);
|
||
raster::clear(dst, Color32::from_rgb(0x30, 0x34, 0x3d));
|
||
raster.paint(dst, ww, wh, &prims);
|
||
if frame < 3 { log("[dc] paint ok\n"); }
|
||
|
||
BitBlt(hdc, 0, 0, ww, wh, memdc, 0, 0, SRCCOPY);
|
||
if frame < 3 { log("[dc] bitblt ok\n"); }
|
||
|
||
frame += 1;
|
||
if frame > 2 && !had_events && repaint_after > std::time::Duration::from_millis(5) {
|
||
let ms = repaint_after.as_millis().min(50) as u32;
|
||
Sleep(if ms == 0 { 5 } else { ms });
|
||
}
|
||
}
|
||
|
||
log("[deaicup] quit, ExitProcess(0)\n");
|
||
exit_process(0);
|
||
}
|
||
}
|