How to List All Windows of One App That Are Open in All Desktops

I would like to be able to generate a list, and hopefully access one-by-one, all the open windows of any specific app no matter what desktop they are in. Many apps have a Windows menu that lists all the open windows, but that seems like I would have to have specific macros to manage the lists for each different app. That's not ideal.

Specifically, to start, I want to list all the open TextEdit windows and all the open Typora windows. They have very different UIs for showing that list themselves.

And I'm stuck at where to go.

I found one KBM token that comes close, %WindowName%All%, but it just lists all the windows of the current app in the current desktop. I want to expand that to list all the windows of the app in all desktops. If I have three TextEdit windows in the current desktop and six other TextEdit windows in other desktops, if I display %WindowName%All% when one of the TextEdit widows is formost, it shows a list of the three windows in the current desktop.

I imagine KBM has some built-in way to do it, but I haven't been able to find it. I could step through all the different desktops, one after another, and see if it has any TextEdit windows and then select one and then use %WindowName%All%, but that seems inelegant.

Any suggestions?

Here's an example. And it does work if windows are open on different screens.

image

This does not work with windows on different desktops.

Neither Keyboard Maestro nor AppleScript UI-Scripting can see windows in different desktops.

So this for instance will not work:

tell application "System Events"
   tell application process "TextEdit"
      set winNameList to name of every window
   end tell
end tell

An AppleScriptable app like TextEdit will let you get every window no matter what desktop it's in.

tell application "TextEdit"
   set winNameList to name of every window
end tell

But not all apps are AppleScriptable...

This is dismally bad UI by Apple – especially considering how long desktops have been available on macOS 10+.

The guy with the most success managing desktops with Keyboard Maestro is @_jims. See this thread:

MACROS: Desktop Spaces • Macros for Navigation and Window Management, v1.1 (Superseded)

I'd just ask Jim directly if his system can do what you want.

If not I think you're stuck with a utility like Witch or something like it.

-Chris

1 Like

Many apologies. I saw "Desktops" but thought "Screens". I guess we may be actually talking about Spaces?

I tried Spaces long ago, they didn't work well and I don't think of them much any more. It's true, I only tested with multiple monitors in a single space.

2 Likes

Sorry for the confusion, @devoy, sometimes I remember to call them Desktops/Workspaces. Both Apple's doc and UI are pretty inconsistent. Mission Control calls them Desktop 1, Desktop 2, etc.

@ccstone / Chris, are you serious that AppleScript cannot see other Mission Control Desktops? That's absurd. And, unfortunately, I believe you. Do you know how to tell what is an AppleScriptable app and what is not? For instance, just this week I started using a new Markdown Editor called Typora. How could I look up somewhere or test if it is AppleScriptable?

I currently have 21 Desktops/Workspaces between two monitors. (I don't want to try imagining how to live without them.) I also currently have 23 TextEdit windows open. Using the Windows menu in TextEdit, I can get a list of the filenames, but it doesn't show me which Desktop/Workspace each one is in. That's what I was hoping I could entice KBM to do for me.

I guess I'll just have to step through each of 21 desktops and generate a list of what window are open and then filter, sort, and format the combined list into something useful. Definitely not a sweet solution.

But that just begs the question, How to List All Open Windows on a Desktop

Thanks.

Hey August,

I'm quite serious, but I did forget to mention one thing.

System Events can see the windows in the desktop you're currently IN.

It cannot see the windows that are in open desktops your are NOT currently IN.

This is just flaming stupid. Apple should have added a desktop property to System Events' window properties long ago, but they haven't.

For that matter Apple's UI for managing desktops continues to be abysmal – far better has been done with Mac utilities and on other flavors of Unix.

Of course – drop the app on the Apple Script Editor.app or on Script Debugger. If you see an AppleScript dictionary open the app is scriptable. HOWEVER – a “scriptable” app may or may not have more than the default AppleScript suite.

The default suite only adds a few commands and classes to work with – for example window bounds and window size and position.

Once you know what you're looking at you can quickly determine if an app has more than the default suite, but it takes a little education to get there.

Typora 0.11.5 is a perfect example of default-suite-only.

It hasn't seen any love from the author for years until this August.

The AppleScript default-suite is new, so maybe he'll add some Typora-specific commands – especially if users ask for them (hint).

-Chris

Thanks Chris.

Because he responded to a support question I had, I have the direct email address for Abner at Typora, so I requested better programmatic access, either an API or more AppleScript access than the default.

Hey August,

Great!

Would you point out to him that the default suite isn't fully connected to the app?

tell application "Typora"
   its class
   its frontmost
   its name
   # its properties -- fails
   its version
end tell

tell application "Typora"
   tell front window
      its bounds
      its class
      its closeable
      its document
      its floating
      # its miniaturizable -- fails
      # its miniaturized -- fails
      its modal
      its name
      # its properties -- fails
      its resizable
      its titled
      its visible
      its zoomable
      its zoomed
   end tell
end tell

tell application "Typora"
   tell front document
      its class
      its modified
      its name
      # its path -- fails
      # its properties -- fails
   end tell
end tell

Welcome additions:

  • Get/Set entire text of the given document.
  • Get/Set the selection of the front document.

That's all I can think of right off the top. Please add any specific requests you might have to my list.

-Chris

Thanks Chris,

I am a newbie at AppleScript. Is there a property that will tell you which desktop/workspace Any particular window is in?

If you’re willing to use JXA, the Core Graphics framework might be able to enumerate the full list of windows.

It contains some low-level C APIs, one of which in particular seems pertinent:

CGWindowListCopyWindowInfo().

It takes two arguments, which you can probably just pass 0 for each:

ObjC.import('CoreGraphics');
ObjC.deepUnwrap($.CGWindowListCopyWindowInfo(0, 0));
2 Likes

Hey @CJK,

Oh? That looks quite spiffy.

Looks like every window on the system is enumerated.

I just verified that it sees windows in desktops other than the current one too!

Good one!

Now one has to learn how to extract data from the collection, because you don't really want to parse that mass of text...

-Chris

I got a reply from Abner at Typora.

Currently Typora does not provide more AppleScript APIs than default, but I guess get opened window / file should be possible.

like macos - Listing all windows of all applications - Stack Overflow
or getting list of open windows - AppleScript | Mac OS X - MacScripter
or macos - Listing all opened documents across all open visible apps in AppleScript - Ask Different

Sorry, I'm not an expert for AppleScript, but hope those links help.

I haven't had time to look into those links but I posted them for anyone else following this thread.

Abner doesn't even have the default AppleScript suite hooked up properly.

None of the links he gave you is able to work with apps in spaces other than the one you're in.

The best hope so far is the code @CJK provided.

-Chris

Sorry for not checking back sooner.

Here’s a function that returns more manageable data:

ObjC.import('CoreGraphics');

Ref.prototype.$ = function() {
	return ObjC.deepUnwrap(ObjC.castRefToObject(this));
}

Application.prototype.getWindowList = function() {
	let pids = Application('com.apple.systemevents')
	          .processes.whose({ 'bundleIdentifier':
			        this.id() }).unixId();

	return  $.CGWindowListCopyWindowInfo(
		    $.kCGWindowListExcludeDesktopElements,
		    $.kCGNullWindowID).$()
			 .filter(x => pids.indexOf(x.kCGWindowOwnerPID) + 1
			           && x.kCGWindowLayer     == 0
					   && x.kCGWindowStoreType == 1
					   && x.kCGWindowAlpha     == 1
			).map(x => [{ id     : x.kCGWindowNumber,
			              name   : x.kCGWindowName,
			              bounds : x.kCGWindowBounds  
					   }]);
}

To use, e.g. to get Terminal’s windows:

Application('Terminal').getWindowList();

Hey @CJK,

I'm happy to see this.

Unfortunately it doesn't work on my Mojave system.

It seems to be breaking down here:

kCGWindowListExcludeDesktopElements,
          $.kCGNullWindowID).$' is undefined)

-Chris

Thanks Chris,

Since I'm on Mojave too, that's an important glitch to me.

1 Like

Hi Chris,

It appears to be a bit fixed in the current version of Typora, v1.1.5, out of Beta where it had been for years, and now costing $15 instead of free. (There is a free trial mode with a reminder/nag window that counts down the trial period.)

When I open Script Editor and look at the Typora Dictionary, all the items you listed above as "fails" are now defined in the dictionary doc. Does that mean they work now?

However, "selection" is not defined. Should it be? Or is it accessed another way? I don't find "selection" in the TextEdit dictionary either, so I think I must be missing a concept of operation here. When I google "applescript get selection" I get some advice on how to save the clipboard, send the ⌘+C keystroke, save that in a variable, and restore the clipboard. So it seems get selection isn't well supported in other scriptable apps.

If I create an AppleScript script something like this:

tell application "Typora"
    its visible
end tell

Is running that in the Script Editor enough? Do I need to make a KM macro to run it while Typora is frontmost? Putting it into the script editor with a delay 3 ahead of it lets me click the Run button and then make Typora frontmost.

When i use visible there I get an error and when I use frontmost I get true in the Results pane of the Script Editor. So I'm still not sure how to tell if the AppleScript support got fixed in the Typora 1.1 version. Can you tell?

Thanks for your help getting me this far.

Hey @ccstone / Chris,

I kind of got sidetracked into trying to make Typora work the way I would like it to, but that's not necessarily the direct path to my OP problem.

Were you ever able to get @CJK's function to work?

I'm realizing that one of the purposes I had for the original question (but not all) was to be able to tell one desktop from another, to tell when a desktop had been moved, etc. and I think I can do that using some one specific app that is well behaved that way. Maybe TextEdit, maybe something else. The idea here is to emulate what CurrentKey does invisibly by having some app that has one window on each and every desktop and using that to force window changes.

Maybe I build a small app, maybe I dedicate some app that I don't really use for anything else, so that leaves out TextEdit. Still sorting this one out.

About Mojave vs Catalina

I'm getting set to give up Mojave for Catalina. I don't use my 32-bit apps much anymore and there are free or affordable alternatives to the ones I do. Mostly the threshold for me is that sharing Notes between my iPhone and my Mac, on Mojave I can share only individual files with another person while on the iPhone I can share whole folders with someone else, and then they disappear from the Mac because Mojave doesn't grok shared folders. Moving to Catalina should fix this as well as give me updates to some other software that no longer supports Mojave.

I spent a day generating a list of all the 32-bit apps I had (there were 145 of them) and going through to see what I would miss if I deleted them (mostly Office 11 and a.bunch of Adobe stuff that I don't use or the licence has expired). The big one has been Acrobat Pro X, but some of the features I have been counting on are now in Acrobat Reader and others are available as free web utilites. Worst case is I subscribe for a month if there's something I HAVE TO have.

Sorry for the long Mojave/Catalina digression.

Did you test it for yourself ? I don't have Mojave, so I'm afraid I cannot, but I'm happy to report that it still works in Monterey. However, using the info that @ccstone provided about the possible error-prone statements, I've refactored the previous function to remove references to those elements:

Application.prototype.getWindowList = function() {
        ObjC.import('CoreGraphics');
        let pids = Application('com.apple.systemevents').processes
                   .whose({'bundleIdentifier':this.id()}).unixId();
        return  ObjC.deepUnwrap( ObjC.castRefToObject(
                $.CGWindowListCopyWindowInfo(16, 0) )).filter(e =>
                pids.includes(e.kCGWindowOwnerPID) &&
                              e.kCGWindowLayer==0  &&
                              e.kCGWindowAlpha==1  &&
                              e.kCGWindowStoreType==1).flatMap(x => 
                              [{ id     : x.kCGWindowNumber,
                                 name   : x.kCGWindowName,
                                 bounds : [ 'X','Y',
                                            'Width',
                                            'Height' ].map(z => 
                                          x.kCGWindowBounds[z]) }]);
}

As before, to use the function:

Application('<app name_or_id>').getWindowList()
2 Likes

Thanks @CJK, I appreciate your effort and I look forward to trying it. Unfortunately that could be a month away. Only being able to work on this every few months is frustrating. By the time I get back up to speed on the problem, my time window is nearly gone.

Could I ask a favor IFF you have time and interest? Would you be willing to wrap that function in a KM call to AppleScript (or whatever it takes) to demo it? Please don’t if it wouldn’t be fun.

I’m asking because I can get further faster if I can start with a working demo than if I first have to make something that works at all and then apply it to my problem.

Thanks!