const { app, BrowserWindow, Tray, Menu, nativeImage, ipcMain } = require('electron') const path = require('path') const AutoLaunch = require('auto-launch') const si = require('systeminformation') // 保持对窗口对象的全局引用,如果不这样做,窗口将会在JavaScript垃圾回收时自动关闭 let mainWindow let tray = null // 创建开机启动管理器 let autoLauncher = new AutoLaunch({ name: 'Motioner', path: process.execPath, }) // 跟踪开机启动状态 let isAutoLaunchEnabled = false // 存储GPU监控定时器 let gpuMonitorInterval = null // 创建窗口的函数 function createWindow() { // 创建浏览器窗口 mainWindow = new BrowserWindow({ width: 350, height: 180, webPreferences: { nodeIntegration: true, contextIsolation: false, }, resizable: false, // 创建无边框窗口 frame: false, // 保持窗口始终在最前面 alwaysOnTop: true, // 窗口透明 transparent: false, backgroundMaterial: 'mica', // 隐藏任务栏图标 skipTaskbar: true, // 隐藏窗口切换时的显示 show: false, // 隐藏窗口标题栏和任务栏显示 titleBarStyle: 'hidden', // 隐藏窗口在Alt+Tab切换中显示 hiddenInMissionControl: true, }) // 加载应用的index.html mainWindow.loadFile('index.html') // 处理最小化事件 ipcMain.on('window-minimize', () => { mainWindow.hide() }) // 处理关闭事件 ipcMain.on('window-close', () => { app.quit() }) // 处理开机启动设置事件 ipcMain.on('set-auto-launch', (event, enable) => { if (enable) { autoLauncher .enable() .then(() => { console.log('开机启动已启用') isAutoLaunchEnabled = true // 重新创建托盘菜单以更新状态 createTray() }) .catch(err => { console.error('启用开机启动失败:', err) }) } else { autoLauncher .disable() .then(() => { console.log('开机启动已禁用') isAutoLaunchEnabled = false // 重新创建托盘菜单以更新状态 createTray() }) .catch(err => { console.error('禁用开机启动失败:', err) }) } }) // 当窗口关闭时触发 mainWindow.on('closed', function () { // 取消对窗口对象的引用,通常会存储窗口在数组中,这是删除相应元素的时候 mainWindow = null // 清除GPU监控定时器 if (gpuMonitorInterval) { clearInterval(gpuMonitorInterval) gpuMonitorInterval = null } }) } // 保存窗口原始位置和大小 let originalBounds = { width: 350, height: 150 } // 当Electron完成初始化并准备创建浏览器窗口时调用此方法 app.whenReady().then(() => { createWindow() // 创建系统托盘 createTray() // 启动GPU监控 startGpuMonitoring() // 显示窗口 mainWindow.show() // 监听窗口失焦事件 mainWindow.on('blur', () => { // 2秒后缩小窗口 setTimeout(() => { if (mainWindow && !mainWindow.isDestroyed()) { // 保存当前窗口位置和大小 const bounds = mainWindow.getBounds() originalBounds = { ...bounds } // 缩小窗口到小球大小,以顶部中心点为基准 const newX = Math.round(bounds.x + bounds.width / 2 - 20) const newY = bounds.y try { mainWindow.setBounds({ x: newX, y: newY, width: 150, height: 210, }) // 移除backgroundMaterial带来的阴影 mainWindow.setBackgroundMaterial('none') mainWindow.setOpacity(0.3) } catch (error) { console.error('设置窗口边界时出错:', error) } } }, 1000) // 通知渲染进程窗口已失焦 if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('window-blurred') } }) // 监听窗口聚焦事件 mainWindow.on('focus', () => { // 恢复窗口到原始大小和位置 if (mainWindow && !mainWindow.isDestroyed()) { try { mainWindow.setBounds({ x: originalBounds.x, y: originalBounds.y, width: originalBounds.width, height: originalBounds.height, }) // 恢复backgroundMaterial带来的阴影 mainWindow.setBackgroundMaterial('mica') mainWindow.setOpacity(1) } catch (error) { console.error('设置窗口边界时出错:', error) } } // 通知渲染进程窗口已聚焦 if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('window-focused') } }) app.on('activate', function () { // 在macOS上,当单击dock图标并且没有其他窗口打开时,通常在应用程序中重新创建一个窗口 if (BrowserWindow.getAllWindows().length === 0) createWindow() }) }) // 当所有窗口都关闭时退出应用 app.on('window-all-closed', function () { // 在macOS上,应用程序及其菜单栏通常会保持活动状态,直到用户明确退出 if (process.platform !== 'darwin') app.quit() }) // 创建系统托盘 function createTray() { // 创建托盘图标 const iconPath = path.join(__dirname, 'assets/icon.png') let icon try { icon = nativeImage.createFromPath(iconPath) } catch (error) { // 如果找不到图标文件,使用默认图标 icon = nativeImage.createEmpty() } tray = new Tray(icon) // 创建上下文菜单 const contextMenu = Menu.buildFromTemplate([ { label: '显示', click: () => { mainWindow.show() }, }, { label: '开机启动', type: 'checkbox', checked: isAutoLaunchEnabled, click: () => { if (isAutoLaunchEnabled) { autoLauncher .disable() .then(() => { console.log('开机启动已禁用') isAutoLaunchEnabled = false // 重新创建托盘菜单以更新状态 createTray() }) .catch(err => { console.error('禁用开机启动失败:', err) }) } else { autoLauncher .enable() .then(() => { console.log('开机启动已启用') isAutoLaunchEnabled = true // 重新创建托盘菜单以更新状态 createTray() }) .catch(err => { console.error('启用开机启动失败:', err) }) } }, }, { label: '退出', click: () => { app.quit() }, }, ]) tray.setContextMenu(contextMenu) // 点击托盘图标显示窗口 tray.on('click', () => { mainWindow.show() }) } // 启动GPU监控 function startGpuMonitoring() { // 每5秒获取一次GPU信息,减少性能开销 gpuMonitorInterval = setInterval(async () => { try { // 尝试使用nvidia-smi命令获取GPU信息 const { exec } = require('child_process') exec('nvidia-smi --query-gpu=utilization.gpu,memory.used,memory.total --format=csv,noheader,nounits', (error, stdout, stderr) => { if (error) { console.error('执行nvidia-smi时出错:', error) return } if (stderr) { console.error('nvidia-smi stderr:', stderr) return } // 解析输出 const data = stdout .trim() .split(',') .map(item => parseInt(item.trim())) if (data.length >= 3) { const gpuInfo = [ { name: 'NVIDIA GPU', utilizationGpu: data[0], memoryUsed: data[1], memoryTotal: data[2], vram: data[2], }, ] // 发送GPU信息到渲染进程 if (mainWindow && !mainWindow.isDestroyed()) { mainWindow.webContents.send('gpu-info', gpuInfo) } } }) } catch (error) { console.error('获取GPU信息时出错:', error) } }, 200) // 每5秒更新一次 }