4
头图

Electron window communication and expansion screen

This issue mainly introduces the opening of the expansion screen and the message communication between the two windows.

Expansion screen

In the window startup section, we encapsulated a createWindow , and use this to create a window. The extended screen is also a window, but we put it on the corresponding external screen based on how many displays.

Rendering process

We add a button in the rendering process and let it be clicked, and then send the operation (open or hide) to the expansion screen to the main process

<a-button type="primary" @click="openScreen">打开拓展屏</a-button>

const state = reactive({
  open: true,
  message: ''
})
async function openScreen() {
  await window.ipcRenderer.invoke('win-subScreen', {
    open: state.open,
    path: '#/subScreen'
  })
  state.open = !state.open
}

Main process

After the main process receives it, it obtains the array of the current window (with several display screens) screen.getAllDisplays() , and then obtains the information of our external screen externalDisplay
Similarly, we also use createWindow create a new window. Previously, our rendering process passed a path. This path is the page we want to display on the expanded screen.
It should be noted here that the value returned by createWindow needs to be saved with a global variable. As mentioned in the tray, if it is a local variable, it will be destroyed after the function is executed, and the window will also be destroyed.
We use global assign values here, which will be used for window communication later, and it is the same when you use global variables to assign values here.

global.js

global.tray = null
global.willQuitApp = false
global.envConfig = {}
global.sharedObject = {
  win: '',
  subScreen: ''
}

export default global


import { ipcMain, app, screen } from 'electron'
import global from '../config/global'
import createWindow from './createWindow'
import path from 'path'

const win = global.sharedObject.win
ipcMain.handle('win-subScreen', (_, data) => {
  if (data.open) {
    const displays = screen.getAllDisplays()
    const mainBounds = win.getNormalBounds()
    const externalDisplay = displays.find((display) => {
      return display.bounds.x !== 0 || display.bounds.y !== 0
    })
    if (externalDisplay) {
      if (global.sharedObject.subScreen) {
        global.sharedObject.subScreen.show()
      } else {
        global.sharedObject.subScreen = createWindow({
          frame: false,
          show: false,
          parent: win, // win是主窗口
          fullscreen: true,
          webPreferences: {
            webSecurity: false,
            contextIsolation: false,
            enableRemoteModule: true,
            nodeIntegration: process.env.ELECTRON_NODE_INTEGRATION,
            plugins: true,
            preload: path.join(__dirname, 'preload.js'),
            devTools: false
          },
          x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x,
          y: externalDisplay.bounds.y
        }, data.path, `index.html${data.path}`)
        global.sharedObject.subScreen.once('ready-to-show', () => {
          global.sharedObject.subScreen.show()
        })
        global.sharedObject.subScreen.on('closed', () => {
          global.sharedObject.subScreen = null
        })
      }
    } else {
      console.log('未检测到拓展屏')
    }
  } else {
    global.sharedObject.subScreen && global.sharedObject.subScreen.destroy()
  }
})

Here is a small process. Let’s first determine which screen our software is located on. For example, we have two screens, 1 and 2, then our main window and expansion screen should be in different positions, for example: our main window is in If 1, then our expansion screen should be opened at 2. If the position is the same, since our expansion screen is set to full screen, the main window will be blocked at this time. If there is no shortcut key to close, then the expansion cannot be closed. Screen, so there are the following treatments.

const mainBounds = win.getNormalBounds()
mainBounds为主窗口信息,如果主窗口有一半以上都处于副屏幕的话,那么我们认为主窗口在副屏,那么拓展屏打开位置应该在主屏,否则的话应该在副屏。
x: mainBounds.x < 0 && Math.abs(mainBounds.x) > (win.getContentSize()[0] / 2) ? 0 : externalDisplay.bounds.x

Window communication

Generally speaking, there are two ways to communicate between electorn windows.

  1. The rendering process of window A sends information to the main process, and the main process sends the information to the rendering process of window B after receiving it, that is, using the main process as a transit.
  2. During the rendering process of window A, information is sent directly to WebContents.id

way of communication

Here are several common communication methods

ipcRenderer.invoke:渲染进程发送消息给主进程,这是一个promise,可以在其resolve中获取ipcMain.handle的返回值
ipcMain.handle:接收invoke发送的信息,可以return值给ipcRenderer.invoke

ipcRenderer.send:渲染进程发送消息给主进程
ipcMain.on:接收send的信息

ipcRenderer.on:接收主进程的消息
webContents.send:主进程发送消息给渲染进程

ipcRenderer.sendTo:可以通过webContentsId直接发送信息到对应的渲染进程窗口

Let's implement both 1 and 2 here

Rendering process

In the rendering process, the value global in the main process remote remote.getGlobal('sharedObject').subScreen is the window of our previous expansion screen.
transferSub is our plan 1, and directSub is our plan 2.

div class="subMessage">
  <a-textarea
    v-model:value="state.message"
    :auto-size="{ minRows: 2, maxRows: 5 }"
  />
  <a-button type="primary" :disabled="state.message.length === 0" @click="transferSub">中转发送</a-button>
  <a-button type="primary" :disabled="state.message.length === 0" @click="directSub">直接发送</a-button>
</div>

import { defineComponent, reactive, onMounted, onUnmounted, getCurrentInstance } from 'vue'
const { remote } = require('electron')

const state = reactive({
  open: true,
  message: ''
})
const { proxy } = getCurrentInstance()
const { $message } = proxy
function transferSub() {
  window.ipcRenderer.invoke('win-subScreen-message', state.message)
}
function directSub() {
  const subScreen = remote.getGlobal('sharedObject').subScreen
  if (subScreen) {
    window.ipcRenderer.sendTo(subScreen.webContents.id, 'main-subScree', state.message)
  }
}
onMounted(() => {
  window.ipcRenderer.on('subScree-main', (_event, data) => {
    $message.success(data)
  })
})
onUnmounted(() => {
  window.ipcRenderer.removeListener('subScree-main')
})

Main process

Transit, use webContents.send to send the information to the extended screen window

ipcMain.handle('win-subScreen-message', (_, data) => {
  if (global.sharedObject.subScreen) {
    global.sharedObject.subScreen.webContents.send('renderer-subScreen-message', data)
  }
})

Expansion screen information receiving and sending

Here we directly monitor the relayed messages and the directly sent messages, and then directly notify the main window of the message.

<template>
  <div class="subScreen">{{ state.message }}</div>
</template>

<script>
import { defineComponent, reactive, onMounted, onUnmounted } from 'vue'
const { remote } = require('electron')

export default defineComponent({
  setup() {
    const state = reactive({
      message: ''
    })
    onMounted(() => {
      const win = remote.getGlobal('sharedObject').win
      window.ipcRenderer.on('renderer-subScreen-message', (_event, data) => {
        state.message = data
        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了中转发送信息')
      })
      window.ipcRenderer.on('main-subScree', (_event, data) => {
        state.message = data
        window.ipcRenderer.sendTo(win.webContents.id, 'subScree-main', '我收到了直接发送信息')
      })
    })
    onUnmounted(() => {
      window.ipcRenderer.removeListener('renderer-subScreen-message')
      window.ipcRenderer.removeListener('main-subScree')
    })
    return {
      state
    }
  }
})
</script>

verification

We open the expansion screen, enter the information in the main window, click Send directly or relay to send, to see if the display of the expansion window is the information we entered, the main window displays the notification information of the expansion screen.

This series of updates can only be organized during weekends and after get off work. If there are more content, the update will be slower. I hope it can be helpful to you. Please support it by star or like favorites.

This article address: https://xuxin123.com/electron/ipc-subscreen
This github address: link


陌路凡歌
7.9k 声望258 粉丝

人笨,多听,多写,多看书