Search code examples
javascriptnode.jsmultithreadingscreenshotelectron

Electron UI BrowserWindow freezes main BrowserWindow


Code reference: https://github.com/aredfox/screencapturer

Problem description: Here is an electron app with a "MainWindow" that holds a button "Start capture". Once clicked it fires an event to the main process, the main process then launches a new, seperate, "BrowserWindow" object called 'captureWindow' with it's own capture.html and capture.js associated. In capture.js, every three seconds a screenshot is made and saved to c:\temp\screencap (this is a demo app to illustrate a problem, thus I did not make this configurable and hard coded the path in for now). Every time a capture is made in the 'craptureWindow' it freezes, which I expected it to. But, the 'mainWindow' object freezes as well, which I did not expect it to do. How should I handle this, so thaty the mainWindow does not freeze when a process is being run in another "BrowserWindow" object? I assumed electron BrowserWindows (or "tabs") had a seperate thread?


EDIT 20/12/2016 Possible culprit is desktopCapturer.getSources().

ADDENDUM: Found that the issue must be inside the codeblock of getMainSource, because when I cache that "source" result it doesn't freeze the whole of electron. Thus it must be that the filter method or getting the screen itself is causing the issue of the freeze.

function getMainSource(desktopCapturer, screen, done) {
    const options = {
        types: ['screen'], thumbnailSize: screen.getPrimaryDisplay().workAreaSize
    }
    desktopCapturer.getSources(options, (err, sources) => {
        if (err) return console.log('Cannot capture screen: ', err)

        const isMainSource = source => source.name === 'Entire screen' || source.name === 'Screen 1'
        done(sources.filter(isMainSource)[0])
    })
}

The solution though is not caching the result of getMainSource (aka the "source"), as it will result in the same image data each time of course. I verified that by writing to file as png, and indeed each screenshot then was the exact same, even though enough had changed on the desktop. TODO: Possible option is to setup a video stream and save an image from the stream?


Solution

  • If you're wanting to capture screenshots cross platform, i'd advice using the approach below in stead of relying on the built in electron-api's. Not that they're not good, but they're not suited for taking screenshots every three seconds for example.

    The solution for me was the npm-module desktop-screenshot - and a npm package called hazardous, as this was needed on Windows & asar execution.

    The code I ended up implementing was this - it might be a source of inspiration/example for your problem.

    /* ******************************************************************** */
    /* MODULE IMPORTS */
    import { remote, nativeImage } from 'electron';
    import path from 'path';
    import os from 'os';
    import { exec } from 'child_process';
    import moment from 'moment';
    import screenshot from 'desktop-screenshot';
    /* */
    /*/********************************************************************///
    
    /* ******************************************************************** */
    /* CLASS */
    export default class ScreenshotTaker {    
        constructor() {
            this.name = "ScreenshotTaker";        
        }
    
        start(cb) {
            const fileName = `cap_${moment().format('YYYYMMDD_HHmmss')}.png`;
            const destFolder = global.config.app('capture.screenshots');
            const outputPath = path.join(destFolder, fileName);        
            const platform = os.platform();
            if(platform === 'win32') {
                this.performWindowsCapture(cb, outputPath);
            }
            if(platform === 'darwin') {
                this.performMacOSCapture(cb, outputPath);
            }
            if(platform === 'linux') {
                this.performLinuxCapture(cb, outputPath);
            }
        }
    
        performLinuxCapture(cb, outputPath) {
            // debian
            exec(`import -window root "${outputPath}"`, (error, stdout, stderr) => {
                if(error) {
                    cb(error, null, outputPath);
                } else {
                    cb(null, stdout, outputPath);
                }
            });
        }
        performMacOSCapture(cb, outputPath) {
            this.performWindowsCapture(cb, outputPath);
        }
        performWindowsCapture(cb, outputPath) {
            require('hazardous');
            screenshot(outputPath, (err, complete) => {
                if(err) {
                    cb(err, null, outputPath);
                } else {
                    cb(null, complete, outputPath);
                }
            });
        }
    }
    /*/********************************************************************///