sidorares / node-x11

X11 node.js network protocol client
MIT License
518 stars 72 forks source link

PropertyNotify event.window is null #145

Closed specialblend closed 7 years ago

specialblend commented 7 years ago

I'm using this code snippet based on your post here

main.js

var x11 = require('x11');
x11.createClient(function (err, display) {
    var X = display.client;
    X.ChangeWindowAttributes(display.screen[0].root, {eventMask: x11.eventMask.PropertyChange});
    X.on('event', function (ev) {
        if (ev.name === 'PropertyNotify') {
            X.GetAtomName(ev.atom, function (err, name) {
                if (name === '_NET_ACTIVE_WINDOW') {
                    X.GetProperty(0, ev.window, ev.atom, X.atoms.WINDOW, 0, 4, function (err, prop) {
                        console.log('New active window:' + prop.data.readUInt32LE(0));
                    });
                }
            });
        }
    });
});

However, it seems that ev.window is null, as well as prop:

cj@lighthouse:~/workspace/moodring$ node main.js 
/home/cj/workspace/moodring/main.js:10
                        console.log('New active window:' + prop.data.readUInt32LE(0));
                                                               ^

TypeError: Cannot read property 'data' of undefined
    at /home/cj/workspace/moodring/main.js:10:64
    at ReadFixedRequest.callback (/home/cj/workspace/moodring/node_modules/x11/lib/xcore.js:490:39)
    at ReadFixedRequest.execute (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:41:10)
    at UnpackStream.resume (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:165:30)
    at UnpackStream.write (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:102:10)
    at Socket.<anonymous> (/home/cj/workspace/moodring/node_modules/x11/lib/xcore.js:88:21)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Socket.Readable.push (_stream_readable.js:134:10)

What's the proper way to listen for changes in the active (focused) window? I'm running Ubuntu 16.04 with Unity.

sidorares commented 7 years ago

not sure why ev.window is not defined. Can you log what's in ev prop is not defined because of incorrect window param to X.GetProperty. Try to use display.screen[0].root instead of ev.window

specialblend commented 7 years ago

@sidorares Thanks for the prompt response.

Here's the new code:

var x11 = require('x11');
x11.createClient(function (err, display) {
    var X = display.client;
    X.ChangeWindowAttributes(display.screen[0].root, {eventMask: x11.eventMask.PropertyChange});
    X.on('event', function (ev) {
        console.log(ev);
        if (ev.name === 'PropertyNotify') {
            X.GetAtomName(ev.atom, function (err, name) {
                if (name === '_NET_ACTIVE_WINDOW') {
                    X.GetProperty(0, display.screen[0].root, ev.atom, X.atoms.WINDOW, 0, 4, function (err, prop) {
                        console.log(prop);
                    });

                    X.GetProperty(0, display.screen[0].root, ev.atom, X.atoms.WM_CLASS, 0, 4, function (err, prop) {
                        console.log(prop);
                    });

                    X.GetProperty(0, display.screen[0].root, ev.atom, X.atoms.WM_NAME, 0, 4, function (err, prop) {
                        console.log(prop);
                    });
                }
            });
        }
    });
});

Here's the full output from switching windows twice:

{ type: 28,
  seq: 3,
  name: 'PropertyNotify',
  wid: 480,
  atom: 402,
  time: 47337714,
  state: 0,
  rawData: <Buffer 1c 00 03 00 e0 01 00 00 92 01 00 00 f2 50 d2 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00> }
{ type: 28,
  seq: 3,
  name: 'PropertyNotify',
  wid: 480,
  atom: 317,
  time: 47337715,
  state: 0,
  rawData: <Buffer 1c 00 03 00 e0 01 00 00 3d 01 00 00 f3 50 d2 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00> }
{ type: 33, bytesAfter: 0, data: <Buffer 01 00 c0 03> }
{ type: 33, bytesAfter: 1, data: <Buffer > }
{ type: 33, bytesAfter: 1, data: <Buffer > }
{ type: 28,
  seq: 8,
  name: 'PropertyNotify',
  wid: 480,
  atom: 402,
  time: 47342186,
  state: 0,
  rawData: <Buffer 1c 00 08 00 e0 01 00 00 92 01 00 00 6a 62 d2 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00> }
{ type: 28,
  seq: 8,
  name: 'PropertyNotify',
  wid: 480,
  atom: 317,
  time: 47342187,
  state: 0,
  rawData: <Buffer 1c 00 08 00 e0 01 00 00 3d 01 00 00 6b 62 d2 02 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00> }
{ type: 33, bytesAfter: 0, data: <Buffer fe 00 40 03> }
{ type: 33, bytesAfter: 1, data: <Buffer > }
{ type: 33, bytesAfter: 1, data: <Buffer > }

As you can see, I'm not longer getting errors, but I still can't get WM_NAME or WM_CLASS. I can confirm the windows are healthy and have the proper name:

cj@lighthouse:~/workspace/moodring$ xdotool getwindowfocus getwindowname
moodring - [~/workspace/moodring] - .../main.js - WebStorm 2017.1.4

Please advise on how to get the window properties such as name and class.

And finally, thanks so much for sharing this library!

sidorares commented 7 years ago

you need to use ev.wid instead of ev.window

try to have a look at https://github.com/santigimeno/node-ewmh ( as a library or as example )

also if you find yourself fixing bug in node-x11 examples or docs feel free to send pr so that everyone will benefit

I'll try your GetProperty code later today, on a windows machine atm

specialblend commented 7 years ago

@sidorares ev.wid returns the same value as display.screen[0].root, precisely 480, so I'm getting the same results.

I'm more than happy to continuously contribute to this library (I need node.js practice) if you can accommodate getting me started.

Edit: I tried node-ewmh with no luck. The repository seems to be 3 years old at best and only support setting and not getting properties:

var x11 = require('x11');
var EWMH = require('ewmh');
x11.createClient(function (err, display) {
    if (err) {
        throw err;
    }

    var ewmh = new EWMH(display.client, display.screen[0].root);
    ewmh.on('ActiveWindow', function (d) {
        console.log(d);
    });
    ewmh.set
});

Output

events.js:160
      throw er; // Unhandled 'error' event
      ^

Error: Could not set SubstructureRedirect to root window event_mask
    at /home/cj/workspace/moodring/node_modules/ewmh/lib/ewmh.js:19:29
    at ReadFixedRequest.callback (/home/cj/workspace/moodring/node_modules/x11/lib/xcore.js:490:39)
    at ReadFixedRequest.execute (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:41:10)
    at UnpackStream.resume (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:165:30)
    at UnpackStream.write (/home/cj/workspace/moodring/node_modules/x11/lib/unpackstream.js:102:10)
    at Socket.<anonymous> (/home/cj/workspace/moodring/node_modules/x11/lib/xcore.js:88:21)
    at emitOne (events.js:96:13)
    at Socket.emit (events.js:188:7)
    at readableAddChunk (_stream_readable.js:176:18)
    at Socket.Readable.push (_stream_readable.js:134:10)
sidorares commented 7 years ago

happy to guide you @linkintosh don't hesitate to ask any question

santigimeno commented 7 years ago

Edit: I tried node-ewmh with no luck. The repository seems to be 3 years old at best and only support setting and not getting properties:

The problem is that ewmh is supposed to be used to implement a window-manager. It you try to run it as a X11 client it won't work (setting SubstructureRedirect to the root window when other window manager is running is not allowed)

sidorares commented 7 years ago

@santigimeno I thoughts It's for both sides ( WM reporting to clients and clients listening WM events ) Would be nice to add

santigimeno commented 7 years ago

@sidorares I agree, I just implemented WM side as was my use case at the time :).

santigimeno commented 7 years ago

Back to the OP issue. @SpecialBlend, it's normal the wid you get is the root as the PropertyNotifythe event comes from the root window. To get the active window wid you need to get the _NET_ACTIVE_WINDOW property and then use that wid to get WM_CLASS, WM_NAME, etc. As an example, something like this would work: (I'm using https://github.com/santigimeno/node-x11-prop, that is just a fancy wrapper over GetProperty()):

var x11 = require('x11');
var x11prop = require('x11-prop');
var get_property = x11prop.get_property;

x11.createClient(function (err, display) {
    var X = display.client;
    X.ChangeWindowAttributes(display.screen[0].root, {eventMask: x11.eventMask.PropertyChange});
    X.on('event', function (ev) {
        console.log(ev);
        if (ev.name === 'PropertyNotify') {
            X.GetAtomName(ev.atom, function (err, name) {
                if (name === '_NET_ACTIVE_WINDOW') {
                   // get active window
                   get_property(X, ev.wid, '_NET_ACTIVE_WINDOW', 'WINDOW', function(err, wid) {
                       if (!err) {
                           console.log('_NET_ACTIVE_WINDOW: ' + wid);
                       }

                       if (wid) {
                           get_property(X, wid, 'WM_CLASS', 'STRING', function(err, data) {
                               if (!err) {
                                   console.log('WM_CLASS: ' + data);
                              }
                          });
                       }
                   });
                }
            });
        }
    });
});
specialblend commented 7 years ago

@santigimeno that works perfectly, thank you so much! and thank you @sidorares for your help as well.

I'm quite new to node.js but I will be using both libraries (node-x11 and x11-prop) extensively and I'm happy to contribute to both as I learn more. Regards