diff --git a/packages/gui/package.json b/packages/gui/package.json index ae24c9bc6c..6d6a5aeafc 100644 --- a/packages/gui/package.json +++ b/packages/gui/package.json @@ -23,6 +23,8 @@ "dependencies": { "@docmirror/dev-sidecar": "^1.8.8", "@docmirror/mitmproxy": "^1.8.8", + "@mihomo-party/sysproxy": "^2.0.4", + "@natmri/platform-napi": "0.0.7", "adm-zip": "^0.5.5", "ant-design-vue": "^1.6.5", "compressing": "^1.5.1", @@ -52,9 +54,9 @@ "@vue/eslint-config-standard": "^5.1.2", "babel-eslint": "^10.1.0", "electron": "^17.4.11", + "electron-builder": "^23.0.3", "electron-devtools-installer": "^3.1.0", "electron-icon-builder": "^2.0.1", - "electron-builder": "^23.0.3", "eslint": "^6.7.2", "eslint-plugin-import": "^2.20.2", "eslint-plugin-node": "^11.1.0", diff --git a/packages/gui/src/background.js b/packages/gui/src/background.js index 9bab6aea61..2e7da09ea3 100644 --- a/packages/gui/src/background.js +++ b/packages/gui/src/background.js @@ -1,12 +1,15 @@ 'use strict' /* global __static */ import path from 'path' -import { app, protocol, BrowserWindow, Menu, Tray, ipcMain, dialog, powerMonitor, nativeImage, nativeTheme, globalShortcut } from 'electron' +import { app, protocol, BrowserWindow, Menu, Tray, ipcMain, dialog, nativeImage, nativeTheme, globalShortcut } from 'electron' +import { powerMonitor } from './background/powerMonitor' import { createProtocol } from 'vue-cli-plugin-electron-builder/lib' import backend from './bridge/backend' import DevSidecar from '@docmirror/dev-sidecar' import log from './utils/util.log' import minimist from 'minimist' + +const isWindows = process.platform === 'win32' // eslint-disable-next-line no-unused-vars const isMac = process.platform === 'darwin' // import installExtension, { VUEJS_DEVTOOLS } from 'electron-devtools-installer' @@ -187,6 +190,11 @@ function createWindow (startHideWindow) { Menu.setApplicationMenu(null) win.setMenu(null) + // !!IMPORTANT + if (isWindows) { + powerMonitor.setupMainWindow(win) + } + if (process.env.WEBPACK_DEV_SERVER_URL) { // Load the url of the dev server if in development mode win.loadURL(process.env.WEBPACK_DEV_SERVER_URL) @@ -443,8 +451,14 @@ if (!isFirstInstance) { } powerMonitor.on('shutdown', async (e) => { - e.preventDefault() + if (e) { + e.preventDefault() + } log.info('系统关机,恢复代理设置') + if (isWindows) { + const Sysproxy = require('@mihomo-party/sysproxy') + Sysproxy.triggerManualProxy(false, '', 0, '') + } await quit() }) }) diff --git a/packages/gui/src/background/powerMonitor.js b/packages/gui/src/background/powerMonitor.js new file mode 100644 index 0000000000..dd8b9acbcd --- /dev/null +++ b/packages/gui/src/background/powerMonitor.js @@ -0,0 +1,135 @@ +import { powerMonitor as _powerMonitor } from 'electron' +import { setMainWindowHandle, insertWndProcHook, removeWndProcHook, releaseShutdownBlock, acquireShutdownBlock } from '@natmri/platform-napi' + +class PowerMonitor { + constructor () { + this.setup = false + this._listeners = [] + this._shutdownCallback = null + } + + /** + * @param {BrowserWindow} window + */ + setupMainWindow (window) { + if (!this.setup) { + setMainWindowHandle(window.getNativeWindowHandle().readBigInt64LE()) + this.setup = true + } + } + + addListener (event, listener) { + return this.on(event, listener) + } + + removeListener (event, listener) { + return this.off(event, listener) + } + + removeAllListeners (event) { + if (event === 'shutdown' && process.platform === 'win32') { + this._listeners = [] + if (this._shutdownCallback) { + removeWndProcHook() + releaseShutdownBlock() + this._shutdownCallback = null + } + } else { + return _powerMonitor.removeAllListeners(event) + } + } + + on (event, listener) { + if (event === 'shutdown' && process.platform === 'win32') { + if (!this._shutdownCallback) { + this._shutdownCallback = async () => { + await Promise.all(this._listeners.map((fn) => fn())) + releaseShutdownBlock() + } + insertWndProcHook(this._shutdownCallback) + acquireShutdownBlock('正在停止 DevSidecar 代理') + } + this._listeners.push(listener) + } else { + return _powerMonitor.on(event, listener) + } + } + + off (event, listener) { + if (event === 'shutdown' && process.platform === 'win32') { + this._listeners = this._listeners.filter((fn) => fn !== listener) + } else { + return _powerMonitor.off(event, listener) + } + } + + once (event, listener) { + if (event === 'shutdown' && process.platform === 'win32') { + return this.on(event, listener) + } else { + return _powerMonitor.once(event, listener) + } + } + + emit (event, ...args) { + return _powerMonitor.emit(event, ...args) + } + + eventNames () { + return _powerMonitor.eventNames() + } + + getMaxListeners () { + return _powerMonitor.getMaxListeners() + } + + listeners (event) { + return _powerMonitor.listeners(event) + } + + rawListeners (event) { + return _powerMonitor.rawListeners(event) + } + + listenerCount (event, listener) { + return _powerMonitor.listenerCount(event, listener) + } + + /** + * @returns {boolean} + */ + get onBatteryPower () { + return _powerMonitor.onBatteryPower + } + + /** + * @param {number} idleThreshold + * @returns {'active'|'idle'|'locked'|'unknown'} + */ + getSystemIdleState (idleThreshold) { + return _powerMonitor.getSystemIdleState(idleThreshold) + } + + /** + * @returns {number} + */ + getSystemIdleTime () { + return _powerMonitor.getSystemIdleTime() + } + + /** + * @returns {'unknown'|'nominal'|'fair'|'serious'|'critical'} + */ + getCurrentThermalState () { + return _powerMonitor.getCurrentThermalState() + } + + /** + * @returns {boolean} + */ + isOnBatteryPower () { + return _powerMonitor.isOnBatteryPower() + } +} + +export const powerMonitor = new PowerMonitor() diff --git a/packages/gui/vue.config.js b/packages/gui/vue.config.js index 9cdb055f85..816a2658bf 100644 --- a/packages/gui/vue.config.js +++ b/packages/gui/vue.config.js @@ -32,6 +32,27 @@ module.exports = { }, pluginOptions: { electronBuilder: { + externals: [ + '@mihomo-party/sysproxy', + '@mihomo-party/sysproxy-win32-ia32-msvc', + '@mihomo-party/sysproxy-win32-x64-msvc', + '@mihomo-party/sysproxy-win32-arm64-msvc', + '@mihomo-party/sysproxy-linux-x64-gnu', + '@mihomo-party/sysproxy-linux-arm64-gnu', + '@mihomo-party/sysproxy-darwin-x64', + '@mihomo-party/sysproxy-darwin-arm64', + '@natmri/platform-napi', + "@natmri/platform-napi-win32-x64-msvc", + "@natmri/platform-napi-darwin-x64", + "@natmri/platform-napi-linux-x64-gnu", + "@natmri/platform-napi-darwin-arm64", + "@natmri/platform-napi-linux-arm64-gnu", + "@natmri/platform-napi-linux-arm64-musl", + "@natmri/platform-napi-win32-arm64-msvc", + "@natmri/platform-napi-linux-arm-gnueabihf", + "@natmri/platform-napi-linux-x64-musl", + "@natmri/platform-napi-win32-ia32-msvc" + ], nodeIntegration: true, // Provide an array of files that, when changed, will recompile the main process and restart Electron // Your main process file will be added by default