7

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?

Community
  • 1
  • 1
wfaulk
  • 1,765
  • 1
  • 17
  • 24
  • By 'desktops', do you mean multiple monitor desktops or virtual desktops (available via mission control in 10.7)? That other question is dealing with multiple monitor desktops only. – adamh Dec 12 '13 at 22:20
  • Good point. I meant Mission-Control-style Desktops. Apple overloading this noun makes searching for solutions *really* easy. :/ – wfaulk Dec 12 '13 at 23:14
  • So I discovered that you can get the number of (assumed: virtual) desktops using the following tcsh one-liner: `@ x = ( \`strings ~/Library/Preferences/com.apple.spaces.plist | grep -c '^\$'\` + 1 ); echo $x`. Try it and see if that works for you, as I'm not sure this hacky method is reliable. I'm running Mojave and it seemed to be accurate for me. If it works, you should be able to use keystrokes in an applescript to switch desktops and collect window names. – hepcat72 Jul 09 '19 at 20:08
  • Bumping an old thread...Anybody ever found a solution to this? Edit: possibly within AppleScript... – mpasi Jan 08 '20 at 16:28
  • @hepcat72 , I tried your oneliner. I opened a terminal window (bsh) and ran tcsh. I pasted in your line. The number returned was 32. I currently have 20 open desktops, plus the dashboard, and right now nothing full screen. However, looking in `~/Library/Preferences/com.apple.spaces.plist` is a good idea. – August Aug 16 '22 at 19:07
  • I have tweaked my one-liner since that last comment. I wrote a very difficult to read perl 1-liner, or I'd describe it in detail, but the surrounding applescript sets a variable called `numClusteredDesktops` to its output minus the number of monitors plus 1. If that result is less than 2, then I do pretty much the same as above, but with `sort -u` added in the series. – hepcat72 Aug 17 '22 at 19:22
  • Geez. I should have just looked at my code comment above it: This logic may not be right. It may just randomly work in my use case on 2 computers. The logic is that `strings ~/Library/Preferences/com.apple.spaces.plist` returns (either) a cluster of contiguous UIDs that includes each desktop and monitor, so the number of desktops can be discerned by subtracting the number of monitors and adding back 1 for the main monitor (or, if there are no secondary monitors, it returns non-contiguous UIDs for each desktop only). There's also the assumption that each full screen app gets a desktop UID – hepcat72 Aug 17 '22 at 19:28
  • So looking closer with that comment in mind, my perl perl one-liner creates groups of UIDs that appear on adjacent lines and returns the largest group. Then the applscript surrounding it does the `- numMonitors + 1`. – hepcat72 Aug 17 '22 at 19:36

2 Answers2

1

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
Dri
  • 492
  • 4
  • 13
  • RE: "Is there something I'm not seeing?" -- There is nothing in the _code_ of this answer that requires `use AppleScript version "2.4" -- Yosemite (10.10) or later` or `use scripting additions`. That said, the **OP** is using **OS X 10.7** so that first line of _code_ certainly isn't applicable! The problem with this answer is it completely missed the most important requirement: **Is there any way to get a per-application count of all windows across all Desktops?** This suffers from the same issue in that it only gets the _window_ count of the active **Desktop**, not all **Desktops**! – user3439894 Apr 30 '21 at 16:28
  • @user3439894, thanks for your reply. I take my answer back. Even when I can see all the Applications in the list, disregarding which Desktop they're in, the count value comes as 0 for Applications on Desktops other than Desktop 1. – Dri Apr 30 '21 at 18:28
1

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.

user3439894
  • 7,266
  • 3
  • 17
  • 28