0

I am currently having some issues receiving an constant stream of active windows from my main process to my renderer process (in Angular). I know receiving second-by-second updates will break my app as it does, so I am trying to throttle it a bit using setTimeout. But this still doesn't work. How can I fix this?

renderer - component in Angular

  ipc: IpcRenderer | undefined | null;

  constructor(private electronService: ElectronService) { }

  ngAfterViewInit(): void {
    this.getActiveWindow();
  }

  getActiveWindow() {
    while(this.electronService.isElectronApp) {
      this.ipc = this.electronService.ipcRenderer;
      this.ipc.send('get-active-window');
      this.ipc.on('get-active-window-reply', (_event, reply) => {
        console.log(reply);
      });
    }
  }

main.js

const activeWindows = require('electron-active-window');

ipcMain.on('get-active-window', (_event, _arg) => {
  let i = 0;
  setTimeout(function () {
    activeWindows().getActiveWindow().then((result) => {
      win.webContents.send("get-active-window-reply", result)
    })
  }, 10000 * i)
});

So far I've tried the following, but this only displays the active window once. I'd like to keep track of all the changes:

renderer - component in Angular

 ngOnInit(): void {
    this.getActiveWindow();
  }

  getActiveWindow() {
    if(this.electronService.isElectronApp) {
      this.ipc = this.electronService.ipcRenderer;
      this.ipc.send('get-active-window');
      this.ipc.on('get-active-window-reply', (_event, reply) => {
        console.log(reply);
      });
    }
  }

main.js

ipcMain.on('get-active-window', (_event, _arg) => {
  activeWindows().getActiveWindow().then((result) => {
    win.webContents.send("get-active-window-reply", result)
  });
});
methuselah
  • 12,766
  • 47
  • 165
  • 315
  • 2
    There's a lot of things wrong with this code. You have a while loop that keeps sending events and registering handlers. The response handler just sends a delayed response. Both sides are somewhat asynchronous in nature. Think about what happens when with your current code and what you want and try to rework from there. – Clashsoft Dec 10 '21 at 13:57
  • @Clashsoft thanks for replying. I've updated my question to show where I currently am with this. The second attempt seems to only get the window titles once. I'd like to make this real-time. How can I fix this? – methuselah Dec 10 '21 at 14:15

1 Answers1

2

You're only receiving the active windows once because you're only requesting them once in your ngOnInit.

So in my opinion you've currently two options.

  1. You're requesting the information every few seconds from the client
  2. You let your Electron application notify the client about the windows

Option 1: (only solution afaik)

render.component

import { interval } from 'rxjs';

...

getActiveWindow() {
  if(this.electronService.isElectronApp) {
    this.ipc = this.electronService.ipcRenderer;
    
    this.ipc.on('get-active-window-reply', (_event, reply) => {
      console.log(reply);
    });

    // A request directly to get "instant" feedback
    this.ipc.send('get-active-window');

    // You could also use setInterval but I prefer rxjs more ;)
    // Don't forget to unsubscribe in ngOnDestroy
    interval(2000).subscribe(() => {
      this.ipc.send('get-active-window');
    });
  }
}

This solution creates a lot of overhead since you don't need to update the active windows every few seconds, so I do not recommend this one.

Option 2 (recommended not working):

main.js

// Sends a message every time a BrowserWindow is created
app.on('browser-window-created', (event, window) => {
  this.sendActiveWindows();
});

// Respond to the client request
ipcMain.on('get-active-window', (_event, _arg) => {
   this.sendActiveWindows();
});

// Helper function
sendActiveWindows(){
  activeWindows().getActiveWindow().then((result) => {
    win.webContents.send("get-active-window-reply", result)
  });
}

If you need also the information when a windows is being closed you need to add a listener to BrowserWindow.on('closed',...) (more here) and notify your client then as well.

render.component.ts (only comments added)

getActiveWindow() {
  if(this.electronService.isElectronApp) {
    this.ipc = this.electronService.ipcRenderer;
    // Request the currently active windows, 
    // otherwise this info is missing until new window is created
    this.ipc.send('get-active-window');
    this.ipc.on('get-active-window-reply', (_event, reply) => {
      console.log(reply);
    });
  }
}

This solution is more suitable since your client is only getting notified if the number of windows changes, so I do recommend this one

Update

After clarifying the problem in the chat you cannot use option 2 since you want to detect the active windows of the OS and not of your Electron app. Due that info option 1 is the only valid solution here

Batajus
  • 5,831
  • 3
  • 25
  • 38
  • getActiveWindow should probably return the Observable from interval, otherwise good solution! – Clashsoft Dec 10 '21 at 15:03
  • This depends on you ;) You could also assign the subscription to a variable inside of `getActiveWindow` in that case it would not be necessary. Thanks :) – Batajus Dec 10 '21 at 15:09
  • Thanks for the this @Batajus. I've tried option 2 and it doesn't seem to work. My *main.js* is at https://pastebin.com/uV2b12Nm, and my component code is here: https://pastebin.com/Ae4Bx0Pu. – methuselah Dec 10 '21 at 16:00
  • Option 1 works, but I get the error `(node:22168) MaxListenersExceededWarning: Possible EventEmitter memory leak detected. 11 get-active-window-reply listeners added to [EventEmitter]. Use emitter.setMaxListeners() to increase limit (Use `electron --trace-warnings ...` to show where the warning was created)` in the console, so its probably not a good idea. – methuselah Dec 10 '21 at 16:00
  • It picks up the new window title when I initialise the program, but it doesn't pick it up when I switch to another one, for example Telegram. – methuselah Dec 10 '21 at 16:01
  • Why does it seem like option 2 is not working? Regarding the console message, are you adding the event listener inside of the interval? – Batajus Dec 10 '21 at 16:04
  • Are you creating a new window for Telegram or are you just loading another url? – Batajus Dec 10 '21 at 16:05
  • So the above is part of a desktop app, so I am opening a seperate Telegram/Snipping Tool/Discord instance. I want to get the name of the active window whenever that happens. – methuselah Dec 10 '21 at 16:07
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/240018/discussion-between-batajus-and-methuselah). – Batajus Dec 10 '21 at 16:14