Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AppleScript: Get list of windows on all desktops

I need to get a count of all windows per application. Every way I've tried to do this, I only get a count of windows that are assigned to the current (Mission Control) Desktop. (I'm currently running Mac OS X 10.7, so post-Spaces.) Is there any way to get a per-application count of all windows across all Desktops?

The crux of what I've tried:

tell application "System Events"
  repeat with _app in (every process whose visible is true)
    tell _app
      log (name as string) & ": " & (count of every window)
    end tell
  end repeat
end tell

Note that the whose visible is true clause isn't the problem. It finds all of the appropriate processes, but once I ask the processes for windows, they only count the ones in the active Desktop.

I've tried pulling the log line out of the tell and using name of _app and count of every window of _app, but there's no difference. I've tried grabbing things other than processes from System Events, but anything useful ends up effectively being just a different way to get the same object. I've tried iterating over UI elements, but no windows show up that aren't on the current Desktop, though I do get a menubar for each application.

I'm fine with iterating across all Desktops (though not actually switching to all of them), but I can't even find a way to get a list of Desktops. This answer claims to describe how to do that, but I only ever get a single element inside every desktop. Not that there's an obvious way to get windows once you have that Desktop object anyway.

It's also worth pointing out that desktops are controlled by the Dock, and not by Mission Control. I'm not aware of any way for AppleScript to talk to the Dock, so if you know of something, then an answer or comment about that might help point me in the right direction.

Am I trying to do something impossible?

like image 400
wfaulk Avatar asked Dec 12 '13 21:12

wfaulk


2 Answers

As the OP is over six years old and I am unable to test under OS X 10.7, which is what was being used at that time, nonetheless, the following example AppleScript code works for me under macOS Catalina and returned the correct window count across all Desktops/Spaces, with the exception of any application that does understand the given AppleScript command and why the try and on error statements are being used.

Example AppleScript code:

tell application "System Events" to ¬
    set appBundleIdentifierList to ¬
        the bundle identifier of ¬
            (every process whose visible is true)

repeat with appBundleIdentifier in appBundleIdentifierList
    try
        tell application id appBundleIdentifier to ¬
            set {appName, winCount} to {name, (count windows)}
        log appName & ": " & winCount
    on error errorMessage
        log errorMessage
    end try
end repeat

Sample output on my system that has multiple Desktops/Spaces with windows of the application on all or some of the Desktops/Spaces and the window count for each is correct across all Desktops/Spaces, not just the active Desktop/Space the script was run from.

(*Safari: 6*)
(*Terminal: 2*)
(*TextEdit: 4*)
(*Script Editor: 7*)
(*Finder: 3*)
(*BBEdit: 1*)
(*Norton Secure VPN got an error: every window doesn’t understand the “count” message.*)
(*Music: 2*)

Notes:

Not all applications are AppleScript scriptable, in that some do not contain an AppleScript dictionary within their application bundle.

Since the application process cannot return the correct number of windows across all Desktops/Spaces, this method relies on the application to return the number of windows across all Desktops/Spaces.



Update:

The following example AppleScript code does the following:

  • Gets the bundle identifier of every process whose visible is true.

  • For each bundle identifier, get its name and the window count by querying the application directly.

    If the application understands the AppleScript command, then if goes to the next item in the appBundleIdentifierList list.

    If it does not understand, then the window count is calculated by the following:

    • Attempts to get an invisible window count as they would not show up on the Window menu of an application.
    • Calculates the window count by the number of windows shown on the Window menu of the application.
    • Failing these methods it get the window count by querying the application process, with is only accurate fo the active Desktop/Space and is included only for completeness of trying to ascertain the window count just using basic vanilla AppleScript.
    • Goes to the the application in the appBundleIdentifierList list.

Example AppleScript code:

set menuName to "Window"

tell application id "com.apple.systemevents" to ¬
    set appBundleIdentifierList to ¬
        the bundle identifier of ¬
            (every process whose visible is true)

repeat with appBundleIdentifier in appBundleIdentifierList
    try
        tell application id appBundleIdentifier to ¬
            set {appName, winCount} to {name, (count windows)}
        log appName & ": " & winCount & ¬
            " -- By querying the application directly."
    on error
        set winCount to 0
        set notVisibleWindowList to {}
        set errAppName to ¬
            name of application id appBundleIdentifier
        tell application id "com.apple.systemevents"
            try
                tell application process errAppName
                    set notVisibleWindowList to ¬
                        (windows whose visible is false)
                    if notVisibleWindowList is {} then ¬
                        set winCount to ¬
                            length of notVisibleWindowList
                end tell
            end try
            try
                set theTargetMenuItemsList to ¬
                    the reverse of ¬
                        (get name of ¬
                            menu items of ¬
                            menu menuName of ¬
                            menu bar item menuName of ¬
                            menu bar 1 of ¬
                            application process errAppName)
            on error
                set theTargetMenuItemsList to {}
            end try
        end tell
        if theTargetMenuItemsList is not {} then
            repeat with anItem in theTargetMenuItemsList
                if contents of anItem is ¬
                    missing value then exit repeat
                set winCount to winCount + 1
            end repeat
            log errAppName & ": " & winCount & ¬
                " -- By querying the Window menu of the application process."
        else
            try
                tell application id "com.apple.systemevents" to ¬
                    set winCount to ¬
                        (count windows of ¬
                            application process errAppName)
                log errAppName & ": " & winCount & ¬
                    " -- By querying the application process. " & ¬
                    "May not be accurate, verify as necessary."
            end try
        end if
    end try
end repeat

Running both versions of the example AppleScript code to show the difference in output:

First version of example AppleScript code:

(*Safari: 6*)
(*TextEdit: 4*)
(*Finder: 3*)
(*BBEdit: 1*)
(*Norton Secure VPN got an error: every window doesn’t understand the “count” message.*)
(*Music: 2*)
(*Sublime Text 2 got an error: every window doesn’t understand the “count” message.*)
(*DiskCatalogMaker got an error: every window doesn’t understand the “count” message.*)
(*Script Editor: 7*)
(*System Preferences: 2*)
(*VMware Fusion got an error: every window doesn’t understand the “count” message.*)
(*Activity Monitor got an error: every window doesn’t understand the “count” message.*)
(*Terminal: 2*)

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 form. The bottom line is using basic vanilla AppleScript there is no guarantee to get a complete accurate window count of every visible application unless all the applications at that time cen be queried directly. Querying the Window menu by its application process should also be accurate.

With that said, I think other methods may need to be used to get an accurate count.

like image 154
user3439894 Avatar answered Nov 11 '22 18:11

user3439894


I ran your code, setting up the Applescript version to 2.4 (do that as a habit); on the first run, your code presented me with the appropriate count of all the windows per application.

This code is what I tried, and the results seem to be satisfactory. Is there something I'm not seeing?

use AppleScript version "2.4" -- Yosemite (10.10) or later
use scripting additions


tell application "System Events"
    set my_list to {}
    repeat with _app in (every process whose visible is true)
        tell _app
            log (name as string) & ": " & (count of every window)
            set my_list to my_list & {name as string, count of every window}
        end tell
    end repeat
    log my_list
end tell
like image 1
Dri Avatar answered Nov 11 '22 18:11

Dri