Idle Interval 1.1

Features
Idle Interval Scripting Addition

Definitions: time interval and frequency
Idle Interval Demo and example script fragments
Idle Interval Suite
     application
          timing options
     timing-options
          the "properties" property
          reference interval
          idle interval
          idle frequency
          next idle interval
          next idle date
          notification handler
          modify permission
          version
Techniques (next page)
     A: Create a new idle handler
     B: Copy Idle Interval.osax into your application bundle

     C: Load the internal copy on Mac OS X 10.2
     D: Set the SAInternalOnly Info.plist entry
     E: Get a path to the active copy of the scripting addition



Definitions: time interval and frequency   
Many features of the Idle Interval scripting addition use a value called a time interval. This is simply a period of seconds, expressed as a real number in AppleScript. The frequency property is measured in events per second, also expressed as a real number.


Idle Interval Demo and example script fragments   
You can try most these example script fragments using Script Editor, except where indicated. The examples use the Idle Interval Demo application (from the Try Block Utilities disk) as the target application, so they will work in Script Editor. You can use any AppleScript Studio application as a target in place of the Idle Interval Demo, as long as it has an idle handler.

You can use these examples in your AppleScript Studio application script, targeting itself, by simply leaving off the outermost "tell application ..." and "end tell".

For example, here are two script fragments when targeting a different application, as you might do from Script Editor, or from your AppleScript Studio application script:

-- fragment #1 includes outermost 'tell' and 'end tell'
tell application "Idle Interval Demo"
     set idle interval of timing options to 0
     get properties of timing options
end tell

-- fragment #2 uses line continuation (option-L) and does not include 'end tell'
tell application "Idle Interval Demo" to ¬
     get version of timing options


The same two samples inside your AppleScript Studio application, targeting itself:

-- fragment #1 without outermost 'tell' and 'end tell'
set idle interval of timing options to 0
get properties of timing options

-- fragment #2 without 'tell' or line continuation (option-L)
get version of timing options


Idle Interval Suite   
Defines all the scriptable classes implemented by the Idle Interval scripting addition.


application (class)   
The class of an AppleScript Studio application object with all of its inherited properties.


timing options   
read-only: YES
A new property of the application class, added by the Idle Interval scripting addition. It returns an object of class timing-options. Event sources with modify permission can change most of the properties of the timing-options class, but some of the properties are read-only.

You can get and set properties of timing options any time, from any handler in your script, except some properties are valid only after your application passes a certain point during its launch. The idle interval, idle frequency, next idle interval and next idle date properties all derive from the same timer. The timer does not exist during the first three handlers called by AppleScript Studio, will finish launching, awake from nib and launched. Until the timer exists, the four properties derived from it are not valid for reading or writing. Sometime before the fourth handler, will become active, AppleScript Studio creates the timer and sets the idle interval to 1.0. During and after will become active, all timing options properties are valid. If your script does not implement will finish launching, awake from nib or launched, then as long as the scripting addition is available when your application runs, you may safely assume the timing options properties are always valid.


timing-options (class)   
The class of an object that implements timing options for AppleScript Studio applications: its unique properties are reference interval, idle interval, idle frequency, next idle interval, next idle date, notification handler and modify permission. You rarely use the timing-options class value explicitly, but instead refer to the timing options property of an application.


the "properties" property   
read-only: NO
The timing-options class inherits from the item class. In Cocoa scripting this class is represented as NSCoreSuite.abstractObject. One property of an item is named properties. It allows you to get and set multiple properties of an object in one statement.

You can get all the properties of a timing-options object as a record:

tell application "Idle Interval Demo" to ¬
     get properties of timing options

-- Result:
-- {reference interval:1.26296350052437E+8, idle interval:1.61803397247, idle frequency:0.618033994968, next idle interval:0.831329405308, next idle date:date "Saturday, January 1, 2005 10:19:10 AM", notification handler:"timingOptionsChanged", modify permission:remote process, class:timing-options}


You can also set multiple properties by supplying a record containing only the properties you want to change:

tell application "Idle Interval Demo"
     set properties of timing options to {idle interval:0.5, next idle interval:3.14}
     get properties of timing options
end tell

-- Result:
-- {reference interval:1.26306276845052E+8, idle interval:0.5, idle frequency:2.0, next idle interval:3.071436032653, next idle date:date "Saturday, January 1, 2005 10:19:39 PM", notification handler:"timingOptionsChanged", modify permission:remote process, class:timing-options}


Some properties refer to the same thing, and could contradict each other if you set both in one command. If you set both idle interval and idle frequency, the idle frequency is ignored. If you set both next idle interval and next idle date, the next idle date is ignored:

-- in this case the idle frequency you supply is ignored
tell application "Idle Interval Demo"
     set properties of timing options to {idle interval:10, idle frequency:10}
     get {idle interval, idle frequency} of timing options
end tell

-- Result:
-- {10, 0.1}


-- in this case the next idle date you supply is ignored
tell application "Idle Interval Demo"
     set properties of timing options to¬
          {next idle interval:60, next idle date:date "Friday, May 1, 2020 12:00:00 AM"}
     get {next idle interval, next idle date} of timing options
end tell

-- Result:
-- {59.897455006838, date "Saturday, January 1, 2005 10:21:47 PM"}


reference interval   
read-only: YES
The time interval from January 1 2001 00:00:00 GMT until now. (January 1 2001 00:00:00 GMT is the Mac OS X absolute reference date). To measure the duration between two events, you can get the reference interval twice and subtract the two values.

tell application "Idle Interval Demo"
     set firstTime to get reference interval of timing options
     delay 3
     set lastTime to get reference interval of timing options
     return lastTime - firstTime
end tell

-- Result:
-- 3.005004823208


idle interval   
read-only: NO
The time interval between repeating invocations of your idle handler. The idle interval has always existed in AppleScript Studio applications with an idle handler, but until now, it never had its own name as a scriptable property. It was simply "the most recent positive number returned by the idle handler".

The idle interval property always contains a real number. During and after will become active, it is a valid property to read and write (1.0 by default). Before that time, the property is not valid: getting it returns 0.0 and setting it has no effect.

-- AppleScript Studio sets the idle interval to 1.0 by default, but the
-- Idle Interval Demo uses its own initial values in foreground and background.
tell application "Idle Interval Demo" to ¬
     get idle interval of timing options

-- Result:
-- 1.61803397247


tell application "Idle Interval Demo" to ¬
     get class of (get idle interval of timing options)

-- Result:
-- real


A repeating idle interval is measured from the beginning of the idle handler, not when it returns. In other words, if you set the idle interval to 2.0, then your idle handler will start every two seconds, whether it takes 0.5, 1.0 or 1.5 seconds to execute. This means you need to choose an idle interval with at least enough time for your idle handler to execute.

The idle interval is the intended interval, not necessarily the actual interval. If the idle handler takes longer than one idle interval to execute, the actual interval will be greater than the intended interval. You can see this with the Idle Interval Demo if you set the idle interval below 0.2 seconds while viewing the "every idle (slowest)" tab view. At some point, further lowering of the idle interval does not make any difference, and your CPU spends a high proportion of its time in the idle handler.

When you set the idle interval, you implicitly set the next idle interval to the same value. The next call to your idle handler will occur one idle interval after the set command, and the idle calls will repeat every idle interval thereafter.

tell application "Idle Interval Demo"
     -- these two statements are identical, use one or the other:
     set idle interval of timing options to 70 -- set next idle interval implicitly
     set properties of timing options to {idle interval:70, next idle interval:70}
end tell


If you want the next idle call to occur sooner or later than one idle interval, then set both properties in one statement, using a different number for the next idle interval. This is useful when setting a long idle interval with a short next idle interval, to ensure the idle handler is called once right away. It is also useful when setting a short idle interval with a longer next idle interval, to guarantee the idle handler is not called too soon.

tell application "Idle Interval Demo"
     -- next idle occurs sooner than one idle interval
     set properties of timing options to {idle interval:70, next idle interval:5}
     -- next idle occurs later than one idle interval
     set properties of timing options to {idle interval:0.04, next idle interval:7200}
     -- next idle occurs as soon as possible
     set properties of timing options to {idle interval:300, next idle interval:0}
end tell


To use your idle handler as a one-shot timer, set the idle interval to zero, then use the next idle interval or next idle date to schedule the one-shot timer. AppleScript will call your idle handler only once at the right time.

-- (1) you can schedule a one-shot using two set commands
tell application "Idle Interval Demo"
     set idle interval of timing options to 0 -- completely disable idle handling
     
     -- ... some time later, maybe even in a different handler ...
     set next idle interval of timing options to 3.5555 -- schedule a one-shot
end tell

-- (2) you can also schedule a one-shot in one set command
tell application "Idle Interval Demo" to ¬
     set properties of timing options to {idle interval:0, next idle interval:3.5555}

-- (3) this might not be a one-shot, because the idle interval might not be zero
tell application "Idle Interval Demo" to ¬
     set next idle interval of timing options to 3.5555 -- schedule the next idle call


To disable your idle handler, set the idle interval and next idle interval to zero.

tell application "Idle Interval Demo"
     -- these two statements are identical, use one or the other:
     set idle interval of timing options to 0 -- implicitly set next idle interval to 0
     set properties of timing options to {idle interval:0, next idle interval:0}
end tell


To disable only repeating invocations of your idle handler while leaving a one-shot timer in place, use this syntax:

tell application "Idle Interval Demo"
     set properties of timing options to ¬
          {idle interval:0, next idle interval:get next idle interval of timing options}
end tell


The minimum idle interval or next idle interval is 0.0001 seconds. However, the resolution of the idle interval is better than 1/10000. In other words, even if you set the idle interval to a value much higher than the minimum, digits past the fourth decimal place make a difference after many iterations. The scripting addition does not impose a maximum idle interval, but Mac OS X may have a maximum, depending on the version, on the order of 2.77678974292136E+11 seconds, which is well over 8800 years.


idle frequency   
read-only: NO
The frequency of repeating invocations of your idle handler. It usually equals 1/idle interval, except when the idle interval is zero, in which case the idle frequency is also zero. The idle frequency property otherwise behaves very much like the idle interval property: it always contains a real number (1.0 by default); it is not valid until will become active; when it is not valid, getting it returns 0.0 and setting it has no effect; setting it to zero disables repeating invocations of your idle handler; and you can modify it with other properties in one statement.

-- AppleScript Studio sets the idle interval to 1.0 by default, but the
-- Idle Interval Demo uses its own initial values in foreground and background.
tell application "Idle Interval Demo" to ¬
     get {idle interval, idle frequency} of timing options

-- Result:
-- {1.61803397247, 0.618033994968}


Setting the idle frequency implicitly sets the next idle interval to the new idle interval, which is either the inverse of the new idle frequency, or zero.

-- Setting the idle frequency sets idle interval and next idle interval to its inverse.
tell application "Idle Interval Demo"
     set idle frequency of timing options to 0.1
     get {idle frequency, idle interval, next idle interval} of timing options
end tell

-- Result:
-- {0.1, 10.0, 9.687160775065}


-- Setting idle frequency to zero sets three properties to zero.
tell application "Idle Interval Demo"
     set idle frequency of timing options to 0
     get {idle frequency, idle interval, next idle interval} of timing options
end tell

-- Result:
-- {0.0, 0.0, 0.0}


The maximum idle frequency is 10,000 per second, based on the minimum idle interval of 0.0001 seconds. However, like the idle interval, digits past the fourth decimal place make a difference after many iterations. The scripting addition does not impose a minimum idle frequency, but Mac OS X may have a maximum idle interval (see above).


next idle interval   
read-only: NO
The time interval from now until the next idle handler call. If the value is greater than zero, it decreases over time until it reaches zero, at which point AppleScript calls your idle handler.

tell application "Idle Interval Demo"
     tell timing options
          set valueList to {}
          set its idle interval to 0
          set its next idle interval to 5
          repeat
               set the end of valueList to its next idle interval
               if the end of valueList is 0 then exit repeat
               delay 1
          end repeat
          return valueList
     
end tell
end tell

-- Result:
-- {4.820664465427, 3.815763324499, 2.799529641867, 1.782570227981, 0.766299620271, 0.0}


You can set the next idle interval at any time. The next invocation of your idle handler occurs when the next idle interval reaches zero, regardless of the current value of the idle interval. After that, repeating invocations continue (or not) as specified by the idle interval.

If you set the next idle interval explicitly to zero, AppleScript Studio calls your idle handler as soon as possible, regardless of the current idle interval value. This is the best way to invoke your idle handler "immediately" whether or not you have a repeating idle interval.

-- Setting next idle interval to zero by itself invokes idle as soon as possible.
tell application "Idle Interval Demo" to ¬
     set next idle interval of timing options to 0


The scripting addition does not impose a maximum next idle interval, but Mac OS X may have a maximum, depending on the version, on the order of 2.77678974292136E+11 seconds, which is well over 8800 years.


next idle date   
read-only: NO
The date and time of the next idle handler call. It is identical to the next idle interval property, except it is expressed as a constant future date instead of a real number that counts down. When the current date counts up to match the next idle date, AppleScript calls your idle handler.

The next idle date has a resolution of 1 second. It is not as precise as the next idle interval, but sometimes a date format is more convenient if you do not need the extra precision. The AppleScript date format can represent dates up to the year 9999. For example, after you set a large next idle interval, if you get the next idle date, it will have a correspondingly large year. However, when you set the next idle date, the date must be earlier than February 6, 2040.

-- The next idle date has a resolution of 1 second.
tell application "Idle Interval Demo" to ¬
     get {next idle date, next idle interval} of timing options

-- Result:
-- {date "Saturday, January 1, 2005 10:20:12 AM", 1.325348690152}


Here are two examples of setting the next idle date:

tell application "Idle Interval Demo"
     set next idle date of timing options to (current date) + 3 * minutes
     get {next idle date, next idle interval} of timing options
end tell

-- Result:
-- {date "Saturday, January 1, 2005 10:24:37 AM", 179.553127393126}


tell application "Idle Interval Demo"
     set next idle date of timing options to date "1/2/2005 16:56"
     get {next idle date, next idle interval} of timing options
end tell

-- Result:
-- {date "Sunday, January 2, 2005 4:56:00 PM", 1.0964409165822E+5}


The next idle date contains missing value when you disable the idle handler. However, you can not set the next idle date to missing value, you can only set the property using an AppleScript date value. Set the next idle interval to zero if you want to invoke your idle handler as soon as possible.

tell application "Idle Interval Demo"
     set idle interval of timing options to 0
     get {next idle date, next idle interval} of timing options
end tell

-- Result:
-- {missing value, 0.0}


notification handler   
read-only: NO
The string name of a handler in your AppleScript studio script. The name is not case sensitive. If it is not an empty string, the Idle Interval scripting addition calls your handler whenever the timing options change (including when your script makes the change).

Your notification handler receives one argument, a record describing the nature of the change, the new timing options properties just after the change was made, and the source of the event that made the change:

{name: string, contents: record of timing options, source: eventSource}

The name field is a single string, but it can contain multiple messages. The possible strings are: "idle interval changed", "idle interval disabled", "next idle interval changed", "notification handler changed" and "modify permission changed". In certain cases the notification string may contain multiple concatenated messages. Be sure to use "contains" or "is in" (or some variant) to check for a particular string, instead of checking for simple equality.

The contents field is a record of the new timing options. This is similar to the properties record of a timing-options object but without the class property.

If the notification handler changes, the scripting addition notifies the old handler.

The event source is an enumerated value from the list of possible modify permission values.

-- set sample notification handler for AppleScript Studio (not for Script Editor)
set notification handler of timing options to "timingOptionsChanged"

-- sample notification handler for AppleScript Studio (not for Script Editor)
on timingOptionsChanged(notifyRecord)
     -- notifyRecord = {name:string, contents:record, source:eventSource}
     -- name could be multiple comma-delimited strings
     -- contents contains all the properties of timing-options except "class".
     -- source is an enumerated value like the modify permission property
     try
          copy name of theNotification to theMessage
          copy contents of theNotification to theContents
          copy source of theNotification to theSource
          copy theContents to logContents
          tell logContents to ¬
               set its modify permission to its modify permission as string
          log {"timingOptionsChanged: ", theMessage, logContents, theSource as string}
          
          set internalRequest to theSource is in {direct call, same process}
          if internalRequest then
               log " (internal request, no action taken)"
          else
               if theMessage contains "idle interval disabled" then
                    -- statements to handle disabled idle interval
               else if theMessage contains "idle interval changed" then
                    -- statements to handle modified idle interval
               else if theMessage contains "next idle interval changed" then
                    -- statements to handle modified next idle interval
               end if
          end if
     on error errMsg number errNum
          log {"timingOptionsChanged error: ", errMsg, errNum}
     end try
     log {"timingOptionsChanged returns"}
     return true
end timingOptionsChanged


modify permission   
read-only: NO
Instead of (or in addition to) receiving notification of changes to the timing options, you may want to simply block certain sources of changes. The modify permission contains an enumerated value, describing the most distant event source that can change the timing options.

Any event source can read the modify permission property (or any other timing-options property) any time. However, only an AppleScript Studio application script can change the modify permission of its timing options. Even if it sets the modify permission property to no process, preventing even its own handlers from changing the timing options, the application script can always change the modify permission property.

The possible values are: no process, direct call, same process, local process, and remote process, and are defined as follows.

no process: Effectively locks the timing options from any changes, even from your own script; to allow your script to unlock the timing options, your script always has direct call privileges to change the modify permission property itself.

direct call: Block changes for any event source except your own application script.

same process: Block changes from all sources except calls originating from inside your application. This includes direct calls and calls from other source such as scripting additions.

local process: Allow changes from all sources on the same machine, but block any changes from applications running on remote machines.

remote process: This is the default value, providing no security beyond your Sharing preferences for this machine. All event sources, including remote applications, can change the timing options, assuming you have enabled Remote Apple Events for your machine.


-- set modify permission for AppleScript Studio (not for Script Editor)
set modify permission of timing options to direct call


-- get modify permission (for AppleScript Studio or Script Editor)
tell application "Idle Interval Demo"
     get modify permission of timing options
end tell

Result:

remote process


version   
read-only: YES
The version of the active Idle Interval scripting addition.

tell application "Idle Interval Demo"
     get version of timing options
end tell

-- Result:
-- "1.0.2"


           <----- BACK: Getting Started                                         NEXT: Techniques ----->




Thursday, 07-Aug-2008 02:07:40 MST

© 2004-2007 Stuart B. Russell. All Rights Reserved.
Trademarked terms are the property of their respective owners.