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


1 Like

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.