Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

App#windows() does not include windows that are not in the current space #131

Open
rjlasko opened this issue Aug 13, 2016 · 15 comments
Open

Comments

@rjlasko
Copy link

rjlasko commented Aug 13, 2016

  • Version: 2.3.49
  • macOS: 10.11.3

Let's say that I am in laptop only mode, with two spaces open, and an application having a window in each space.

I don't believe this applies in this case, but note that in Mission Control, I have the following settings:
screen shot 2016-08-13 at 12 25 14 pm

if i run the following code:

keys.push(
    new Key('3', altButton, function() {
        var apps = App.all()
        for (var i = 0; i < apps.length; i++) {
            var app = apps[i]
            if (app.name() == "Sublime Text") {
                Phoenix.log("app.windows() = " + app.windows())
                for (var j = 0; j < app.windows().length; j++) {
                    var w = app.windows()[j]
                    Phoenix.log("windows[" + j + "].title() = " + w.title())
                    Phoenix.log("windows[" + j + "].screen().hash() = " + w.screen().hash())
                    Phoenix.log("windows[" + j + "].spaces() = " + w.spaces())
                    for (var k = 0; k < w.spaces().length; k++) {
                        var s = w.spaces()[k]
                        Phoenix.log("windows[" + j + "].space[" + k + "].hash() = " + s.hash())
                    }
                }
            }   
        }
    })
);

For the following cases I get the following output in Console:

A. The app has both windows (visible) in the first (currently active) space

8/13/16 4:14:20.032 PM Phoenix[8074]: app.windows() = [object PHWindow],[object PHWindow]
8/13/16 4:14:20.035 PM Phoenix[8074]: windows[0].title() = artifacts.xml
8/13/16 4:14:20.035 PM Phoenix[8074]: windows[0].screen().hash() = 2077752381
8/13/16 4:14:20.036 PM Phoenix[8074]: windows[0].spaces() = [object PHSpace]
8/13/16 4:14:20.036 PM Phoenix[8074]: windows[0].space[0].hash() = 3
8/13/16 4:14:20.040 PM Phoenix[8074]: windows[1].title() = phoenix.js
8/13/16 4:14:20.040 PM Phoenix[8074]: windows[1].screen().hash() = 2077752381
8/13/16 4:14:20.040 PM Phoenix[8074]: windows[1].spaces() = [object PHSpace]
8/13/16 4:14:20.041 PM Phoenix[8074]: windows[1].space[0].hash() = 3

B. The app has one window in the first space (which is currently active), and a second window in the second (non-visible) space:

8/13/16 4:14:43.246 PM Phoenix[8074]: app.windows() = [object PHWindow]
8/13/16 4:14:43.249 PM Phoenix[8074]: windows[0].title() = phoenix.js
8/13/16 4:14:43.249 PM Phoenix[8074]: windows[0].screen().hash() = 2077752381
8/13/16 4:14:43.250 PM Phoenix[8074]: windows[0].spaces() = [object PHSpace]
8/13/16 4:14:43.251 PM Phoenix[8074]: windows[0].space[0].hash() = 3

C. Same as B, but If switch to the second space, and rerun:

8/13/16 4:14:56.452 PM Phoenix[8074]: app.windows() = [object PHWindow]
8/13/16 4:14:56.454 PM Phoenix[8074]: windows[0].title() = artifacts.xml
8/13/16 4:14:56.454 PM Phoenix[8074]: windows[0].screen().hash() = 2077752381
8/13/16 4:14:56.455 PM Phoenix[8074]: windows[0].spaces() = [object PHSpace]
8/13/16 4:14:56.455 PM Phoenix[8074]: windows[0].space[0].hash() = 4
@kasper
Copy link
Owner

kasper commented Aug 13, 2016

Hmm, interesting. App#windows() asks the Accessibility API to return all windows. No tricks. I wonder if this is considered by Apple as the desired result.

@kasper
Copy link
Owner

kasper commented Aug 13, 2016

Oh, and a quick hint. App.get(appName) might be handy.

@kasper
Copy link
Owner

kasper commented Aug 13, 2016

I think this is again caused by the “Displays have separate Spaces” option being disabled. If that is the case, I’m not sure what we can do about it. Could you again test this theory?

@rjlasko
Copy link
Author

rjlasko commented Aug 14, 2016

Yeah, that is what i thought that it might be too, but I double checked it and verified that the behavior is the same when the option is both enabled & disabled. Hard to believe, right? Does it do the same for you?

@mafredri
Copy link
Contributor

Did you log out between enabling / disabling the separate spaces option?
It's required for it to take fully effect I believe.

On Sun, 14 Aug 2016, 11:58 p.m. Robert Lasko, notifications@github.com
wrote:

Yeah, that is what i thought that it might be too, but I double checked it
and verified that the behavior is the same when the option is both enabled
& disabled. Hard to believe, right? Does it do the same for you?


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#131 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AAI_0ffERWelp5D8pfNQjgUPN0rWY4Xaks5qf4FWgaJpZM4JjwOj
.

@rjlasko
Copy link
Author

rjlasko commented Aug 14, 2016

Yes, i did logout. I've triple verified that it isn't just some wacky user (me) error. Am I the only person that is experiencing this?

@rjlasko
Copy link
Author

rjlasko commented Aug 14, 2016

this should be really easy to replicate. here is the code i am using to indicate the problem in Console:

keys.push(
    new Key('3', ["alt"], function() {
        var app = App.get("APP_NAME_GOES_HERE")
        Phoenix.log("app.windows() = " + app.windows())
        for (var j = 0; j < app.windows().length; j++) {
            var w = app.windows()[j]
            Phoenix.log("windows[" + j + "].title() = " + w.title())
            Phoenix.log("windows[" + j + "].screen().hash() = " + w.screen().hash())
            Phoenix.log("windows[" + j + "].spaces() = " + w.spaces())
            for (var k = 0; k < w.spaces().length; k++) {
                var s = w.spaces()[k]
                Phoenix.log("windows[" + j + "].space[" + k + "].hash() = " + s.hash())
            }
        }
    })
);

open up your app with two windows in separate spaces, and then Alt+3

@kasper
Copy link
Owner

kasper commented Aug 15, 2016

@rjlasko I haven’t encountered this before and definitely have been able to get windows from different Spaces. (For example Space#windows() wouldn’t work otherwise. Including many other APIs.)

Could this be a behaviour related to a certain app? Have you verified this behaviour with multiple apps?

@kasper
Copy link
Owner

kasper commented Aug 18, 2016

@rjlasko Ok, sorry I retract my previous comment — I probably have only tried fetching windows from different but active spaces. I actually can reproduce this and guess what, unfortunately this is how Apple seems to have implemented it! You only get the windows for the active space. I wonder why this hasn’t come up before!

Other window managers seem to hack this by listening to window events (open and close). 👎

@rjlasko
Copy link
Author

rjlasko commented Aug 20, 2016

Sorry for the delayed reply... So there are actually two related issues here.

  1. App#windows() not returning windows in other non-active spaces.
  2. Space#windows() not returning windows if not an active space.

That is a bummer, however I guess that not all is lost. My final goal was to be able to reorganize all active apps onto different predefined spaces on a single-display system. Maybe there is still a workaround available for this involving navigating to all spaces, and performing operations while the active space has been altered. I will report my success after I have tested it out.

@rjlasko
Copy link
Author

rjlasko commented Aug 20, 2016

Actually, i don't think that a workaround is possible, is it? I was thinking about moving a window to a different space and then focusing on it to transition, but that is not enough if i cannot add/delete a space.

There is no API that allows the creation or deletion of a space, correct? If so, then spaces support is ultimately limited to what is already within the set of active (visible) spaces. =(

Please prove me wrong.

@kasper
Copy link
Owner

kasper commented Aug 20, 2016

@rjlasko Well the Spaces support is minimal, because Apple simply doesn’t give public APIs for the feature. To limit issues, we only use the private APIs that make sense. Creating and removing spaces with the private APIs require you to kill the Dock with every change. Not really practical. So you need to preset the Spaces with the UI.

You can however switch to a Space easily by focusing to an app/window that is located there. Obviously this is made harder by the fact that you can’t get windows from different Spaces if they are there to start with (but if you move a window to a different Space, you already have the reference to it). App#windows() and Space#windows() rely on Window#all(). Since Window#all() is not returning windows from other Spaces than the current one (but still from all current Spaces for different screens), the whole chain breaks in this regard. This seems to be the case with Hammerspoon as well. I still am baffled on why I haven’t noticed before (since I’ve manually tested these when they were implemented).

To improve this, I guess we would need to start with the windows returned by the Accessibility API and then listen to events to add windows created and deleted in other Spaces than the current (you could try this yourself). Hammerspoon seems to do this in the filter extension. Not sure if this would work, since you could already have windows in different Spaces. I’m confused why Apple has implemented this how it is. I would expect if I ask the Accessibility API to “return me all windows”, it would return all windows. Since it returns windows from different screens anyway (and screens can have different Spaces).

@kasper
Copy link
Owner

kasper commented Aug 20, 2016

I guess the main idea is that you cannot create windows in other Spaces than the current one (for every screen). Not even from the UI, new windows open to the current Space for the active screen.

You could have your Spaces preset in the way you prefer. Open the windows in the current Space and then add/move them to an other one. This should work just fine.

@kasper kasper changed the title App#windows() does not include windows that are not in the current space. App#windows() does not include windows that are not in the current space Nov 19, 2016
@joeshaw
Copy link

joeshaw commented Sep 10, 2018

FYI, App#mainWindow() will return a window even if it is on a different space. It's not ideal, but it's better than nothing.

@robotastronaut
Copy link

robotastronaut commented Jan 16, 2024

A quick update on this: I noted that App#windows() will return windows on a different space if those windows are minimized. If you're trying to move all of an app's windows from an inactive space to the current active space, you can do something hacky like the following to get it to work:

function moveAppToCurrentSpace(name) {
    let app = App.get(name)
    if (!app) return
    const space = Space.active()
    while(app.mainWindow() !== undefined) {
        app.mainWindow().minimize()
    }
    space.moveWindows(app.windows())
    app.windows().forEach(w => w.unminimize())
    app.focus()
}

This isn't without some wonkiness. The minimize animation, for instance, will occur on the new space. Some apps (WezTerm, for instance) seem to destroy their windows rather than minimize them. I don't actually recommend using the snippet above, but it does point to something smelly, in my opinion.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

5 participants