How to get bounds of all open windows?

I know the Finder allows you to get and set the bounds of windows, but I’m under the impression that this is not universally applicable to every application. Since I need to be able to get and set the bounds of every open window, perhaps querying the WindowServer might work? I have no idea how to do this, as I obviously can’t open its dictionary (if if even has one). Thoughts?

Nathaniel

The problem is that some apps will report bounds (coordinates of bounding rectangle), and some report size (TopLeft, BottomRight coordinates). If you sort that out, you can generally get them all (look in Code Exchange too - I recall something there).

Here’s a script adapted from one by Nigel Garvey in my files:

-- adapted from a script by Nigel Garvey for resizing windows.
-- this script just finds them all and lists {appName, WinTitle, WinBounds}
set allBounds to {}
tell application "Finder"
	set running to name of processes whose visible is true and name is not "Finder" and name is not "QuickSilver"
	repeat with anApp in running
		try -- for scriptable application, not the Finder.
			tell application anApp
				set allWindows to (every window)
				repeat with aWindow in allWindows
					set Wbounds to (get bounds of theWindow)
					set Wtitle to aWindow's title
					set end of allBounds to {contents of anApp, Wtitle, Wbounds}
				end repeat
			end tell
		on error -- Non-scriptable application, not excluded.
			tell application "System Events"
				tell application process anApp
					set allWindows to (every window)
					repeat with aWindow in allWindows
						set {{p1, p2}, {s1, s2}} to aWindow's {position, size}
						set Wbounds to {p1, p2, p1 + s1, p2 + s2}
						set Wtitle to aWindow's title
						set end of allBounds to {contents of anApp, Wtitle, Wbounds}
					end repeat
				end tell
			end tell
		end try
	end repeat
	-- for the Finder
	set allWindows to (every window whose visible is true)
	repeat with aWindow in allWindows
		set Wbounds to bounds of aWindow
		set Wtitle to aWindow's title
		set end of allBounds to {"Finder", Wtitle, Wbounds}
	end repeat
end tell

Unfortunately, the script given here contains an error, since in the non-GUI scripting part there is a typo (theWindow) instead of aWindow.

In addition, the repeat loop does not need to be used in this part. Also, for myself and those who need it, I simplified the script, eliminating the confusion with nested tell blocks. I’ll post it here just in case:

 

on windowsInfo() -- all windows info in form {appName, WinTitle, WinBounds}
	tell application "System Events" to set runningApps to name of processes whose visible is true
	set allBounds to {}
	repeat with anApp in runningApps
		try
			tell application anApp to tell (windows whose visible is true) to set end of allBounds to {contents of anApp, name as text, item 1 of bounds}
		on error
			tell application "System Events" to tell process anApp
				repeat with aWindow in (get windows)
					set {{p1, p2}, {s1, s2}} to aWindow's {position, size}
					set end of allBounds to {contents of anApp, aWindow's title, {p1, p2, p1 + s1, p2 + s2}}
				end repeat
			end tell
		end try
	end repeat
	return allBounds
end windowsInfo

windowsInfo()

 

@KniazidisR
Your approach use 2 AppleScript filter and that approach could be very slow.
From Stack Exchange I find a better solution and faster.
It use Python to run AppleScript and use 2 for loops. You could properly do the same in AppleScript.

use framework "Foundation"
use scripting additions

set pyScript to "
import os
import sys
import json
import applescript

prog = None
cmd  = 'tell Application \"System Events\" to get the {title,position,size} of every window of every process'

def main():
    s = applescript.AppleScript(cmd)
    output = s.run()
    stuff = zip(*output)
    info = []
    unnamed = 0
    for row in stuff:
        for item in zip(*row):
            if item[0]:
                title = item[0]
            else:
                title = f'unnamed-{unnamed:0>3}'
                unnamed += 1
            x = item[1][0]
            y = item[1][1]
            w = item[2][0]
            h = item[2][1]
            info.append(
                dict(
                    title=title,
                    x=x,
                    y=y,
                    w=w,
                    h=h
                )
            )
    print(json.dumps(info, indent=2))

if __name__ == '__main__':
    prog = os.path.basename(sys.argv[0])
    sys.exit(main())"

set jsonString to do shell script "/usr/bin/python3 <<EOF" & pyScript & return & "EOF"
tell application "TextEdit" to make new document with properties {text:jsonString}