The days of this utility are numbered, because it isn’t 64-bit, and I wasn’t able to recompile it with a 64-bit architecture (possibly because I don’t know what I’m doing).
The author of the utility hasn’t been active on that forum for a while, and I see that I can use AsObjC instead, but I haven’t been able to figure out the syntax. What I’m trying to do is find the size and width of the current monitor or of the largest of multiple monitors and offer the user a different set of options depending on what those numbers are.
Can anyone point the way to getting those numbers in AppleScript?
Shane, That does seem to be a good starting point, but I just found this, which seems to be functionally identical to the capsizes utility (though I have only one monitor, and can’t be certain):
on getResolutions()
set resolutions to {}
repeat with p in paragraphs of ¬
(do shell script "system_profiler SPDisplaysDataType | awk '/Resolution:/{ printf \"%s %s\\n\", $2, $4 }'")
set resolutions to resolutions & {{word 1 of p as number, word 2 of p as number}}
end repeat
# `resolutions` now contains a list of size lists;
# e.g., with 2 displays, something like {{2560, 1440}, {1920, 1200}}
end getResolutions
(Sorry I haven’t figured out how to format that as a script…)
You format a script by beginning with a line containing three backquote characters, and ending the same way. I’ve edited your post.
But it’s an awfully inefficient way of doing things.
This will give you frame of every screen. The frame is a list of the position in x,y coordinates of the bottom-left, plus the size:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
One other thing: Your script also returns pixels rather than points, which is probably not what you want.
Perhaps you could get that in the return type, either as a rearranged record, or as a list ?
-- screenXYWH :: Bool -> Bool ->
-- {X :: Int, Y :: Int, W :: Int, H :: Int}
on screenXYWH(blnActiveScreen, blnVisibleOnly)
tell current application's NSScreen
if blnActiveScreen then
set scr to mainScreen
else
set scr to firstObject() of screens()
end if
end tell
tell scr
if blnVisibleOnly then
set rec to visibleFrame()
else
set rec to frame()
end if
end tell
set {xy, wh} to {origin, |size|} of rec
return {X:X of xy, Y:Y of xy, W:width of wh, H:height of wh}
end screenXYWH
Yep, it’s not hard using standard AppleScript, I just thought that might be a cool ASObjC trick to collapse a record to a list.
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
use scripting additions
set screenList to (current application's NSScreen's screens()'s valueForKey:"frame") as list
repeat with oScreen in screenList
set {xy, wh} to {origin, |size|} of oScreen
set contents of oScreen to {X of xy, Y of xy, width of wh, height of wh}
end repeat
-->{{0.0, 0.0, 2048.0, 1152.0}, {2048.0, 0.0, 2048.0, 1152.0}}
I realised some time after I posted that script that it was problematic, and you’ve raised the issue. When run under 10.13, as I am here, it returns {{x, y}, {width, height}}, when it should really be returning the record form you’re seeing. So to be generally useful, a script should be able to handle either.
This is one of the issues I covered here:
In this case, the approach depends a bit on what you want. If you only wanted the dimensions, you could do this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame")
repeat with aFrame in allFrames
set contents of aFrame to {current application's NSWidth(aFrame), current application's NSHeight(aFrame)}
end repeat
If you want{x, y, width, height}, you could use this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
repeat with aFrame in allFrames
set contents of aFrame to {current application's NSWidth(aFrame), current application's NSHeight(aFrame), current application's NSMinX(aFrame), current application's NSMinY(aFrame)}
end repeat
Or a tad more concisely:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
repeat with aFrame in allFrames
set contents of aFrame to current application's {NSWidth(aFrame), NSHeight(aFrame), NSMinX(aFrame), NSMinY(aFrame)}
end repeat
And if best performance is imperative, you could probably use something a bit more quick and dirty using record-to-list coercions, like this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
set allFrames to (current application's NSScreen's screens()'s valueForKey:"frame") as list
repeat with aFrame in allFrames
set aList to aFrame as list
set contents of aFrame to ((item 1 of aList) as list) & (item 2 of aList) as list
end repeat
return allFrames
It’s easy to convert a dictionary to an array, but what’s returned here is an AppleScript list. Converting that to a dictionary first would just be extra work. More importantly, whereas coercing a record to a list is reliable in terms of the order of items in AppleScript (in practice, not by definition), that’s not the case with dictionaries/arrays in Objective-C.
As always, many thanks for all the advice here. The script that I’m modifying is designed for people still using 10.8 or 10.9, so I need to use a non-ASObjC solution (I think).
I do need pixels not points, here. At the risk of boring everyone, here is what this is for: the script sets up the SheepShaver emulator, and has an option that writes the size of SheepShaver screen (window) into the SheepShaver prefs file. I show a menu that offers the user a range of sizes - but I want to make sure that the largest size that I offer is one that will fit into the users’s actual monitor. The old dspsizes utility did this very well, but I wasn’t able to build a 64-bit version, so I’ll need a standard AppleScript solution for the next two years or so. Then I’ll stop supporting 10.8 and 10.9 and will use these very cool ASObjC methods, which I’ve written into my script for future reference.
FYI, 10.9 can use ASObjC as long as you confine it to a script library. You can also get pixels either by asking for the screen’s backingScaleFactor() and doing the conversion yourself, or converting a frame something like this:
use AppleScript version "2.4" -- Yosemite (10.10) or later
use framework "Foundation"
use framework "AppKit"
set allScreens to current application's NSScreen's screens() as list
repeat with aScreen in allScreens
set aFrame to aScreen's frame()
set pixelFrame to (aScreen's convertRectToBacking:aFrame)
set contents of aScreen to {current application's NSWidth(pixelFrame), current application's NSHeight(pixelFrame)}
end repeat
I used ASObjC Runner under 10.9, but I’m more or less committed to supporting 10.8 for at least another year. Most of my projects are involved with emulators and other ways to use old software and old formats, so the users I work with aren’t rushing to update things…