-2

I want to understand how the variable "window" which has the attribute MetaWindow can be used in different functions and vars while not being explicitly defined such as let app = this._tracker.get_window_app(window); and then passed on to other functions through a callback. Reference code here: windowAttentionHandler.js

Like here:

var WindowAttentionHandler = class {
    constructor() {
        this._tracker = Shell.WindowTracker.get_default();
        this._windowDemandsAttentionId = global.display.connect('window-demands-attention',
                                                                this._onWindowDemandsAttention.bind(this));
        this._windowMarkedUrgentId = global.display.connect('window-marked-urgent',
                                                            this._onWindowDemandsAttention.bind(this));
    }

    _getTitleAndBanner(app, window) {
        let title = app.get_name();
        let banner = _("“%s” is ready").format(window.get_title());
        return [title, banner];
    }

    _onWindowDemandsAttention(display, window) {
        // We don't want to show the notification when the window is already focused,
        // because this is rather pointless.
        // Some apps (like GIMP) do things like setting the urgency hint on the
        // toolbar windows which would result into a notification even though GIMP itself is
        // focused.
        // We are just ignoring the hint on skip_taskbar windows for now.
        // (Which is the same behaviour as with metacity + panel)

        if (!window || window.has_focus() || window.is_skip_taskbar())
            return;

        let app = this._tracker.get_window_app(window);
        let source = new WindowAttentionSource(app, window);
        Main.messageTray.add(source);

        let [title, banner] = this._getTitleAndBanner(app, window);

        let notification = new MessageTray.Notification(source, title, banner);
        notification.connect('activated', () => {
            source.open();
        });
        notification.setForFeedback(true);

        source.showNotification(notification);

        source.signalIDs.push(window.connect('notify::title', () => {
            [title, banner] = this._getTitleAndBanner(app, window);
            notification.update(title, banner);
        }));
    }
};
Sebastian Simon
  • 18,263
  • 7
  • 55
  • 75
hehe_br
  • 127
  • 1
  • 6
  • What do you mean _“while not define\[d\] anywhere in the code”_? It’s right there in the parameter list. – Sebastian Simon Apr 25 '21 at 18:19
  • @SebastianSimon, just like `let app = this._tracker.get_window_app(window);` is define[d] in a function and passed on to other functions through a callback. I just want to understand where does 'window' come from. Your comment does not help clarifying it. – hehe_br Apr 25 '21 at 21:39
  • Still, `window` is available from the parameter list, in the same way that `a` and `b` are available in `function sum(a, b){ return a + b; }`. Whichever part of the runtime calls these functions supplies the appropriate arguments. If you call `sum`, you call `sum(3, 5)` to get `8`. You wouldn’t consider `a` and `b` _“not being defined”_, would you? In exactly the same way, if the host environment (e.g. the Gnome shell) calls `WindowAttentionHandler.prototype._onWindowDemandsAttention`, it calls that function with the appropriate context and the appropriate arguments. – Sebastian Simon Apr 26 '21 at 17:39
  • @SebastianSimon, just as if I could atribute `a` to `window` and not get `Error: Expected an object of type MetaWindow for argument 'metawin' but got type undefined`. – hehe_br Apr 26 '21 at 19:34
  • Where does this error come from? I don’t see how this is related. – Sebastian Simon Apr 26 '21 at 19:50
  • @SebastianSimon, I wrote in the question "I want to understand" how the function works so I can use the same logic in my own code that is similar to the one presented in the question. – hehe_br Apr 26 '21 at 22:12
  • I have read [Where do the parameters in a javascript callback function come from?](https://stackoverflow.com/questions/34624634/where-do-the-parameters-in-a-javascript-callback-function-come-from) and it doesn't answer what is enquired here. – hehe_br Apr 27 '21 at 18:53

1 Answers1

-2

The variable window is not defined as MetaWindow in the suggested code because it borrows such status using the method .bind from the listening signal window-demands-attention in the module global.display. The method .bind is responsible for sending the type MetaWindow as this allowing it to be used in other functions with reference to the window which triggered the function _onWindowDemandsAttention in the first place:

global.display.connect('window-demands-attention', this._onWindowDemandsAttention.bind(this));

The bind() method allows an object to borrow a method from another object without making a copy of that method. This is known as function borrowing in JavaScript.

Here is an example based on the suggested code for getting the focus when the window demands the attention:

var GetFocus = class {
    constructor() {
      this.focusID = global.display.connect('window-demands-attention', this._onWindowDemandsAttention.bind(this));
    }

    _onWindowDemandsAttention(display, window) {
         Main.activateWindow(window);
    }

    _destroy() {
        global.display.disconnect(this.focusID);
    }
}

The function was not working before because I was missing the .bind.

hehe_br
  • 127
  • 1
  • 6
  • `bind` just creates the new function; it doesn’t yet call it with the argument that becomes `window`. `global.display.connect` is responsible for doing that. – Sebastian Simon Apr 26 '21 at 19:49
  • So that’s where the error comes from; you didn’t explain that in your earlier comment. Removing `bind` removes the context from the function. In other words, `this` (inside `this._onWindowDemandsAttention`) will no longer refer to that `this` value. That’s the purpose of `.bind`. But, this is not the function call itself; it’s not what’s providing the `display` and `window` arguments. That’s what I said in my previous comment. Removing `.bind` doesn’t prove anything; `bind` does not explain `window`. I also “don’t know what is wrong with you”, but please follow the [code of conduct](/conduct). – Sebastian Simon Apr 26 '21 at 22:19
  • By the way, this `context.method.bind(context)` pattern is necessary when passing a method including its context. It’s explained in [Losing “this” context in JavaScript when passing around members](https://stackoverflow.com/q/30486345/4642212). – Sebastian Simon Apr 26 '21 at 22:25
  • @SebastianSimon, even more polite by closing the question. Why did you close the question when the said duplicate does not explain how `window` get type object as `MetaWindow` while not being defined as so? `.bind` is why, and this was what I wanted to know. – hehe_br Apr 27 '21 at 15:59
  • Your current question does not include anything about the error or the type. The `.bind` call only ensures that the correct `this` can be used inside your method. It does not set the `window` argument here. The error doesn’t seem related to the `window` argument. `metawin` is something else. The duplicate target explains, where the two arguments `display` and `window` come from: `global.display.connect` expects a callback function as its second argument. `global.display.connect` then calls that function back; by calling it, the two arguments are provided. – Sebastian Simon Apr 27 '21 at 16:11
  • Since your question is still not edited, the duplicate target still perfectly applies. If you disagree, the proper response is to edit your post and explain exactly what is different and why. See [Someone flagged my question as already answered, but it's not](https://meta.stackexchange.com/q/194476/289905), ["This question already has answers here" - but it does not. What can I do when I think my question's not a duplicate?](https://meta.stackoverflow.com/q/252252/4642212). Also, there’s no need to mix “kindness” in here: this is basic content curation and is unrelated to friendliness. – Sebastian Simon Apr 27 '21 at 16:16
  • The quote from the documentation _“with a given sequence of arguments preceding any provided when the new function is called”_ does not apply here, because `.bind` is only called with a single argument: that’s the `this` context. The argument list starts from the second argument, i.e. it should’ve been something like `.bind(this, someDisplay, someWindow)`, but it isn’t. The argument list is empty, so the caller, `global.display.connect`, must provide both. That’s why this answer is wrong: _this particular_ `.bind` call does not provide a reference to `window`; it’s from somewhere else. – Sebastian Simon Apr 27 '21 at 16:18
  • @SebastianSimon, For you to understand what I mean you need to be familiar with the code suggested. `window` is just a name and you could name it whatever, but the attribute with it is not. That is why I said you cannot attribute `a` to `window` otherwise it will be set to undefined. The `.bind` allows to borrow the method it is attached to, in this case the value of the specific `MetaWindow`. – hehe_br Apr 27 '21 at 18:18
  • If one removes `.bind` from the listening signal `window-demands-attention` then, for example, the call on `var WindowAttentionSource` line 104 `Main.activateWindow(this._window);` will be rendered to undefined, because that window attribute was not sent forward, it will throw the JS error, and the window that demanded the attention will not be activated (get the focus). – hehe_br Apr 27 '21 at 18:18
  • That is the reason I asked how in the suggested code `window` could be used and reused even though its attribute was not defined anywhere in that module. The reason is that `window` is borrowing its `MetaWindow` attribute through `.bind` from `window-demands-attention` in `global.display`. I then added `.bind` in the respective signal in the code I am working on, and now `window` is working fine anywhere else. Why such enquire deserves down votes? And how the suggested duplicate answers such? – hehe_br Apr 27 '21 at 18:19
  • Is there anyone who can moderate what is happening here? Otherwise, thanks! – hehe_br Apr 27 '21 at 21:28
  • @hehe_br I think the frustration is because your question seems to be based on an incorrect assumption about how callbacks work — as far as I can understand your question, it's indeed answered by the answers that Simon linked. But you say those don't answer your question, so you must be asking something else that we don't understand. Can you clarify your question, or maybe explain what you mean by 'attribute' in "its MetaWindow attribute" or "attribute `a` to `window`"? I don't understand what that means in this context, and maybe that will help understand your question. – ptomato Apr 29 '21 at 05:53
  • @ptomato This is why I posted the question in Unix, as one needs to be familiar with GJS, JS for Gnome. This `global.display.connect('window-demands-attention',` listen to when a window in Gnome demands attention and it calls the function `this._onWindowDemandsAttention.bind(this));`. Without `.bind` none of the subsequent `window` will work, because they demand the attribute [`MetaWindow`](https://gjs-docs.gnome.org/meta7~7_api/meta.display), responsible for managing windows in GJS, and it will throw the undefined error. – hehe_br Apr 29 '21 at 16:48
  • It is not just a parameter in `_onWindowDemandsAttention(display, window)` as Simon said, `window` carries the type `MetaWindow`. Otherwise how does the command `Main.activateWindow(window);` knows which window to give focus? The same window that triggered the call sent with `bind`. – hehe_br Apr 29 '21 at 16:49
  • You mentioned `Main.activateWindow(this._window)` and `Main.activateWindow(window)`. The first depends on `this`, which `.bind` is able to set; the second one just uses the parameter which `.bind` is not able to set in this case. It looks like `this._window` needs to be of type `MetaWindow`. Of course `this.a` will be `undefined`. Is this what you mean by “attribute a to window”? That’s where the error comes from, right? But `this._window` and `window` have nothing to do with eachother. Your question is about `window`, your answer about `this._window`. Is this where the confusion comes from? – Sebastian Simon Apr 30 '21 at 04:05
  • @SebastianSimon, then look to `var WindowAttentionSource` line 60: `this._window = window;` in [windowAttentionHandler.js](https://github.com/GNOME/gnome-shell/blob/master/js/ui/windowAttentionHandler.js). – hehe_br Apr 30 '21 at 13:50
  • @hehe_br Do you mean `window` is an instance of MetaWindow? and you want to know where this MetaWindow object is created? Then I think the answer really is in https://stackoverflow.com/questions/34624634/where-do-the-parameters-in-a-javascript-callback-function-come-from — some code in gnome-shell creates this object and passes it as a parameter to your callback. It does not come from Function.bind(). – ptomato May 01 '21 at 05:01
  • @hehe_br Not sure what your point is. That line is in a different function with its own `window` parameter, in a different class. It sounds as if you’re asking why `this._window` can be used in another function; or, in other words, where the `this` context comes from which provides the `_window` property. (Correct `this` context implies `MetaWindow` type.) _That_ can be explained by `.bind`. But it is unrelated to the three _different_ `window` parameters in the code. Also, “attribute” isn’t the correct term for `MetaWindow`; it’s a _class_ (also labeled _type_ in the error message). – Sebastian Simon May 01 '21 at 12:33
  • @ptomato, my question was to understand how the window that triggered the action could be used in so many places, even though, no object `MetaWindow` was defined anywhere. If I take `bind` from the first connect, no function with the `window` will work because they need to be attached to the window that created the action. What I did not know and I found out is the role of `bind`, making the link between the window demanding attention with other functions as giving it focus, or its title, etc. Tell me then how I get `window` on line 33 to work without the `bind` in line 13? – hehe_br May 01 '21 at 16:07
  • @hehe_br The `window` in `if(!window`…`)` (line 33) is not related to the `.bind(this)` (line 13), period. `window` is a parameter passed by calling the function `this._onWindowDemandsAttention.bind(this)` from inside the native function `global.display.connect`. Note that `this._onWindowDemandsAttention.bind(this)` itself is not the function call. `.bind(this)`, on the other hand, makes `this._tracker` and `this._getTitleAndBanner` point to the correct values inside `_onWindowDemandsAttention`. It doesn’t affect the parameter `window`. – Sebastian Simon May 01 '21 at 16:24
  • @SebastianSimon, yes, now you got what I wanted to know `Correct 'this' context implies MetaWindow type. That can be explained by '.bind'`. The 'attribute' I used referred to your answer that it is defined in the parameters like `function sum(a, b){ return a + b; }`, but what do you get with `sum(-,8)` or `sum(a,z)`? `ReferenceError: a is not defined`, it expects a specific value. Still in `sum(3, 5)` it is attributed `3, 5` to `a, b` but in the code suggested `window` is not defined. – hehe_br May 01 '21 at 16:30
  • @SebastianSimon, It is the same `window` throughout: the window demands attention > it creates a message it is ready with the window title > and if you click the message it opens the window. – hehe_br May 01 '21 at 16:32
  • The error that you see when removing `.bind(this)` is caused by a function expecting a `MetaWindow` object, but receiving `undefined` (`this.a`, or `this._window` without `.bind(this)`). This is not related to the `window` _parameter_, but to a property on `this` (e.g. `this._window`). _“no object `MetaWindow` was defined anywhere”_ — It doesn’t need to be created in this piece of code. `this._window` is passed to `Main.activateWindow`, so what is `this._window`? In `_init` it says `this._window = window;`, so what is `window`? A parameter passed to `_init` by whichever function calls `_init`. – Sebastian Simon May 01 '21 at 16:36
  • `sum(-, 8)` isn’t valid syntax, so I don’t know what you expect here. `sum(a, z)` works fine if `a` and `z` exist in scope. Parameters are only scoped _inside_ their functions; the `ReferenceError` tells you that the _outside_ `a` (when calling `sum`) is not defined, not the _inside_ `a`. `window` is a parameter, i.e. in the _inside_ scope. How do you determine that “`window` is not defined”? You cannot see the code that calls the `_onWindowDemandsAttention` function. But this code supplies the `window` argument. And inside `_onWindowDemandsAttention`, it, again, _is_ defined. – Sebastian Simon May 01 '21 at 16:45
  • @SebastianSimon, if so, try to run the mini example I placed in the answer, and tell me how do you get the window that triggered the function `_onWindowDemandsAttention` to be activated without `.bind`. That was my struggle and it seems it is yours too. – hehe_br May 01 '21 at 16:54
  • Uh, yes, the code you posted works perfectly fine without `.bind(this)`, which makes sense because `_onWindowDemandsAttention` does not make use of `this`. If you still get an error, look at the stacktrace to find the origin of the error, and add a [mre]. – Sebastian Simon May 01 '21 at 17:30
  • @SebastianSimon, not for me in my Gnome, without `.bind` it throws: `JS ERROR: TypeError: window is undefined`. With `.bind` it works just fine and the window is activated. The minimal example, and the whole code was suggested in the question. – hehe_br May 01 '21 at 17:48
  • And that error occurs in `activateWindow@resource:///org/gnome/shell/ui/main.js:674:30`; and `window === undefined`? Is `display === undefined` for you as well? Can’t reproduce this behavior in Gnome 40 with X11 (Arch Linux). There should be no reason why removing `.bind(this)` fails, even if it’s Ubuntu or Wayland or whatever. Either the mistake is somewhere else or it’s a bug. Does `.bind({})` instead of `.bind(this)` work? – Sebastian Simon May 01 '21 at 18:21
  • And just to confirm: by removing `.bind` you mean removing `.bind(this)`, right? You’re not calling `this._onWindowDemandsAttention(this)`, correct? – Sebastian Simon May 01 '21 at 18:29
  • @SebastianSimon, just removing `.bind` as `...this._onWindowDemandsAttention(this));` Gnome 40 with X11 here as well! The code suggested in the answer works perfectly. – hehe_br May 01 '21 at 18:36
  • So… you’ve been testing `global.display.connect('window-demands-attention', this._onWindowDemandsAttention(this))` all this time and not `global.display.connect('window-demands-attention', this._onWindowDemandsAttention)`? Wow… what a huge misunderstanding. The additional `(this)` makes it a function call. You don’t want to call the function here! `global.display.connect` expects the function itself! And `this._onWindowDemandsAttention` _is_ that function, _not_ `this._onWindowDemandsAttention(this)`. That also explains why the second parameter, `window`, is `undefined`: it isn’t passed. – Sebastian Simon May 01 '21 at 18:42
  • I do want to call a function here when I need that `window` reference to be used in another var, for instance. – hehe_br May 02 '21 at 08:23
  • Well, of course the function should be called eventually, but _not here_. `global.display.connect` is like `addEventListener`. See [addEventListener in JavaScript triggers the click event automatically](/q/11489734/4642212). Again, `global.display.connect` expects a function as its second argument. `this._onWindowDemandsAttention(this)` is not a function; it’s `undefined`, because `this._onWindowDemandsAttention` does not return anything. `this._onWindowDemandsAttention` itself _is_ a function. You need to pass the function, not call it. The function will be called later as an event listener. – Sebastian Simon May 02 '21 at 20:58
  • …which is why `this._onWindowDemandsAttention.bind(this)` also worked fine, even if the `.bind(this)` is superfluous: `.bind` returns a new function, therefore `this._onWindowDemandsAttention.bind(this)` _is_ a function. It does _not call_ `this._onWindowDemandsAttention`. Also relevant: [In JavaScript, does it make a difference if I call a function with parentheses?](/q/3246928/4642212) and [What is the difference between a function call and function reference?](/q/15886272/4642212). – Sebastian Simon May 02 '21 at 21:01