Can I use AppleScript to detect if there are any windows on screen?
This is one of those tasks that, on the surface of it, is fairly simple to do, but when you experiment a bit more, turns out can be a little tricky, as not every application or application window reports their properties or attributes completely truthfully all of the time.

This line of AppleScript is the most conventional and commonly-used way to get a list of open windows from applications running on your system: 

    tell application "System Events" to ¬
        get every window of every process whose background only is false


This will give you a list of even those windows that are currently minimised and sitting under their dock icon.  You can get a list of only those windows that are not minimised, and thus visible on your screen at that moment:

    tell application "System Events" to ¬
        get every window of (every process whose visible is true)


However, this will occasionally miss detecting some windows of background only applications (some menu bar apps) that don't report as being visible even when their windows are open.  So, to catch those, you can use this:

    tell application "System Events" to get windows of ¬
        (every process whose class of windows contains window) ¬
            whose value of attribute "AXMinimized" is false


This has the nice benefit of returning only non-empty nests of lists in AppleScript, is my favourite method to use as being one that catches background only, "invisible" windows.

To call these commands from bash, you can use this:

    osascript \
        -e 'tell application "System Events"' \
        -e '    get every window of (every process ¬' \
        -e '        whose background only is false  ¬' \
        -e '        and visible is true)' \
        -e 'end tell'


This is the combination of the first two examples I gave, which returns a comma-delimited list of windows denoted by their titles, like this:

,,, window Edit - Ask Different of application process Safari,, \
    window ~ — osascript  /Users/CK — ttys000 — ⌘1 of application process Terminal,, \
    window Untitled 2 of application process Script Editor, \
    window Messages of application process Script Editor, \
    window Library of application process Script Editor


(I've split the output over several lines for readability.)  Note the three leading commas, which, in AppleScript, are where you'd get empty nesting lists featuring as a result of processes that are visible and in the foreground, but simply don't have any open windows, thus return {} in AppleScript, which becomes an empty string "" in stdout.

As you can see, I have this Safari window open; a Terminal window open; and three Script Editor windows open.  However, it missed my Instagram feed window.  Here's the other example run in bash:

    osascript \
        -e 'tell application "System Events" to ¬' \
        -e '    get every window of (every process ¬' \
        -e '        whose class of windows contains window) ¬' \
        -e '    whose value of attribute "AXMinimized" is false'


which returns this for me right now:

    window Edit - Ask Different of application process Safari, \
    window ~ — osascript  /Users/CK — ttys000 — ⌘1 of application process Terminal, \ 
    window Untitled 2 of application process Script Editor, \
    window Messages of application process Script Editor, window Library of application process Script Editor, \
    window Main Window of application process Flume


(Flume is my Instagram app.)  However, the cost of using this method is a bit more processing time (a couple of seconds).

Finally, if all you require is just the actual number of open windows on your screen to determine whether this is non-zero or whatever, then simply swap get every window... with get the number of (windows...) in whichever method you end up choosing to use.



Note:  Some of these commands may work differently on older systems.  I tested these on Mac OS 10.13 using AppleScript 2.7.  However, very occasionally, System Events can throw an error.  Please run the command again.  This is not a problem with the script, but an issue with System Events and its occasional temperamental behaviour.