How to List All Open Windows on a Desktop

I would like to be able to create a snapshot list of where I am when I have to put a project aside for a while. Bookmarking all my open Chrome tabs I have handled. But how to automatically generate a list of all other open windows on any particular desktop?

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 all apps in the current desktop.

And I'm stuck at where to go.

I imagine KBM has a built-in way to do it, but I haven't been able to find it.

Any suggestions?

Hey August,

Not as far as I know.

Keyboard Maestro can list apps and the windows of the front app, but I don't think it can list windows of arbitrary apps.

System Events can though.

--------------------------------------------------------
# Auth: Christopher Stone
# dCre: 2021/09/17 17:11
# dMod: 2021/09/17 17:11 
# Appl: System Events
# Task: List All Windows of All Applications in the Current Desktop (Space).
# Libs: None
# Osax: None
# Tags: @Applescript, @Script, @System_Events, @List, @Windows, @Desktop
--------------------------------------------------------

tell application "System Events"
   tell (application processes whose background only is false)
      set appNameList to name
      set appWindowNameList to name of windows
   end tell
end tell

set _cntr to 1

repeat with appName in appNameList
   set contents of appName to {"# " & appName & " #" & linefeed} & item _cntr of appWindowNameList & ""
   set _cntr to _cntr + 1
end repeat

set AppleScript's text item delimiters to linefeed

return appNameList as text

--------------------------------------------------------

-Chris


Addendum – 2022/10/03 15:01 CDT


Please Note – the script lists only lists windows from foreground apps.

application processes whose background only is false

If you want to catch windows from non-normal apps then:

application processes whose background only is true

Or you can talk to a specific app directly:

tell application "System Events"
   tell application process "Keyboard Maestro Engine"
      set winList to every window
   end tell
end tell

Or you can try to get every possible window:

tell application "System Events"
   tell (every application process)
      set appNameList to name
      set appWindowNameList to name of windows
   end tell
end tell

set _cntr to 1

repeat with appName in appNameList
   set contents of appName to {"# " & appName & " #" & linefeed} & item _cntr of appWindowNameList & ""
   set _cntr to _cntr + 1
end repeat

set AppleScript's text item delimiters to linefeed

return appNameList as text

Note that some apps (particularly background apps) may not have proper names for their windows. LaunchBar is one such app.

-ccs


2 Likes

Thanks, Chris (@ccstone),

I finally tried this script out. It looks like a good starting point for what I have in mind. I'll keep you posted on that.

I made a small mod to the output to make it more compact. I changed the line to:

   set contents of appName to {"# " & appName & " :" } & item _cntr of appWindowNameList & ""

I'm really curious why you appended an empty string, "" at the end. Why not just end the line with the desired element of appWIndowNameList?

With that mod, here's my output for my KBM desktop:

# Script Editor :
a03AM-CallKBMOnSpaceChange.scpt
Untitled

# Keyboard Maestro :
New Action
Keyboard Maestro Editor — List All WIndows of Current Desktop

# Google Chrome :
How to List All Open Windows on a Desktop - Questions & Suggestions - Keyboard Maestro Discourse - Google Chrome - August

# TextEdit :
Window= K KeyboardMaestro.rtf

# CurrentKey Stats :
K KeyboardMaestro

# Brave Browser :

# FoldingText :

# TextDo :

# System Preferences :

# Notes :

# Safari :

# TextMate :

# Terminal :
Keyboard Maestro — -bash — 80×24

# Activity Monitor :

# Typora :

# Finder :
Keyboard Maestro

# a03AM-CallKBMOnSpaceChange :

There's the Finder window open at my Keyboard Maestro folder, a Terminal window open at the same directory, TextEdit open with my Keyboard Maestro.rtf file, Google Chrome open at this page (the other 12 tabs are not listed), etc. And several open apps with no windows on this Desktop. Every other window I can account for, except one: The "New Action" window in KBM. I figure that's the overlay window in the editor.

So this is really good info.

Of note is the Current Key window which I believe is the hidden window (positioned behind the toolbar, IIRC from other window data) that CK uses to switch focus to and thus switch to this desktop on demand.

As you might have gathered from other posts of mine, because Current Key has been withdrawn because the author does not want to support Monterey, etc., I'm looking to find a way to emulate the feature of CK that matters most to me, the Desktop identification and switching functions.

This script above is another step towards that.

Thank you.

When converting the list to text it adds a linefeed.

Hi Chris (@ccstone),

I've been using your script and have encountered an intermittent bug.

Repeatedly, but not always, when I run the script it misses some windows, most frequently TextEdit windows. And when I run it a second time, it gets those right.

Because it's intermittent, it's been very hard to pinpoint what the failure conditions are. Was it the first time I ran it after a reboot? Was it the first time I ran it after changing desktops or a time lag or does it happen when I have too many Chrome windows open?

Maybe related, maybe separate, as you can see in the sample output above, sometimes it lists the single CurrentKey window that's on this desktop, sometimes it lists all CurrentKey windows on all desktops, sometimes it lists the correct single window, but preceded by a blank line, sometimes it doesn't list any CK windows, even though I know there's on there.. Those errors do not go away simply by running it a second time. Fortunately, the CurrentKey app is not important to what I am trying to do.

Unfortunately, TextEdit is very important to my plans. At the moment, I can't tell whether the CK list problems have anything to do with the TextEdit problems.

Any ideas where I might begin looking?

Thanks

Not offhand. I suppose you could always set the script to run twice.

What is current key?

You mean CurrentKey Stats?

I have to wonder what its background-only status is under different circumstances.

I think that if you ask TextEdit directly about its windows it will give you a full listing - but I'm not in a position to test at the moment.

So you might be able to special case TextEdit.

Yes, the author refers to it by both names. Since I don't use its stats-gathering capabilities, I have not held onto that part of the name.

I'm not sure what you mean by this. Background-only status? Different circumstances?

Yes, this AppleScript:

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

will do the job, mostly, but it's all one line with comma-space delimiters and I have numerous files where I use comma-space in the file names, so a little more complication is necessary, as in this example from @ComplexPoint:

How to List All Windows of One App That Are Open in All Desktops - #44 by ComplexPoint

Only if you output it like that. It actually returns a list object -- the textual representation of that object (eg in Script Editor's Results pane) may look like a comma-delimited list of strings but you can easily create a one-window-per-line text block ready for KM to use in a "For Each Line in a Collection" action:

tell application "TextEdit"
	set winNameList to name of every window
end tell
set AppleScript's text item delimiters to return
set winNameList to winNameList as text

@ComplexPoint's version looks more complicated because he's made a generic function -- you don't need to do that. And because it's a function he's resetting TIDs at the end, which you also (probably) don't need to do (thanks to @CJK for beating that into me :wink: ).

No it's not. You're interpreting what was returned as if it's literal plain text. But it's an AppleScript list object, so the commas aren't text characters, they're syntax that delimit individual list items stored at different addresses in memory.

If you want to convert it to text, with one window name per line, then:

set my text item delimiters to linefeed

tell application "TextEdit" to get the ¬
        the name of the windows as text

This is a property of a System Events process class object that reports whether an application runs in the background only or not. Applications that run in the background include those that have no user interface at all, as well as those that run from the menu bar, but may sometimes have popovers or even windows that are designed to be more transient in their use.

You can see the property in question being used to predicate the filtering of application processes in @ccstone's code:

tell (application processes whose background only is false)

Regarding the incomplete window list returned for TextEdit by System Events, I wonder if it relates to windows that are grouped together in tabs. System Events seems only to be aware of the window whose tab is currently the active one—I'm seeing this in TextEdit, ScriptEditor, and Finder. Generally speaking, System Events is examining the UI, so it usually only knows about things that are physically present on the screen, with one or two notable exceptions. So it may be worth considering the applications as two separate groups to handle differently: those apps that are not scriptable can have their windows enumerated by System Events, while those that are scriptable can enumerate them with AppleScript:

to enumerateWindowsForApplications given AppleScript:AppleScript as integer : 0
	local I
	
	set my text item delimiters to linefeed & tab
	set I to AppleScript
	
	script applicationWindows
		property list : {}
		property index : 2 - I
		property handler : a reference to item index of my [A, B]
		
		on A()
			local id
			tell application id ("com.apple.systemevents") ¬
				to repeat with P in (get processes ¬
				whose background only = false and ¬
				has scripting terminology = true)
				set id to P's bundle identifier
				set the end of my list to {[linefeed, "#", ¬
					space, P's name], the name of ¬
					every window of AppleScript's ¬
					application id id}
			end repeat
		end A
		
		on B()
			tell application id ("com.apple.systemevents") ¬
				to repeat with P in (get processes ¬
				whose background only = false and ¬
				has scripting terminology = false)
				set the end of my list to {[linefeed, "#", ¬
					space, P's name], the name of ¬
					P's windows}
			end repeat
		end B
		
		property enumerator : my handler as script
	end script
	
	applicationWindows's enumerator()
	return the list of applicationWindows as text
end enumerateWindowsForApplications

(enumerateWindowsForApplications with AppleScript) & ¬
	(enumerateWindowsForApplications without AppleScript)
1 Like

Oh! You beat me to the punch.

Hi @ccstone, (and @CJK and @Nige_S if you're interested),

It's been five months since I looked at this thread. The system that I'm trying to put together is more complex than I imagined and has several different active trailheads. This was one aspect of one of them. Having gotten this basic issue mostly answered, to the point of a "proof of principle", my attention has wandered to other areas where I'm still trying to even define the issues.

I got to trying this out again today and I discovered another bug in Chris's original script in item 2 above (heading quoted for reference):

The bug is that there are some minimized windows that get listed on every desktop.

I think it works like this: I had some apps minimized, among them was a Safari window. Normally, if I'm in a different desktop than where that Safari window had been originally, if I restore that Safari window, it gets restored to the original window and focus moves to that Desktop. And it doesn't get listed by Chris's script.

But I rebooted while that app was minimized, in this case Safari and some others, and it seems the system lost track of what desktops they originated on. The result was that those windows got listed in the script output no matter what desktop I was on. When I un-minimized each of them, they each got restored to the current Desktop.

I don't know if there's a way around this. In a sense the script is accurate in that they could be on the current Desktop, if they got restored. It's not exactly wrong to list them, but it's not right either. If there's a way of telling them apart from fully active windows, maybe they could be flagged as minimized or something.

This report is simply FYI, it's not an urgent issue at all. I just thought that the behavior was an interesting twist on the intent of the script and that you might want to know about it.

My script doesn't have any bugs – it does have limitations imposed by macOS' shoddy implementation of spaces and lack of a proper API.

-ccs

2 Likes

LOL. My apologies. I consider a bug to be unintended behavior, no matter what the source, but I could have been more generous about where I implied the problem might be. Not completely coping with all the various ideosyncracies of the OSes inconsistent behavior does produce unintended behavior, but that doesn't make it your fault.

Windows have a miniaturised property -- true when minimised, false when not. So you could use that to eg omit minimised windows. For example:

tell application "Safari"
	return name of every window whose miniaturized is false
end tell
2 Likes

NICE! Thank you!

I can empathize, Chris!

2 Likes

I found a thread on Stack Exchange / Ask Different:

Here is where user3439894 got to, as of Apr 30, 2021:

Second version of example AppleScript code:

(*Safari: 6 -- By querying the application directly.*)
(*TextEdit: 4 -- By querying the application directly.*)
(*Finder: 3 -- By querying the application directly.*)
(*BBEdit: 1 -- By querying the application directly.*)
(*Norton Secure VPN: 0 -- By querying the application process. May not be accurate, verify as necessary.*)
(*Music: 2 -- By querying the application directly.*)
(*Sublime Text 2: 4 -- By querying the Window menu of the application process.*)
(*DiskCatalogMaker: 2 -- By querying the Window menu of the application process.*)
(*Script Editor: 7 -- By querying the application directly.*)
(*System Preferences: 2 -- By querying the application directly.*)
(*VMware Fusion: 1 -- By querying the Window menu of the application process.*)
(*Activity Monitor: 0 -- By querying the Window menu of the application process.*)
(*Terminal: 2 -- By querying the application directly.*)

As you can see even Activity Monitor, a native default macOS application, the Window menu had to be queried as the application directly didn't understand the basic count windows AppleScript command.

Although the output of the second version of the code was accurate across all Desktops/Spaces at the time it was executed, any application that has "By querying the application process. May not be accurate, verify as necessary." as part of its output only includes the window count of the active Desktop/Space it was executed from.

I want to ask a question that related to this OP, for a different purpose:

Is it possible to identify the second window in a Desktop/Space when it is in a different application from the frontmost app?

I'm going to start a new thread...