Electron non-context-aware native module in renderer issue - javascript

I have tried many things and can not seem to get the robotjs code to execute even with rebuild, and other examples I've seen to try.
I still get the Loading non-context-aware native module in renderer error.
I have a button, and on click of the button I want the robotjs code to be executed.
Here is my current code :
bot.js
const robot = require('robotjs')
const electron = require('electron')
const ipc = electron.ipcRenderer
const Bot = () => {
const button = document.getElementById('start');
// button.addEventListener('click', () => console.log('Click'))
button.addEventListener('click', function (e) {
// Get mouse position.
var mouse = robot.getMousePos();
// Get pixel color in hex format.
var hex = robot.getPixelColor(mouse.x, mouse.y);
console.log("#" + hex + " at x:" + mouse.x + " y:" + mouse.y);
});
}
Bot();
My main file :
index.js
const { app, BrowserWindow, ipcMain } = require('electron');
const robot = require('robotjs')
const path = require('path');
// const Bot = require('./bot')
// Handle creating/removing shortcuts on Windows when installing/uninstalling.
if (require('electron-squirrel-startup')) { // eslint-disable-line global-require
app.quit();
}
const createWindow = () => {
const mainWindow = new BrowserWindow({
width: 800,
height: 600,
webPreferences: {
nodeIntegration: true,
}
});
// and load the index.html of the app.
mainWindow.loadFile(path.join(__dirname, 'index.html'));
// Open the DevTools.
mainWindow.webContents.openDevTools();
};
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow);
// Quit when all windows are closed, except on macOS. There, it's common
// for applications and their menu bar to stay active until the user quits
// explicitly with Cmd + Q.
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
// On OS X it's common to re-create a window in the app when the
// dock icon is clicked and there are no other windows open.
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});
// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and import them here.
Here is the index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>Hello World!</title>
<link rel="stylesheet" href="index.css" />
</head>
<body>
<div class="App">
<div class="Header">
<h1>Checkout-Bot</h1>
</div>
<div class="Wrapper">
<p>Click Start</p>
<button id="start">Start</button>
</div>
</div>
<script src="bot.js">
Bot();
</script>
</body>
</html>

Have you read this issue on Github?
They removed the ability to use non-NAPI and non-context aware modules.
If you're not using Electron 11 yet, you can still use those modules, if you add the following line in the main process:
app.allowRendererProcessReuse = false

As an alternative solution, Try https://www.npmjs.com/package/#nut-tree/nut-js instead of robotjs. Electron (Last checked with 18.2.0) works well with nutjs.
Please refer to https://www.npmjs.com/package/#nut-tree/nut-js & https://nutjs.dev/docs/tutorial-first_steps/get_moving to see the documentation and working samples.

Related

Electron - close renderer from main handler

TLDR: Best way to close a renderer window from main (or the renderer itself).
I am splitting up a process between X invisible renderer windows. When a renderer finishes its work, I want it to send an event to the main process and then close. Right now I have
//invisibleRenderer.js
doStuff().then(() => {
ipcRenderer.invoke('finish');
}
What is the best way to close the window? Is it in the ipcMain.handle? I can't figure out which method to call with which id.
ipcMain.handle('finish', (event, args) => {
//do what? event.frameId, event.processId, event.sender.id...
})
You can use BrowserWindow.fromId(event.sender.id) to get a reference to the window from where the IPC call originated, as demonstrated in this question.
This works fine in the main process. The following example opens 5 windows and loads the same contents. Each renderer script invocation picks a random delay (to simulate some work) and then dispatches the finish message to the main process. There, the main process picks up the BrowserWindow reference using event.sender.id and closes the corresponding window.
app.js
const {app, BrowserWindow, ipcMain} = require("electron");
const windows = [];
app.on("ready", function() {
for (let i=0; i<5; i++) {
let win;
win = new BrowserWindow( {
width: 300,
height: 300,
x: i*100,
y: i*100,
webPreferences: {
nodeIntegration: true,
contextIsolation: false
},
});
win.loadURL(`file://${__dirname}/index.html`);
windows.push(win);
}
});
ipcMain.handle("finish", (event, args) => {
const win = BrowserWindow.fromId(event.sender.id);
win.close();
});
index.html
<html>
<body>
Renderer
</body>
<script type="text/javascript" src="renderer.js"></script>
</html>
renderer.js
const {ipcRenderer} = require("electron");
// Simulate some work by choosing a random delay..
const delay = Math.round(Math.random()*10000);
setTimeout(() => ipcRenderer.invoke("finish"), delay);

Mute functionality not working in Agora Web SDk

I am trying to make simple 1:1 video conferencing app with Agora web SDK, i started with their basic demo and currently am trying to implement audio/video mute/unmute functionality. I read and tried other SO answers on the same but they too didn't worked.
Here is my index.html file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Video Call Demo</title>
<link rel="stylesheet" href="./styles/style.css">
</head>
<body>
<h1>
Video Call Demo<br><small style="font-size: 14pt">Powered by Agora.io</small>
</h1>
<h4>My Feed :</h4>
<div id="me"></div>
<button onclick="myFunc()">Mute</button> <!-- button for muting -->
<p id="demo"></p> <!-- this works for onclick action of above button -->
<h4>Remote Feeds :</h4>
<div id="remote-container">
</div>
<h4>Canvas :</h4>
<div id="canvas-container">
</div>
<script src="scripts/AgoraRTCSDK-2.4.0.js"></script>
<script src="scripts/script.js"></script>
<script src="https://cdn.agora.io/sdk/release/AgoraRTCSDK-3.2.1.js"></script>
</body>
</html>
Here is script.js file where actual logic is implemented
/**
* #name handleFail
* #param err - error thrown by any function
* #description Helper function to handle errors
*/
let handleFail = function(err){
console.log("Error : ", err);
};
// Queries the container in which the remote feeds belong
let remoteContainer= document.getElementById("remote-container");
let canvasContainer =document.getElementById("canvas-container");
/**
* #name addVideoStream
* #param streamId
* #description Helper function to add the video stream to "remote-container"
*/
function addVideoStream(streamId){
let streamDiv=document.createElement("div"); // Create a new div for every stream
streamDiv.id=streamId; // Assigning id to div
streamDiv.style.transform="rotateY(180deg)"; // Takes care of lateral inversion (mirror image)
remoteContainer.appendChild(streamDiv); // Add new div to container
}
/**
* #name removeVideoStream
* #param evt - Remove event
* #description Helper function to remove the video stream from "remote-container"
*/
function removeVideoStream (evt) {
let stream = evt.stream;
stream.stop();
let remDiv=document.getElementById(stream.getId());
remDiv.parentNode.removeChild(remDiv);
console.log("Remote stream is removed " + stream.getId());
}
function addCanvas(streamId){
let canvas=document.createElement("canvas");
canvas.id='canvas'+streamId;
canvasContainer.appendChild(canvas);
let ctx = canvas.getContext('2d');
let video=document.getElementById(`video${streamId}`);
video.addEventListener('loadedmetadata', function() {
canvas.width = video.videoWidth;
canvas.height = video.videoHeight;
});
video.addEventListener('play', function() {
const $this = this; //cache
(function loop() {
if (!$this.paused && !$this.ended) {
ctx.drawImage($this, 0, 0);
setTimeout(loop, 1000 / 30); // drawing at 30fps
}
})();
}, 0);
}
// Client Setup
// Defines a client for RTC
let client = AgoraRTC.createClient({
mode: 'live',
codec: "h264"
});
// Client Setup
// Defines a client for Real Time Communication
client.init("d2684f11650c446faaeb289d973e2dd3",() => console.log("AgoraRTC client initialized") ,handleFail);
let localStream = AgoraRTC.createStream({
// streamID: uid,
audio: true,
video: true,
screen: false,
camera: {
camId: '',
micId: '',
stream: {}
}
});
// The client joins the channel
client.join(null,"any-channel",null, (uid)=>{
localStorage.streamID = uid
// Stream object associated with your web cam is initialized
// let localStream = AgoraRTC.createStream({
// streamID: uid,
// audio: true,
// video: true,
// screen: false
// });
// Associates the stream to the client
localStream.init(function() {
//Plays the localVideo
localStream.play('me');
//Publishes the stream to the channel
client.publish(localStream, handleFail);
},handleFail);
},handleFail);
//When a stream is added to a channel
client.on('stream-added', function (evt) {
client.subscribe(evt.stream, handleFail);
});
//When you subscribe to a stream
client.on('stream-subscribed', function (evt) {
let stream = evt.stream;
addVideoStream(stream.getId());
stream.play(stream.getId());
addCanvas(stream.getId());
});
// this is the function for muting
function myFunc(){
document.getElementById("demo").innerHTML="yay" // p tag works
let x = localStream.muteVideo(); // from docs
console.log(x);
// taken from one of the answers but it isn't working
client.on("mute-video", function (evt) {
const remoteId = evt.uid;
localStream.camera.stream.muteVideo();
});
}
//When a person is removed from the stream
client.on('stream-removed',removeVideoStream);
client.on('peer-leave',removeVideoStream);
There are JavaScript scope errors for a few variables and objects you have given. I would recommend fixing those. You have also tried to import two versions of the SDK (2.x and 3.x) in the HTML file.
For example: script.js:62 Uncaught ReferenceError: AgoraRTC is not defined. Try to debug all such errors you see in the console.
I would recommend following the Agora documentation for setting up a working video call and the muteAudio and the muteVideo functions for muting audio and video.
Simple Sample App: https://github.com/AgoraIO/Basic-Video-Call/tree/master/One-to-One-Video/Agora-Web-Tutorial-1to1
Fully Professional Sample App: https://github.com/akshatvg/speakOut

How to use electron tabs webview as to communicate from browser window to tabs?

I am trying to have my communicate between different rendering processes in electron. Rather than have multiple windows I am using electron-tabs which can be found here. To start I simply want my main window to be able to send a message using ipcRenderer to each tab. With multiple windows, you could store each window in a global variable like this stack overflow question and then use the web-contents to send a message.
UPDATE: In the electron-tabs documentation, it suggests you can use the tabs webview the same as you would a windows web-contents (see here). Unfortunately, either I am missing something or it is a little more complicated.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs... !!!! DOES NOT WORK !!!!
circuitTabs[i].webview.send('testMessage',i);
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});
Try following this:
<webview>.send(channel, ...args)
channel String
...args any[]
Returns Promise<void>
Send an asynchronous message to renderer process via channel, you can also send arbitrary arguments. The renderer process can handle the message by listening to the channel event with the ipcRenderer module.
See webContents.send for examples.
Look at the docs below for more information.
https://www.electronjs.org/docs/api/webview-tag#webviewsendchannel-args
I figured it out. You can get the webcontentsID from the tab and use ipcRenderer.sendTo function.
For the code: I have a main window mainWindow.html
<html>
<head></head>
<body>
<div class="etabs-tabgroup" >
<div class="etabs-tabs" ></div>
<div class="etabs-buttons" style="padding:5px 0px"></div>
</div>
<div class="etabs-views"></div>
<link rel="stylesheet" href="node_modules/electron-tabs/electron-tabs.css">
<script>
const TabGroup = require('electron-tabs') ;
const electron = require('electron') ;
const {ipcRenderer} = electron;
var tabs = [];
// Create a new tabGroup
let tabGroup = new TabGroup();
// Add 3 tabs to tabGroup
for (var i = 0; i <3;i++){
tabs.push(tabGroup.addTab({
src: 'file://' + __dirname + '/tab.html',
webviewAttributes: {
nodeintegration: true
}
}));
// Send test message to tabs...
var webContentsID = tabs[i].webview.getWebContentsId();
ipcRenderer.sendTo(webContentsID,'testMessage');
}
</script>
</html>
tab.html
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<script>
const electron = require('electron') ;
const {ipcRenderer} = electron;
const { remote } = require('electron');
ipcRenderer.on ('testMessage', (event, i) => { alert(`Message from main window to tab ${i}`); });
</script>
</body>
</html>
main.js
const electron = require('electron');
const {app, BrowserWindow} = electron;
let mainWindow = null;
app.on('ready', function () {
mainWindow = new electron.BrowserWindow({width: 1200,height: 800,
webPreferences: {
nodeIntegration: true,
webviewTag: true
}
});
mainWindow.loadURL(path.join(__dirname, '/mainWindow.html'));
mainWindow.on('ready-to-show', function () {
mainWindow.show();
mainWindow.focus();
});

Electron/Javascript: Detect when window is un/maximized

I am creating an electron application with a custom window bar in place of the default Windows one. I need to know when the window is maximized or un-maximized in order to change the icon on the window bar to reflect the window's state.
maximize / unmaximized event is availble for browserwindow. https://github.com/electron/electron/blob/master/docs/api/browser-window.md#event-maximize
you can use:
const { remote } = require("electron");
var win = remote.BrowserWindow.getFocusedWindow();
if(win.isFullScreen()) {
// your code here
}
or :
const { remote } = require("electron");
var win = remote.BrowserWindow.getFocusedWindow();
if(win.isMaximized()) {
// your code here
}

Can't register click events in html BrowserWindow within Electron

I have been following through a tutorial for Electron and I am using a html document in a new BrowserWindow as a pop-up. The problem is, when I use an onclick handler on a html tag, it doesn't seem to fire. If I open the developer tools, I can type my function into the console and it behaves as expected!
Not sure if I'm missing something really obvious here but would appreciate anyone who could help me solve the issue.
Here is the body tag from the html for BrowserWindow:
<body id="page-info">
<main class="info-content">
<h3 class="page-headline">Wisdom Pet</h3>
<p>Welcome to the Wisdom Pet App.<br>
<strong>Version 1.0.0</strong><br>
<small>Built with love by Ray Villalobos</small>
</p>
<p>close window</p>
</main>
<script type="text/javascript">enter code here
var electron = require('electron');
var shell = electron.shell;
var ipc = electron.ipcRenderer;
/* Also tried using event listeners without html onclick
var closeAbout= document.getElementById("closeAbout");
closeAbout.addEventListener("click", function(){
send();
});
*/
function send(){
console.log("sending...");
ipc.sendSync('closeInfoWindow')
}
</script>
And here is the main.js file:
var Electron = require("electron");
var Menu = Electron.Menu;
var BrowserWindow = Electron.BrowserWindow;
var app = Electron.app;
var ipc = Electron.ipcMain;
var myAppMenu, menuTemplate;
app.on("ready", function(){
var appWindow = new BrowserWindow({
show: false
});
appWindow.loadURL("file://" + __dirname + "/index.html");
var infoWindow = new BrowserWindow({
show: false,
width: 400,
height: 300,
frame: false,
transparent: true,
});
infoWindow.loadURL('file://' + __dirname + '/info.html');
appWindow.once("ready-to-show", function(){
appWindow.show();
});
ipc.on("openInfoWindow", function(event, arg){
event.returnValue = "";
infoWindow.show();
})
ipc.on('closeInfoWindow', function(event, arg){
event.returnValue = '';
infoWindow.hide();
}); //closeInfoWindow
});
The ipc.on("openInfoWindow") work fine from the main window, but once the infoWindow is open, I can't close it unless I send the code directly from the developer tools.

Categories

Resources