Files
maqt-desktop/electron/main.ts

221 lines
7.0 KiB
TypeScript
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.
import { app, BrowserWindow, ipcMain, shell } from 'electron';
import * as path from 'path';
import { spawn, execSync, ChildProcess } from 'child_process';
import * as fs from 'fs';
let mainWindow: BrowserWindow | null = null;
let overlayProcess: ChildProcess | null = null;
let monitorProcess: ChildProcess | null = null;
// --- 工具路径 ---
function getToolsDir(): string {
// 开发模式exe 在项目根目录下的 resources/tools/
if (process.env.VITE_DEV_SERVER_URL) {
const devPath = path.join(app.getAppPath(), 'resources', 'tools');
if (fs.existsSync(devPath)) return devPath;
// 备选:当前目录的 resources/tools/
const cwdPath = path.join(process.cwd(), 'resources', 'tools');
if (fs.existsSync(cwdPath)) return cwdPath;
}
// 生产模式exe 在 resources/tools/ 下
return path.join(process.resourcesPath, 'tools');
}
function exePath(name: string): string {
return path.join(getToolsDir(), name);
}
// --- 窗口 ---
function createWindow() {
mainWindow = new BrowserWindow({
width: 1400,
height: 900,
minWidth: 1100,
minHeight: 700,
frame: false,
transparent: false,
backgroundColor: '#1a1a1a',
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
nodeIntegration: false,
devTools: true,
},
});
// 优先从 VITE_DEV_SERVER_URL 加载,否则尝试 localhost:5173最后用 dist 文件
const devUrl = process.env.VITE_DEV_SERVER_URL || 'http://localhost:5173';
// 尝试加载 dev server
mainWindow.loadURL(devUrl).catch(() => {
console.log('[main] Dev server not available, loading dist...');
mainWindow?.loadFile(path.join(__dirname, '../dist/index.html'));
});
mainWindow.webContents.openDevTools({ mode: 'detach' });
}
// === IPC: 系统优化 ===
ipcMain.handle('optimize:list', async () => {
// 返回所有优化项及其当前状态
return [
{ id: 'hyperv', label: '关闭 Hyper-V', status: 'pending' },
{ id: 'services', label: '系统服务优化', status: 'pending' },
{ id: 'hpet-perf', label: 'HPET 高性能模式', status: 'pending' },
{ id: 'hpet-comp', label: 'HPET 兼容模式', status: 'pending' },
{ id: 'power-plan', label: '卓越性能电源计划', status: 'pending' },
{ id: 'ntfs', label: 'NTFS 优化', status: 'pending' },
{ id: 'memory', label: '内存区域优化', status: 'pending' },
{ id: 'gpu-sched', label: 'GPU 硬件加速调度', status: 'pending' },
{ id: 'game-bar', label: '关闭游戏栏', status: 'pending' },
{ id: 'mouse-accel', label: '关闭鼠标加速', status: 'pending' },
];
});
ipcMain.handle('optimize:item', async (_event, id: string) => {
try {
const h1Exe = exePath('MaqiangTangh1.exe');
if (!fs.existsSync(h1Exe)) {
console.warn(`[optimize] h1.exe not found at ${h1Exe}, using simulation`);
await new Promise(r => setTimeout(r, 1000));
return { success: true, id, action: 'optimized' };
}
execSync(`"${h1Exe}" --optimize "${id}"`, { timeout: 30000 });
return { success: true, id, action: 'optimized' };
} catch (err: any) {
return { success: false, id, error: err.message };
}
});
ipcMain.handle('optimize:restore', async (_event, id: string) => {
try {
const h1Exe = exePath('MaqiangTangh1.exe');
if (!fs.existsSync(h1Exe)) {
await new Promise(r => setTimeout(r, 1000));
return { success: true, id, action: 'restored' };
}
execSync(`"${h1Exe}" --restore "${id}"`, { timeout: 30000 });
return { success: true, id, action: 'restored' };
} catch (err: any) {
return { success: false, id, error: err.message };
}
});
// === IPC: XiXi Overlay ===
ipcMain.handle('overlay:start', async (_event, options?: any) => {
try {
if (overlayProcess) {
overlayProcess.kill();
overlayProcess = null;
}
const overlayExe = exePath('MaqiangTangXiXiOverlay.exe');
if (!fs.existsSync(overlayExe)) {
return { success: false, error: `Overlay exe not found at ${overlayExe}` };
}
const args: string[] = [];
if (options?.gameWindow) args.push('--game-window', options.gameWindow);
if (options?.opacity) args.push('--opacity', String(options.opacity));
overlayProcess = spawn(overlayExe, args, {
cwd: path.dirname(overlayExe),
detached: false,
});
overlayProcess.on('exit', (code) => {
console.log(`[overlay] exited with code ${code}`);
overlayProcess = null;
});
return { success: true, pid: overlayProcess.pid };
} catch (err: any) {
return { success: false, error: err.message };
}
});
ipcMain.handle('overlay:stop', async () => {
if (overlayProcess) {
overlayProcess.kill();
overlayProcess = null;
return { success: true };
}
return { success: false, error: 'Overlay not running' };
});
// === IPC: 硬件监控 ===
ipcMain.handle('monitor:start', async () => {
try {
if (monitorProcess) {
monitorProcess.kill();
monitorProcess = null;
}
const monExe = exePath('MaqiangTangHardwareMonitor.exe');
if (!fs.existsSync(monExe)) {
return { success: false, error: `Monitor exe not found at ${monExe}` };
}
monitorProcess = spawn(monExe, [], {
cwd: path.dirname(monExe),
stdio: ['ignore', 'pipe', 'pipe'],
});
monitorProcess.stdout?.on('data', (data) => {
// 将硬件数据转发到渲染进程
if (mainWindow && !mainWindow.isDestroyed()) {
try {
const sensors = JSON.parse(data.toString());
mainWindow.webContents.send('monitor:data', sensors);
} catch { /* 非 JSON 数据忽略 */ }
}
});
monitorProcess.on('exit', (code) => {
console.log(`[monitor] exited with code ${code}`);
monitorProcess = null;
});
return { success: true, pid: monitorProcess.pid };
} catch (err: any) {
return { success: false, error: err.message };
}
});
ipcMain.handle('monitor:stop', async () => {
if (monitorProcess) {
monitorProcess.kill();
monitorProcess = null;
return { success: true };
}
return { success: false, error: 'Monitor not running' };
});
// === IPC: 窗口控制 ===
ipcMain.handle('window:minimize', () => mainWindow?.minimize());
ipcMain.handle('window:maximize', () => {
if (mainWindow?.isMaximized()) mainWindow.unmaximize();
else mainWindow?.maximize();
});
ipcMain.handle('window:close', () => mainWindow?.close());
// === IPC: 通用 ===
ipcMain.handle('get-app-version', () => app.getVersion());
ipcMain.handle('open-external', async (_event, url: string) => {
await shell.openExternal(url);
});
ipcMain.handle('get-resources-path', () => process.resourcesPath);
ipcMain.handle('fs:exists', (_event, p: string) => fs.existsSync(p));
// === 应用生命周期 ===
app.whenReady().then(createWindow);
app.on('window-all-closed', () => {
// 清理子进程
if (overlayProcess) overlayProcess.kill();
if (monitorProcess) monitorProcess.kill();
if (process.platform !== 'darwin') app.quit();
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) createWindow();
});