main.js 6.3 KB

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