Module hm
> INTERNAL CHANGE: Using the 'checks' library for userscript argument checking.
Using the 'compat53' library, but syntax-level things (such as # not using __len) are still Lua 5.1
Hammermoon main module
Internal/advanced use only
Hammermoon core facilities for use by extensions.
API CHANGE: Doesn't exist in Hammerspoon
INTERNAL CHANGE: Deprecation facility
Deprecate a field or function of a module or class
module
:<#module>
<#module>
table or<#module.class>
tablefieldname
:<#string>
field or function namereplacement
:<#string>
the replacement field or function to direct users to
API CHANGE: Doesn't exist in Hammerspoon
INTERNAL CHANGE: Deprecation facility
Disallow a field or function of a module or class (after deprecation)
module
:<#module>
<#module>
table or<#module.class>
tablefieldname
:<#string>
field or function namereplacement
:<#string>
the replacement field or function to direct users to
Function hm._core.module(name,classes,submodules)
-> <#module>
API CHANGE: Doesn't exist in Hammerspoon
INTERNAL CHANGE: Allows allocation tracking, properties, deprecation; handled by core
Declare a new Hammermoon module.
-
name
:<#string>
module name (without the"hm."
prefix) -
classes
:<#table>
a map with the initial metatables (as values) for the module's classes (whose names are the map's keys), if any; the metatables can can contain__tostring
,__eq
,__gc
, etc. This table, suitably instrumented, will be available in the resuling module's_classes
field -
submodules
:<#table>
a plain list of submodule names, if any, that will be automatically required as the respective fields in this module are accessed -
Returns
<#module>
: the "naked" table for the new module, ready to be filled with functions
Use this function to create the table for your module.
If your module instantiates objects, you should pass classes
(the values can just be empty tables),
and retrieve the metatable for your objects (and the constructor) via the _classes[<CLASSNAME>]
field
of the returned module table. Note that the __gc
metamethod of a class, if used, must be already
in the class table passed to this function (i.e. you cannot add it afterwards) for Hammermoon's allocation debugging to work.
Usage:
local mymodule=hm._core.module('mymodule',{myclass={}})
local myclass=mymodule._classes.myclass
function mymodule.myfunction(param) ... end
function mymodule.construct(args) ... return myclass._new(...) end
function myclass:mymethod() ... end
...
return mymodule -- at the end of the file
API CHANGE: Doesn't exist in Hammerspoon; this also allows fields in modules and objects to be trivially type-checked.
INTERNAL CHANGE: Modules don't need to handle properties internally.
Add a property to a module or class.
module
:<#module>
<#module>
table or<#module.class>
tablefieldname
:<#string>
desired field namegetter
:<#function>
getter functionsetter
:<#function>
setter function; iffalse
the property is read-only; ifnil
the property is immutable and will be cached after the first query.type
:<#string>
field type (for writable fields)sanitize
:<#boolean>
iftrue
, usesanitizeargs
instead ofcheckargs
This function will add to the module or class a user-facing field that uses custom getter and setter.
Internal/advanced use only
API CHANGE: Doesn't exist in Hammerspoon
Debug options
INTERNAL CHANGE: Uielements are cached
Cache uielement objects (default true
).
Uielement objects (including applications and windows) are cached internally for performance; this can be disabled.
INTERNAL CHANGE: Centralized switch for assertion checking - Hammermoon modules should all use
hmassert()
Disable assertions (default false
).
If set to true
, assertions are disabled for slightly better performance.
INTERNAL CHANGE: Centralized switch for type checking - Hammermoon modules should all use
hmcheck()
Disable type checks (default false
).
If set to true
, type checks are disabled for slightly better performance.
INTERNAL CHANGE: User objects are retained
Retain user objects internally (default true
).
User objects (timers, watchers, etc.) are retained internally by default, so userscripts needn't worry about their lifecycle. If falsy, they will get gc'ed unless the userscript keeps a global reference.
Module hm._os
> Internal/advanced use only
Low level access to macOS
Extends
<hm#module>
INTERNAL CHANGE: Instance to be used by extensions.
AXUIElementCreateSystemWide()
instance
Internal/advanced use only
INTERNAL CHANGE: Centralized callback registry for notification centers, to be used by extensions.
event
:<#string>
cb
:<#function>
priority
:<#boolean>
Module hm.applications
> API CHANGE: Running applications and app bundles are distinct objects. Edge cases with multiple bundles with the same id are solved.
Run, stop, query and manage applications.
Extends
<hm#module>
Function hm.applications.bundlesForBundleID(bid)
-> {
<#bundle>
, ...}
Internal/advanced use only
INTERNAL CHANGE: returns all bundles for a given bundle id
-
bid
:<?>
-
Returns
{
<#bundle>
, ...}
:
Extends
<hm#module.object>
Type for running application objects.
Property (read-only) <#application>.windows
: {
<hm.windows#window>
, ...}
INTERNAL CHANGE: the ad-hoc filtering is done here at the source rather than downstream in hm.windows
The application's windows.
Module hm.screens
Manipulate screens (monitors).
Extends
<hm#module>
Property (read-only) hm.screens.screens
: {
<#screen>
, ...}
INTERNAL CHANGE: The screen list is cached and kept up to date by an internal watcher
The currently connected and enabled screens.
Extends
<hm#module.class>
Property <#screen>.mode
: <#screenMode>
API CHANGE: A user-facing string instead of a table. Refresh rate, color depth are supported.
INTERNAL CHANGE: Will pick the highest refresh rate (if not specified) and color depth=4 (if available, and unless specified to 8). depth==8 isn't supported in HS!
The screen's current mode.
Module hm.timer
> API CHANGE: All timers are of the 'delayed' sort for maximum flexibility.
INTERNAL CHANGE: Don't bother with NSTimer intermediates, we abstract directly from CFRunLoopTimer
Schedule asynchronous execution of functions in the future.
Extends
<hm#module>
INTERNAL CHANGE: high precision
Returns the number of seconds since midnight local time.
- Returns
<#number>
: number of seconds, with millisecond precision or better
Extends
<hm#module.object>
Type for timer objects.
A timer holds an execution unit that can be scheduled for running later in time in various ways via its :run...()
methods.
After being scheduled a timer can be unscheduled (thus prevented from running) via its :cancel()
method.
API CHANGE: This replaces all repeating timers, whether created via
hs.timer.new()
,hs.timer.doEvery()
, orhs.timer.doAt()
INTERNAL CHANGE: High frequency timers (less than 1s repeat interval) are like
hs.timer
s, i.e. the repeat schedule is managed internally by theCFRunLoopTimer
for performance. Other timers behave likehs.timer.delayed
s, i.e. they are rescheduled "manually" after every trigger.
Schedules repeated execution of the timer.
repeatInterval
:<#intervalString>
delayOrStartTime
:<?>
(optional) the timer will start executing: if omitted ornil
, right away; if an<#intervalString>
or a number (in seconds), after the given delay; if a<#timeOfDayString>
, at the earliest occurrence for the given timecontinueOnError
:<#boolean>
(optional) iftrue
, the timer will keep repeating (and executing) even if its<#timerFunction>
causes an errordata
:<?>
(optional) arbitrary data that will be passed to the<#timerFunction>
If delayOrStartTime
is a <#timeOfDayString>
, the timer will be scheduled to execute for the first time at the earliest occurrence
given the repeatInterval
, e.g.:
- If it's 17:00,
myTimer:runEvery("6h","0:00")
will set the timer 1 hour from now (at 18:00) - If it's 19:00,
myTimer:runEvery("6h","0:00")
will set the timer 5 hour from now (at 0:00 tomorrow) - If it's 21:00,
myTimer:runEvery("6h","20:00")
will set the timer 5 hours from now (at 2:00 tomorrow)
Usage:
-- run a job every day at 8, regardless of when Hammermoon was (re)started:
hm.timer.new(doThisEveryMorning,myData):runEvery("1d","8:00")
-- run a job every hour on the hour from 8:00 to 20:00:
for h=8,20 do hm.timer.new(runJob):runEvery("1d",h..":00") end
-- start doing something every second in 5 seconds:
local myTimer=hm.timer.new(mustDoThisVeryOften)
myTimer:runEvery(1,5)
-- and later (maybe in some event callback), stop:
myTimer:cancel()
API CHANGE: This replaces non-repeating timers (
hs.timer.new()
andhs.timer.doAfter()
) as well ashs.timer.delayed
s
INTERNAL CHANGE: These timers technically "repeat" into the distant future, so they can be reused at will, but are transparently added to and removed from the run loop as needed
Schedules execution of the timer after a given delay.
delay
:<#intervalString>
data
:<?>
(optional) arbitrary data that will be passed to the<#timerFunction>
Every time you call this method the "execution countdown" is restarted - i.e. any previous schedule (created
with any of the :run...()
methods) is overwritten. This can be useful
to coalesce processing of unpredictable asynchronous events into a single
callback; for example, if you have an event stream that happens in "bursts" of dozens of events at once,
use an appropriate delay
to wait for things to settle down, and then your callback will run just once.
Usage:
local coalesceTimer=hm.timer.new(doSomethingExpensiveOnlyOnce)
local function burstyEventCallback(...)
coalesceTimer:runIn(2.5) -- wait 2.5 seconds after the last event in the burst
end