To communicate between the main thread and render thread(s) you will want to become familiar with Electron's Inter-Process Communication and Context Isolation.
Additionally, you will want to become familiar with Electron's ipcRenderer and ipcMain modules.
On brightness adjustments, you will want to communicate that adjustment to the main thread through the use of IPC. To safely communicate to the main thread, one should use a preload.js script.
The preload.js script is integrated into the construction of your application window, allowing permissible actions to take place.
Below is a preload script that uses "channel names" to convey information to and from the main thread and render thread(s).
Is a good design decision not to implement concrete functions directly within your preload.js script. Instead, use you preload script as a gate keeper to only allow messages between threads. This will greatly simplify your preload script and separate your concerns.
Below is an example of a preload script that will fulfill your requirements, noting the use of the channel names brightness:increment and brightness:decrement. You can rename these channel names to whatever you like.
preload.js (main thread)
// Import the necessary Electron components.
const contextBridge = require('electron').contextBridge;
const ipcRenderer = require('electron').ipcRenderer;
// White-listed channels.
const ipc = {
'render': {
// From render to main.
'send': [
'brightness:increment',
'brightness:decrement'
],
// From main to render.
'receive': [],
// From render to main and back again.
'sendReceive': []
}
};
// Exposed protected methods in the render process.
contextBridge.exposeInMainWorld(
// Allowed 'ipcRenderer' methods.
'ipcRender', {
// From render to main.
send: (channel, args) => {
let validChannels = ipc.render.send;
if (validChannels.includes(channel)) {
ipcRenderer.send(channel, args);
}
},
// From main to render.
receive: (channel, listener) => {
let validChannels = ipc.render.receive;
if (validChannels.includes(channel)) {
// Deliberately strip event as it includes `sender`.
ipcRenderer.on(channel, (event, ...args) => listener(...args));
}
},
// From render to main and back again.
invoke: (channel, args) => {
let validChannels = ipc.render.sendReceive;
if (validChannels.includes(channel)) {
return ipcRenderer.invoke(channel, args);
}
}
}
);
Now that our preload script is configured, let's utilise it in the construction of our application window.
Additionally, let's listen on these message channels, and when detected, call our adjustBrightness function.
main.js (main thread)
const electronApp = require('electron').app;
const electronBrowserWindow = require('electron').BrowserWindow;
const electronIpcMain = require('electron').ipcMain;
const nodeFs = require("fs");
const nodePath = require("path");
// Prevent garbage collection
let window;
function createWindow() {
const window = new electronBrowserWindow({
x: 0,
y: 0,
width: 800,
height: 600,
show: false,
webPreferences: {
nodeIntegration: false, // For safety, let's disable Node integration
contextIsolation: true, // For safety, let's enable context isolation
preload: nodePath.join(__dirname, 'preload.js') // Let's use our preload script
}
});
window.loadFile('index.html')
.then(() => { window.show(); });
return window;
}
electronApp.on('ready', () => {
window = createWindow();
});
electronApp.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
electronApp.quit();
}
});
electronApp.on('activate', () => {
if (electronBrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// Let's listen for messages to increment or decrement the brightness
electronIpcMain.on('brightness:increment', () => {
adjustBrightness(10);
})
electronIpcMain.on('brightness:decrement', () => {
adjustBrightness(-10);
})
// Function to adjust screen brightness
function adjustBrightness(adjustment) {
let data = nodeFs.readFileSync('/sys/class/backlight/amdgpu_bl1/brightness', 'utf-8');
let newData = parseInt(data) + adjustment;
if (newData > 255) {
newData = 255;
} else if (newData < 0) {
newData = 0;
}
newData = newData.toString();
nodeFs.writeFileSync('/sys/class/backlight/amdgpu_bl1/brightness', newData, 'utf-8');
}
Lastly, let's make sure our rendered html sends the messages out to the main thread when the corresponding buttons are clicked.
index.html (render thread)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Clock App</title>
</head>
<body>
<input type="button" id="incBrightness" value="+">
<input type="button" id="decBrightness" value="-">
</body>
<script>
document.getElementById('incBrightness').addEventListener('click', () => {
window.ipcRender.send('brightness:increment');
})
document.getElementById('decBrightness').addEventListener('click', () => {
window.ipcRender.send('brightness:decrement');
})
</script>
</html>
incBright()anddecBright()defined in your example?