Despite being a late contribution, most of the attention has (appropriately) been put towards cleaning up the list of processes
and windows
from System Events—which is very often the fastest approach across a variety of contexts in AppleScript to enumerate and filter a large number of objects—so, instead, I’ve elected to turn my attention towards an enumeration of processes
in System Events that yields a fully-saturated list.
It is not remotely fast, although not intolerably slow. Regardless of whether it has practical utility for you or other people in the real world, it might offer some value as a point of interest in the context of the following objectives:
I ran your script to visualise the kind of output you had had in mind:
which yielded the following output, correctly enumerating the windows and applications I opened for this purpose to cover a variety of window styles from both foreground and background processes:
{{application:"iTerm2", windowTitles:{"time osascript -s s ~/L/M/c/#/key"}},
{application:"One Thing", windowTitles:{"One Thing"}},
{application:"Arc", windowTitles:{"Remove empty items from a list (get all windows of all processes) - AppleScript - Late Night Software Ltd."}},
{application:"Transmission", windowTitles:{"Transmission"}},
{application:"Telegram", windowTitles:{"Telegram"}},
{application:"Photo Booth", windowTitles:{"Photo Booth"}},
{application:"Finder", windowTitles:{"Finder Preferences", "CK", "Downloads"}}}
Execution time: ~7.7s
Aside: About UI element collections
It’s worth bearing in mind that, since you’re implementing a repeat
loop to iterate through every process
, your concern regarding the presence of empty elements returned by enumerating every window in every process
isn’t necessarily as pertinent as it might first appear if you aren’t familiar with how System Events handles its enumerated lists of UI elements
prior to dereferencing:
The following code simply enumerates the names of windows to illustrate this aside:
tell application id "com.apple.systemevents" to get ¬
the name of every window in every process
It produces the following output when run against the same set of windows and processes as before:
{{}, {}, {}, {}, {}, {}, {}, {}, {"osascript -s s -e 't
~/L/M/c/#/key"}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {"One Thing"}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {}, {}, {}, {"Remove empty items from a
list (get all windows of all processes) - AppleScript -
Late Night Software Ltd."}, {}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
{}, {}, {}, {}, {}, {}, {}, {}, {"Transmission"}, {}, {},
{}, {"Telegram"}, {}, {"Photo Booth"}, {}, {"Finder
Preferences", "CK", "Downloads"}, {}, {}, {}, {}, {}, {}}
Looking at this output, the presence of the numerous empty list
elements makes sense (those processes have no windows, and no names to populate their respective entries), but beyond looking somewhat unwieldy in depicting what appears to be a laborious task of cleaning up the result, observe what happens if you access the collection while it’s still in its referenced state (i.e. prior to evaluation):
tell application id ("com.apple.systemevents") ¬
to repeat with W in processes's windows
log (get name of W)
end repeat
or, with a counter:
tell application id "com.apple.systemevents" to set |windows| ¬
to a reference to every window in every process
repeat with i from 1 to the number of |windows|
[i, "⏵ ", name of item i in |windows|]
log (result as text)
end repeat
The output of both scripts is broadly similar, but I’ll use the output of the latter as it includes the index, i
, against each window name:
1⏵ osascript -s s -e 't ~/L/M/c/#/key
2⏵ One Thing
3⏵ Remove empty items from a list (get all windows of all processes) - AppleScript - Late Night Software Ltd.
4⏵ Transmission
5⏵ Telegram
6⏵ Photo Booth
7⏵ Finder Preferences
8⏵ CK
9⏵ Downloads
As you can see, provided the collection returned by System Events hasn’t been evaluated, it allows you to iterate through and hence enumerate properties against each object, which you’ll also notice behaves as if it has been flattened.
It is slow because I forced it to evaluate every item and retrieve its name
property. The best use for referenced collections is where there isn’t a need to evaluate its contents until the end, such as in testing for an object’s existence. This can be used to recurse through a UI element
tree very speedily.
A targeted whose
filter
tell application id "com.apple.systemevents" to tell (processes ¬
where the class of its windows contains window) to get ¬
[its name, the name of every window]
which, when run against the same set of windows and processes as before, yields this output:
{{"iTerm2", "One Thing", "Arc", "Transmission",
"Telegram", "Photo Booth", "Finder"}, {
{"time osascript -s s ~/L/M/c/#/key"},
{"One Thing"},
{"Remove empty items from a list (get all windows of all processes) - AppleScript - Late Night Software Ltd."},
{"Transmission"},
{"Telegram"},
{"Photo Booth"},
{"Finder Preferences", "CK", "Downloads"}}}
Execution time: ~12.9s
The above output is a list
containing two sub-list
items, the first of which are process
names, to which items of the second list
provide the corresponding list
of window
names.
Then, to organise this into a record
:
use framework "Foundation"
tell application id "com.apple.systemevents" to tell (every ¬
process where every window's class contains window) ¬
to get dictionaryWithObjects_forKeys_(get windows's ¬
name, get its file's name) of my NSDictionary
return the result as record
Output:
{|iterm.app|:{"time osascript -s s ~/L/M/c/#/key"},
|telegram.app|:{"Telegram"},
|transmission.app|:{"Transmission"},
|arc.app|:{"Remove empty items from a list (get all
windows of all processes) - AppleScript
- Late Night Software Ltd."},
|photo booth.app|:{"Photo Booth"},
|finder.app|:{"Finder Preferences", "CK", "Downloads"},
|one thing.app|:{"One Thing"}}
Execution time: ~13.3s