main.js 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. const { app, BrowserWindow, Menu, ipcMain, globalShortcut, dialog, screen, Tray } = require('electron');
  2. const { join } = require('path');
  3. const { fork } = require('child_process');
  4. const { platform } = require('process');
  5. const mqtt = require('mqtt')
  6. const sleep = (ms) => new Promise(res => setTimeout(res, ms));
  7. class MainSerivce {
  8. #mqttChannel = null
  9. #client = null
  10. #isMin = 1
  11. constructor() {
  12. Menu.setApplicationMenu(null) // 去掉菜单栏
  13. app.commandLine.appendSwitch('wm-window-animations-disabled') // 拖动闪屏
  14. app.commandLine.appendSwitch('autoplay-policy', 'no-user-gesture-required');
  15. this.loadingWin = null
  16. this.mainWin = null
  17. this.icon = join(__dirname, './icon/playGame.png')
  18. this.contrlEvent = null
  19. if (!app.requestSingleInstanceLock({ key: 'contrl' })) {
  20. app.quit()
  21. } else {
  22. app.on('ready', this.onRead.bind(this))
  23. app.on('activate', (e, isVisible) => {
  24. if (!isVisible && this.mainWin) {
  25. // 兼容Mac dock 栏点击
  26. this.mainWin.show()
  27. }
  28. })
  29. app.on('window-all-closed', () => app.quit())
  30. }
  31. }
  32. createLoading() {
  33. const { size: { width, height } } = screen.getPrimaryDisplay()
  34. this.loadingWin = new BrowserWindow({
  35. frame: false, // 无边框(窗口、工具栏等),只包含网页内容
  36. width,
  37. height,
  38. resizable: false,
  39. center: true,
  40. icon: this.icon,
  41. alwaysOnTop: true,
  42. transparent: true // 窗口是否支持透明,如果想做高级效果最好为true
  43. })
  44. if (app.isPackaged) {
  45. this.loadingWin.loadFile(join(__dirname, './loading.html'))
  46. } else {
  47. this.loadingWin.loadFile('loading.html')
  48. }
  49. this.loadingWin.on('close', () => {
  50. this.loadingWin = null
  51. })
  52. }
  53. createWindow() {
  54. this.mainWin = new BrowserWindow({
  55. minWidth: 1300,
  56. minHeight: 760,
  57. width: 1300,
  58. height: 760,
  59. frame: false,
  60. transparent: true,
  61. icon: this.icon,
  62. webPreferences: {
  63. contextIsolation: true,
  64. nodeIntegration: true,
  65. webSecurity: false, // 去掉跨越
  66. nodeIntegrationInWorker: true,
  67. preload: join(__dirname, './preload.js')
  68. },
  69. show: false
  70. })// 创建一个窗口
  71. // 不同环境加载不同文件
  72. if (app.isPackaged) {
  73. this.mainWin.loadFile(join(__dirname, './index.html'))
  74. } else {
  75. this.mainWin.loadURL('http://localhost:6547/')
  76. }
  77. // 事件监听
  78. this.mainWin.on('close', () => { this.mainWin = null })
  79. }
  80. onRead() {
  81. this.createLoading()
  82. this.createWindow()
  83. // 图标
  84. const tray = new Tray(this.icon)
  85. const contextMenu = Menu.buildFromTemplate([
  86. {
  87. label: '退出',
  88. click: () => {
  89. this.mainWin.close()
  90. app.quit()
  91. }
  92. }
  93. ])
  94. tray.setToolTip('控制端')
  95. tray.on('click', () => this.mainWin.show())
  96. // 注册调试模式
  97. globalShortcut.register('Ctrl+F12', () => {
  98. this.mainWin.webContents.toggleDevTools()
  99. })
  100. // 最小化
  101. globalShortcut.register('Ctrl+B', () => {
  102. if (this.#isMin & 1) { this.mainWin.hide() } else {
  103. this.mainWin.show()
  104. }
  105. this.#isMin++
  106. })
  107. // 系统环境
  108. if (platform === 'win32') {
  109. // 右键
  110. tray.setContextMenu(contextMenu)
  111. // 禁用右键
  112. this.mainWin.hookWindowMessage(278, () => {
  113. this.mainWin.setEnabled(false);//窗口禁用
  114. setTimeout(() => {
  115. this.mainWin.setEnabled(true);
  116. }, 100) //延时太快会立刻启动,太慢会妨碍窗口其他操作,可自行测试最佳时间
  117. return true
  118. })
  119. } else if (platform === 'darwin') {
  120. app.dock.setIcon(join(__dirname, './icon/playGame@2x.png'))
  121. }
  122. // 通信
  123. ipcMain.on('signal', (_, evt, data) => {
  124. if (evt === 'close-loading') {
  125. if (this.loadingWin) this.loadingWin.close()
  126. this.mainWin.show()
  127. // if (this.mainWin.isVisible()) this.connectLogi()
  128. } else if (evt === 'minWin') {
  129. this.mainWin.minimize()
  130. } else if (evt === 'closeWin') {
  131. this.#closeMqtt()
  132. this.mainWin.close()
  133. } else if (evt === 'maxWin') {
  134. if (data) { this.mainWin.maximize() } else {
  135. this.mainWin.setBounds({ width: 1300, height: 760 })
  136. this.mainWin.center()
  137. }
  138. } else if (evt === 'loginMqtt') {
  139. this.#initMqtt(data)
  140. } else if (evt === 'closeMqtt') {
  141. this.#closeMqtt()
  142. } else if (evt === 'sendMqtt') {
  143. try {
  144. const db = JSON.stringify(data)
  145. if (this.#client.connected && this.#mqttChannel) this.#client.publish(this.#mqttChannel, db)
  146. } catch (error) {
  147. console.log(error);
  148. }
  149. }
  150. })
  151. }
  152. #initMqtt(data) {
  153. this.#closeMqtt()
  154. try {
  155. this.#client = mqtt.connect(data.url, { username: data.room, password: data.name })
  156. // 监听
  157. this.#client.on('connect', () => this.mainWin.webContents.send('message', { type: 'connect' }))
  158. this.#client.on('message', (_, msg, __) => {
  159. try {
  160. const db = JSON.parse(msg.toString())
  161. if (db.type === 'join') this.#mqttChannel = db.channel
  162. this.mainWin.webContents.send('message', db)
  163. } catch (error) {
  164. console.log(error);
  165. }
  166. })
  167. this.#client.on('error', (e) => { this.mainWin.webContents.send('message', { type: 'disconnect' }); this.#closeMqtt() })
  168. } catch (error) {
  169. this.mainWin.webContents.send('message', { type: 'disconnect' })
  170. }
  171. }
  172. #closeMqtt() {
  173. if (this.#client) this.#client.end()
  174. this.#client = null
  175. this.#mqttChannel = null
  176. this.#isMin = 1
  177. }
  178. connectLogi() {
  179. this.contrlEvent = fork(join(__dirname, './logiControl.js'));
  180. this.contrlEvent.on('message', msg => {
  181. if (!msg || msg.type === 'err' || !msg.data || !msg.data[44]) {
  182. this.contrlEvent.disconnect()
  183. this.contrlEvent.kill(msg.pid ?? 'SIGKILL')
  184. this.contrlEvent = null
  185. dialog.showMessageBox(this.mainWin, { message: msg.text, type: 'error', title: '连接错误' }).then(async ({ response }) => {
  186. if (!response) {
  187. await sleep(2000)
  188. this.connectLogi()
  189. }
  190. })
  191. } else {
  192. if (this.mainWin && !this.mainWin.isDestroyed() && this.contrlEvent) this.mainWin?.webContents.send('message', { type: 'contrl', content: msg.data })
  193. }
  194. })
  195. }
  196. };
  197. new MainSerivce()