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.
- 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.
- 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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。