How to check if a key is pressed in Electron? - electron

Is it possible to check if a key is pressed in Electron? I'm not asking to detect when a key is pressed.
When my app starts, I want to be able to hold shift while it starts, before the window loads, to open a special menu. An example of what I'm looking for is:
app.whenReady().then(() => {
const win = new BrowserWindow({ show: false })
win.loadFile("whatever.html")
// ***
const openSpecialMenu = keyboard.shiftKeyHeld
// ***
// do other stuff
window.show()
})

I don't know if there's an easy way to do this, but I believe you can do this using native APIs.
On Windows you can use GetKeyState.
On macOS you can use the function described in this answer.
You can then build a native node addon that allows you to use these native APIs in your Electron JavaScript code.
In fact, I found a node module for Windows that does what you need: https://github.com/Zysen/node-asynckeystate
You need to do additional work to support other operating systems.

Related

Use ShareArrayBuffer from an electron app

We have an electron app which uses ShareArrayBuffer. It was written with a very old version of electron. When we updated the electron version in order to use SIMD instructions in WASM, it started showing an error saying ShareArrayBuffer is not defined. It seems it is due to the security update in Chromeum and ShareArrayBuffer is available iff Cross Origin Isolation is set.
Google shows some articiles explaining how to enable Cross Origin Isolation on web-pages, but I have not found any article that explains how to do that on an electron app. Does anyone have a pointer to the info or an idea to try?
Thank you,
It can be enabled by adding the following line in background.js
app.commandLine.appendSwitch('enable-features','SharedArrayBuffer')
Reference
As of December 2022, this is the only thing that worked for me:
browserWindow = new BrowserWindow({...});
// Enable SharedArrayBuffer
browserWindow.webContents.session.webRequest.onHeadersReceived((details, callback) => {
details.responseHeaders['Cross-Origin-Opener-Policy'] = ['same-origin'];
details.responseHeaders['Cross-Origin-Embedder-Policy'] = ['require-corp'];
callback({ responseHeaders: details.responseHeaders });
});

Electron: how to close a portable app on pulling out the pendrive

I made a portable app with electron. It works fine. I have it saved in a pendrive, so I don't need to install it and copy into my hard disk to run it. Just executing the app from the pendrive I have it running in my desktop. But what I need, is implement a system that makes that if you pull out the pendrive the app automatically closes. I've been googling something similar and found many ways for using a pendrive as a master key. But that isn't exactly what I need. I don't want to shutdown the PC, I only need to close the app and remove it from memory. There is any way or nodejs library that can help me with that?
You can use node_usbspy module to watch the usb insertion/removal. This module supports only Windows.
If you receive an event with device_status as 0 then you could quit the app with app.quit()
Hope it helps you!
Note: I'm the author of node-usbspy.
I found a practical solution that could work on all platforms:
const basePath = app.getAppPath()
setInterval(() => {
const path = basePath.split('/')
const baseDir = path.slice(0, -1).join('/')
fs.writeFile(baseDir + '/portable.txt', '1', err => {
if(err) {
app.quit()
}
})
}, 1000)
The script above try to save a TXT file every second. If fs.writeFile returns an error, app.quit() is called closing the application.

Firefox native messaging through webextension

Created a webextension for firefox (currently using Nightly 52), that uses native messaging to launch a java program on Linux (Ubuntu 14, 32x).
The webextension loads, reads the .json file and reads the path which points to a script that starts the java program. The JSON and the path are correct as when I use:
var native = browser.runtime.connectNative("passwordmanager");
console.log("native.name" + native.name); //outputs passwordmanager.
native.onDisconnect.addListener(function(m) { console.log("Disconnected"); });
The above code prints the name of the native port and also prints "Disconnected". So I m guessing the native app is terminating for some reason.
The application is only skeleton right now, that just does sysout and reads sysin and works correctly if Launch it directly through the shell script.
While debugging the webextension, I am not able to step into the call to connectNative, as it just steps-over that call instead of doing step-in. So kind of out of options whats' going wrong.
Please let me know if anyone is able to create a native messaging app based on FF webextension and any pointers on what I might be doing wrong.
Thanks
This solution here shows you how to detect onConnect and onFail. It should help you out to figure out your real problem.
So I don't think you can do proper error handling with connectNative from the JS side alone. You can do somewhat error handling if you get the exe side involved, but you can't get a string for "error reason" when an error occurs. The error is only logged to console.
First make sure to set your deeloper prefs, so messages show in your browser console. You can use this addon - https://addons.mozilla.org/en-US/firefox/addon/devprefs/ - or read that addon description it gives you the MDN page with the prefs to set.
Then this is how you can do some sort of error handling (without error reason) (pseudo-code - i might need a .bind in the callbcks):
function connectNative(aAppName, onConnect, onFail) {
var listener = function(payload) {
if (!connected) {
connected = true;
port.onDisconnect.removeListener(failedConnect);
onConnect();
} else {
// process messages
}
}
var failedConnect = function() {
onFail('failed for unattainable reason - however see browser console as it got logged there');
}
var connected = false;
var port = chrome.runtime.connectNative(aAppName);
port.onMessage.addListener(listener);
port.onDisconnect.addListener(failedConnect);
return port;
}
Now in your exe, as soon as it starts up, make it write to stdout something. That will trigger the onConnect.

How can you get information about other apps running or in focus?

My motivation: I'm writing an app to help with some quantified self / time tracking type things. I'd like to use electron to record information about which app I am currently using.
Is there a way to get information about other apps in Electron? Can you at least pull information about another app that currently has focus? For instance, if the user is browsing a webpage in Chrome, it would be great to know that A) they're using chrome and B) the title of the webpage they're viewing.
During my research I found this question:
Which app has the focus when a global shortcut is triggered
It looks like the author there is using the nodObjc library to get this information on OSX. In addition to any approaches others are using to solve this problem, I'm particularly curious if electron itself has any way of exposing this information without resorting to outside libraries.
In a limited way, yes, you can get some of this information using the electron's desktopCapturer.getSources() method.
This will not get every program running on the machine. This will only get whatever chromium deems to be a video capturable source. This generally equates to anything that is an active program that has a GUI window (e.g., on the task bar on windows).
desktopCapturer.getSources({
types: ['window', 'screen']
}, (error, sources) => {
if (error) throw error
for (let i = 0; i < sources.length; ++i) {
log(sources[i]);
}
});
No, Electron doesn't provide an API to obtain information about other apps. You'll need to access the native platform APIs directly to obtain that information. For example Tockler seems to do so via shell scripts, though personally I prefer accessing native APIs directly via native Node addons/modules or node-ffi-napi.
2022 answer
Andy Baird's answer is definitely the better native Electron approach though that syntax is outdated or incomplete. Here's a complete working code snippet, assumes running from the renderer using the remote module in a recent Electron version (13+):
require('#electron/remote').desktopCapturer.getSources({
types: ['window', 'screen']
}).then(sources => {
for (const thisSource of sources) {
console.log(thisSource.name);
}
});
The other answers here are for the rendering side - it might be helpful to do this in the main process:
const { desktopCapturer } = require('electron')
desktopCapturer.getSources({ types: ['window', 'screen'] }).then(async sources => {
for (const source of sources) {
console.log("Window: ", source.id, source.name);
}
})

Is it possible to combine React Native with socket.io

I was working on an app with Phonegap + React.js and Socket.io. However, then React-Native got released and the native feel is amazing.
I tried getting socket.io-client working with React Native, but unfortunately without much success. I did some research and I'm getting the exact same errors as described in this issue: https://github.com/facebook/react-native/issues/375
The comments on the issue said to try and use the fetch API to fetch JS modules, but I think I'm doing this wrong:
var socketScript;
fetch('https://cdn.socket.io/socket.io-1.2.0.js')
.then(function(response) {
socketScript = response._bodyText;
}).done(function() {
var socket = socketScript.io();
});
This returns an undefined is not a function.
Is there any way to make socket.io-client work with React Native? Or am I looking at this the wrong way? Perhaps there are other, better suited solutions?
For those like me stumbling across this question looking how to integrate socket.io with react native.
Since React Native has supported websockets for a short time now, you can now set up web sockets really easily with Socket.io. All you have to do is the following
npm install socket.io-client
first import react-native
assign window.navigator.userAgent = 'react-native';
import socket.io-client/socket.io
in your constructor assign this.socket = io('localhost:3001', {jsonp: false});
So in all it should look like this after npm installing socket.io-client:
import React from 'react-native';
// ... [other imports]
import './UserAgent';
import io from 'socket.io-client/socket.io';
export default class App extends Component {
constructor(props) {
super(props);
this.socket = io('localhost:3001', {jsonp: false});
}
// now you can use sockets with this.socket.io(...)
// or any other functionality within socket.io!
...
}
and then in 'UserAgent.js':
window.navigator.userAgent = 'react-native';
Note: because ES6 module imports are hoisted, we can't make the userAgent assignment in the same file as the react-native and socket.io imports, hence the separate module.
EDIT:
The above solution should work, but in the case it doesn't try create a separate socketConfig.js file. In there import anything that is needed, including const io = require('socket.io-client/socket.io'); and having window.navigator.userAgent = 'react-native'; BEFORE requiring socket.io-client. Then you can connect your socket there and have all listeners in one place. Then actions or functions can be imported into the config file and execute when a listener receives data.
Now, if you want to use socket.io in your RN app, you must use this code:
if (!window.location) {
// App is running in simulator
window.navigator.userAgent = 'ReactNative';
}
// This must be below your `window.navigator` hack above
const io = require('socket.io-client/socket.io');
const socket = io('http://chat.feathersjs.com', {
transports: ['websocket'] // you need to explicitly tell it to use websockets
});
socket.on('connect', () => {
console.log('connected!');
});
Big thanks for Eric Kryski.
import { io } from 'socket.io-client'
const socket = io(`${SOCKET_URL}:${SOCKET_PORT}`)
Important! SOCKET_URL should be your local IP address, not localhost or 127.0.0.1.
To check your local IP:
Mac / Linux: run ifconfig in terminal
Windows: run ipconfig --all in shell
Should be something like: const socket = io('http://10.0.1.6:3000', {transports: ['websocket']})
Short of a polyfill for the WebSocket API, you can create a native module that makes use of web-sockets and send events to Javascript using eventDispatcher.
On the Javascript side, you would subscribe to these events using DeviceEventEmitter.addListener.
For more information on using native modules, see the react-native doc on the topic
Edit Feb 2016: React Native now supports Web Sockets so some of this advice is invalid.
You've misinterpreted the Github issue I'm afraid. In it, aackerman says:
For this specific case you'll likely want to use the fetch API which
is provided by the environment.
He doesn't say that you should use the fetch API to grab remote JS modules. What he's suggesting is that the fetch API be used in place of the built-in Node.JS request module, which isn't available in React Native.
Let's look at your code:
socketScript = response._bodyText;
var socket = socketScript.io();
Think about this for a second - socketScript isn't a JavaScript object, it's a string - therefore how can you call the io method on it?
What you'd really need to do is parse _bodyText before using it (in a browser you could use eval), but then you'd still have the problem that while React Native has a polyfill for XHR and the fetch API, it doesn't yet have one for the WebSocket API. Unless I'm mistaken, this means you're stuck.
I suggest opening a Github issue to request a WebSocket API polyfill and ask for the thoughts of the community. Someone might have a workaround.
Although you can use socket.io-client lib, the community is complaining about compatibility issues with most versions (I did experience some). It works, but now I'm afraid to upgrade the lib because I need to verify the compatibility of the next version to my server's version and react-native's version!
It seems that a lot of people miss react's own implementation of Websockets! I really recommend you use this instead of socket.io-client. It is very similar in usage:
var ws = new WebSocket('ws://host.com/path');
ws.onopen = () => { // connection opened ws.send('something'); // send a message};
ws.onmessage = (e) => { // a message was received console.log(e.data);};
ws.onerror = (e) => { // an error occurred console.log(e.message);};
ws.onclose = (e) => { // connection closed console.log(e.code, e.reason);};
Finally found it.
Client
import { io } from "socket.io-client/build/index"
io("ws://<LOCAL HOME NETWORK IP>:<PORT ON SERVER>")
Server
import express from "express"
import http from "http"
import * as SocketIO from "socket.io"
const app = express()
const server = new http.Server(app)
const io = new SocketIO.Server(server)
const port = 8000
io.on("connection", socket => {
console.log("CONNECTIONS")
}
may be this will through error
import io from "socket.io-client/socket.io"
Then just add below line....
import io from "socket.io-client/dist/socket.io";
then in componenDidMount or useEffect function just add below line.Never use it under constructor of class component.
var socket = io("https://localhost.com:3000", { jsonp: false });
// client-side
socket.on("chat_message", (msg) => {
console.log(msg);
});
2022 Answer
In 2022 you can easily just use the latest version of socket.io-client with React Native.
npm install socket.io-client
import io from 'socket.io-client';
Right now there isn't a good hook based socketIO libary that I've been able to make work with RN but it's pretty straightforward to roll out your own custom hook depending on your needs. IE
function useWebsocket(url) {
const [connected, setConnected] = useState(false);
const [socket, setSocket] = useState(null);
useEffect(()=>{
const newSocket = io(url);
newSocket.on('connect', ()=>setConnected(true));
newSocket.on('disconnect', ()=>setConnected(false));
setSocket(newSocket);
}, [])
return {
connected,
socket,
}
}
Something like this can get you started. This would open a socket for each component that calls the hook, which can work well if you just need one component with one connection. Sharing the connection across components gets a little more hairy but it isn't too bad.
The connected state is really useful for letting your user know the status of connections and stuff like that.
But yeah, point is you can just install it and use it in your component. Don't use it in the body of your functional components

Resources