sidorares/node-x11

Missing async handling (?)

Airblader opened this issue · 10 comments

If I call ChangeWindowAttributes, I can only define an error callback. Is there any way to wait for success? Currently, if I register as the window manager, I am forced to start managing windows before even knowing that no other window manager is active.

Why don't you perform a GetWindowAttributes on root and then if no other WM you ChangeWindowAttributes while grabbing the server? Something more or less like this:

X.GrabServer();
X.GetWindowAttributes(root, function() {
    if (no WM) {
        X.ChangeWindowAttributes(root, ...., function(err) {
        })
    }

    X.UngrabServer();
});

Thanks for the reply. I didn't know xcb_grab_server and don't seem to be able to find (meaningful) documentation on what exactly it does?

Edit: Does node-x11 implement xcb_request_check? It doesn't look like ChangeWindowAttributes returns a cookie, though. Basically, is there any way to query the state of the request?

From the X11 protocol spec:

GrabServer: This request disables processing of requests and close-downs on all connections other than the
one this request arrived on.

So you can perform atomic operations.

Regarding the success callback I don't know how would it be done taking into account that the ChangeWindowAttribuetes request does not generate a response but in case of error

Ah, thanks for that. I was only looking for XCB and didn't find anything.

Regarding the last part, I realized that after (sorry, my mistake). It makes sense that there is no "success" callback, simply because there, well, isn't one. I guess what I'm missing is the equivalent of this:

xcb_void_cookie_t cookie = xcb_change_window_attributes( … );
xcb_generic_error_t *error = xcb_request_check( connection, cookie );
if( error != NULL ) {
    // die
}

// start managing windows

Is there any way to get the cookie and query it like this? Or to block until I can know that the error callback will not be triggered in node-x11? I'm not sure I understand your proposed solution. To decide whether a window manager is running (never mind that this is just a special scenario anyway) I need the ChangeWindowAttributes request. In your example, X will be ungrabbed when GetWindowAttributes is answered, but the ChangeWindowAttributes call will likely terminate only after that still, no?

I think the cookie is a xcb artifact, not something defined in the protocol.

I think that the requests in the same connection are handled sequentially by the server so after calling ChangeWindowAttributes you could call GetWindowAttributes and see if it succeeded, then ungrab. Not completely sure though. Does it make sense to you @sidorares?

I think the cookie is a xcb artifact, not something defined in the protocol.

Mh, I think so, too. The X protocol doesn't seem to know "RequestCheck" either for the same reason.

after calling ChangeWindowAttributes you could call GetWindowAttributes and see if it succeeded, then ungrab

I guess I could do that, though it basically means making a bogus call to X11 to time myself. I barely know the X protocol, though, so this might be as good as it gets. At least I couldn't find any event that might be triggered by this for which I could look.

@Airblader yes, this is how X11 protocol works. I'll try to explain in more details:

  1. when client connects, it starts with request sequence number=1
  2. each time request is sent number is incremented
  3. if there is a response ( with data or error as payload ) server respond with a packet with matching seq number

I think that's not good that ChangeWindowAttributes callback may not be called at all, this is against node CPS style. What we can do is when client sees response with sequence > the one already in client queue it should assume there was no error and no expected payload and invoke callback as callback(null) and remove it from the queue

@sidorares A couple of thoughts:

  • As I understand, this should be done for every request that can generate an error response, right?
  • What if after a request no other response arrives (never or for a while)? Should there be a timeout to call the success callback? What should its interval be?

I think there's no benefit in calling callback(null) since I still need to make the extra request to trigger this. It only allows separating the reaction to it better. But if the X protocol simply doesn't give us anything better we'll have to live with it. :)

Some problems still need to be addressed:

  • callback parameter does not follow node CPS convention ( should be called once and exactly once ). Only called on error for some requests. There is no way to tell if we expect response or not from the data itself, it's part of each request documentation. The best we can do is to keep list of "always return data" and "only when error".
  • If request is called with callback, reference to a function stays in the map until response. If you do a lot of X.DoSomething(param, callback) and there is no error the map would grow over time.
  • worse, sequence id is only 2 bytes unsigned, and reset to 0 after 65535. This means that original callback might be invoked incorrectly as result of error from another request.

Possible solutions:

  • mark functions that are not expected to return data and not allow callbacks there. Force to use connection.on('error', blah) for error handling
  • or, return event emitter from such (or all?) functions and do X.DoSomething(params).on('error', blah). This would solve first problem ( not following CPS ) but still leaves second
  • assume responses are send sequentially and delete all pending callbacks with seqNum < currently received