Internationalisation
Bundled Plugins API
filter
Filter windows by application, title, location on screen and more, and easily subscribe to events on these windows
Warning: this module is still somewhat experimental. Should you encounter any issues, please feel free to report them on https://github.com/Hammerspoon/hammerspoon/issues or #hammerspoon on irc.freenode.net
Windowfilters monitor all windows as they're created, closed, moved etc., and select some (or none) among these windows according to specific filtering rules. These filtering rules are app-specific, i.e. they start off by selecting all windows belonging to a certain application (but you can also define default and override filters - see :setAppFilter(), :setDefaultFilter(), :setOverrideFilter()) and they can allow or reject windows based on:
  • visibility, focused and/or fullscreen status
  • title length or patterns in the title
  • position on screen (inside or outside a certain region or screen)
  • accessibility role (standard window, dialog, etc.)
  • whether they're in the current Mission Control Space or not
The filtering happens automatically in the background; windowfilters then:
  • generate a dynamic list of the windows that currently satisfy the filtering rules (see :getWindows())
  • sanitize and expose all pertinent events on these windows (see :subscribe() and the module constants with all the events)
A default windowfilter (not to be confused with the default filter within a windowfilter) is provided as convenience; it excludes some known apps and windows that are transient in nature, therefore unlikely to be "interesting" for e.g. window management. hs.window.filter.new() (with no arguments) returns a copy of the default windowfilter that you can further tailor to your needs - see hs.window.filter.default and hs.window.filter.new() for more information.
Usage examples:
1
local wf=hs.window.filter
2
3
-- alter the default windowfilter
4
wf.default:setAppFilter('My IDE',{allowTitles=1}) -- ignore no-title windows (e.g. transient autocomplete suggestions) in My IDE
5
6
-- set the exact scope of what you're interested in - see hs.window.filter:setAppFilter()
7
wf_terminal = wf.new{'Terminal','iTerm2'} -- all visible terminal windows
8
wf_timewaster = wf.new(false):setAppFilter('Safari',{allowTitles='reddit'}) -- any Safari windows with "reddit" anywhere in the title
9
wf_leftscreen = wf.new{override={visible=true,fullscreen=false,allowScreens='-1,0',currentSpace=true}}
10
-- all visible and non-fullscreen windows that are on the screen to the left of the primary screen in the current Space
11
wf_editors_righthalf = wf.new{'TextEdit','Sublime Text','BBEdit'}:setRegions(hs.screen.primaryScreen():fromUnitRect'0.5,0/1,1')
12
-- text editor windows that are on the right half of the primary screen
13
wf_bigwindows = wf.new(function(w)return w:frame().area>3000000 end) -- only very large windows
14
wf_notif = wf.new{['Notification Center']={allowRoles='AXNotificationCenterAlert'}} -- notification center alerts
15
16
-- subscribe to events
17
wf_terminal:subscribe(wf.windowFocused,some_fn) -- run a function whenever a terminal window is focused
18
wf_timewaster:subscribe(wf.hasWindow,startAnnoyingMe):subscribe(wf.hasNoWindows,stopAnnoyingMe) -- fight procrastination :)
Copied!

API Overview

API Documentation

Constants

default

Signature
hs.window.filter.default
Type
Constant
Description
The default windowfilter; it filters apps whose windows are transient in nature so that you're unlikely (and often
Notes
  • While you can customize the default windowfilter, it's usually advisable to make your customizations on a local copy via mywf=hs.window.filter.new(); the default windowfilter can potentially be used in several Hammerspoon modules and changing it might have unintended consequences. Common customizations:
  • to exclude fullscreen windows: nofs_wf=hs.window.filter.new():setOverrideFilter{fullscreen=false}
  • to include invisible windows: inv_wf=windowfilter.new():setDefaultFilter{}
  • If you still want to alter the default windowfilter:
  • you should probably apply your customizations at the top of your init.lua, or at any rate before instantiating any other windowfilter; this way copies created via hs.window.filter.new(nil,...) will inherit your modifications
  • to list the known exclusions: hs.inspect(hs.window.filter.default:getFilters()) from the console
  • to add an exclusion: hs.window.filter.default:rejectApp'Cool New Launcher'
  • to add an app-specific rule: hs.window.filter.default:setAppFilter('My IDE',1); ignore tooltips/code completion (empty title) in My IDE
  • to remove an exclusion (e.g. if you want to have access to Spotlight windows): hs.window.filter.default:allowApp'Spotlight'; for specialized uses you can make a specific windowfilter with myfilter=hs.window.filter.new'Spotlight'
Signature
hs.window.filter.defaultCurrentSpace
Type
Constant
Description
A copy of the default windowfilter (see hs.window.filter.default) that only allows windows in the current
Notes
  • This windowfilter will inherit customizations to the default windowfilter if they're performed before referencing this
Signature
hs.window.filter.hasNoWindows
Type
Constant
Description
Pseudo-event for hs.window.filter:subscribe(): the windowfilter now rejects all windows
Notes
  • callbacks for this event will receive (as the first argument) the last window that was allowed (and is now rejected)
  • this pseudo-event won't trigger again until after the windowfilter allows at least one window
  • this pseudo-event will be emitted after the actual event(s) (e.g. windowDestroyed) that caused the window to be rejected

hasWindow

Signature
hs.window.filter.hasWindow
Type
Constant
Description
Pseudo-event for hs.window.filter:subscribe(): the windowfilter now allows one window
Notes
  • callbacks for this event will receive (as the first argument) the window that is now allowed
  • this pseudo-event won't trigger again until after the windowfilter reverts to rejecting all windows
  • this pseudo-event will be emitted after the actual event(s) (e.g. windowCreated) that caused a window to be allowed
Signature
hs.window.filter.sortByCreated
Type
Constant
Description
Sort order for hs.window.filter:getWindows(): windows are sorted in order of creation, oldest first (see also hs.window.filter:setSortOrder())
Signature
hs.window.filter.sortByCreatedLast
Type
Constant
Description
Sort order for hs.window.filter:getWindows(): windows are sorted in order of creation, newest first (see also hs.window.filter:setSortOrder())
Signature
hs.window.filter.sortByFocused
Type
Constant
Description
Sort order for hs.window.filter:getWindows(): windows are sorted in order of focus received, least recently first (see also hs.window.filter:setSortOrder())
Signature
hs.window.filter.sortByFocusedLast
Type
Constant
Description
Sort order for hs.window.filter:getWindows(): windows are sorted in order of focus received, most recently first (see also hs.window.filter:setSortOrder())
Notes
  • This is the default sort order for all windowfilters
Signature
hs.window.filter.windowAllowed
Type
Constant
Description
Pseudo-event for hs.window.filter:subscribe(): a previously rejected window (or a newly created one) is now allowed
Notes
  • this pseudo-event will be emitted before the actual event(s) (e.g. windowCreated) that caused the window to be allowed
Signature
hs.window.filter.windowCreated
Type
Constant
Description
Event for hs.window.filter:subscribe(): a new window was created
Signature
hs.window.filter.windowDestroyed
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was destroyed
Signature
hs.window.filter.windowFocused
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window received focus
Signature
hs.window.filter.windowFullscreened
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was expanded to fullscreen
Signature
hs.window.filter.windowHidden
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was hidden (its app was hidden, e.g. via cmd-h)
Signature
hs.window.filter.windowInCurrentSpace
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window is now in the current Mission Control Space, due to
Signature
hs.window.filter.windowMinimized
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was minimized
Signature
hs.window.filter.windowMoved
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was moved or resized, including toggling fullscreen/maximize
Signature
hs.window.filter.windowNotInCurrentSpace
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window that used to be in the current Mission Control Space isn't anymore,
Signature
hs.window.filter.windowNotOnScreen
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window is no longer actually visible on any screen because it was minimized, closed,
Signature
hs.window.filter.windowNotVisible
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window is no longer "visible" (in any Mission Control Space, as per hs.window:isVisible())
Signature
hs.window.filter.windowOnScreen
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window became actually visible on screen (i.e. it's "visible" as per hs.window:isVisible()
Signature
hs.window.filter.windowRejected
Type
Constant
Description
Pseudo-event for hs.window.filter:subscribe(): a previously allowed window (or a window that's been destroyed) is now rejected
Notes
  • this pseudo-event will be emitted after the actual event(s) (e.g. windowDestroyed) that caused the window to be rejected
Signature
hs.window.filter.windowsChanged
Type
Constant
Description
Pseudo-event for hs.window.filter:subscribe(): the list of allowed windows (as per windowfilter:getWindows()) has changed
Notes
  • callbacks for this event will receive (as the first argument) either a random window among the currently allowed ones, or nil if the windowfilter is rejecting all windows
  • similarly, the second argument passed to callbacks (window's app name) will be nil if the windowfilter is rejecting all windows
  • this pseudo-event will be emitted after the actual event(s) that caused the list of allowed windows to change
Signature
hs.window.filter.windowTitleChanged
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window's title changed
Signature
hs.window.filter.windowUnfocused
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window lost focus
Signature
hs.window.filter.windowUnfullscreened
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was reverted back from fullscreen
Signature
hs.window.filter.windowUnhidden
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was unhidden (its app was unhidden, e.g. via cmd-h)
Signature
hs.window.filter.windowUnminimized
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window was unminimized
Signature
hs.window.filter.windowVisible
Type
Constant
Description
Event for hs.window.filter:subscribe(): a window became "visible" (in any Mission Control Space, as per hs.window:isVisible())

Variables

Signature
hs.window.filter.allowedWindowRoles
Type
Variable
Description
A table for window roles (as per hs.window:subrole()) that are allowed by default.
Notes
  • You can have fine grained control of allowed window roles via the setAppFilter, setDefaultFilter, setOverrideFilter methods.
  • If you know what you're doing you can override the allowed window roles globally by changing this variable, but this is discouraged.
Signature
hs.window.filter.forceRefreshOnSpaceChange
Type
Variable
Description
Tells all windowfilters whether to refresh all windows when the user switches to a different Mission Control Space.
Notes
  • If you defined one or more Spaces-aware windowfilters (i.e. when the currentSpace field of a filter is present), windows need refreshing at every space change anyway, so this variable is ignored
Signature
hs.window.filter.ignoreAlways
Type
Variable
Description
A table of application names (as per hs.application:name()) that are always ignored by this module.
Notes
  • As the name implies, even the empty, "allow all" windowfilter will ignore these apps.
  • You don't need to keep this table up to date, since non GUI apps will simply never show up anywhere; this table is just used as a "root" filter to gain a (very small) performance improvement.

Functions

focusEast

Signature
hs.window.filter.focusEast()
Type
Function
Description
Convenience function to focus the nearest window to the east
Parameters
  • None
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.filter.defaultCurrentSpace:focusWindowEast(nil,nil,true)
Signature
hs.window.filter.focusNorth()
Type
Function
Description
Convenience function to focus the nearest window to the north
Parameters
  • None
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.filter.defaultCurrentSpace:focusWindowNorth(nil,nil,true)
Signature
hs.window.filter.focusSouth()
Type
Function
Description
Convenience function to focus the nearest window to the south
Parameters
  • None
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.filter.defaultCurrentSpace:focusWindowSouth(nil,nil,true)

focusWest

Signature
hs.window.filter.focusWest()
Type
Function
Description
Convenience function to focus the nearest window to the west
Parameters
  • None
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.filter.defaultCurrentSpace:focusWindowWest(nil,nil,true)

isGuiApp

Signature
hs.window.filter.isGuiApp(appname) -> boolean
Type
Function
Description
Checks whether an app is a known non-GUI app, as per hs.window.filter.ignoreAlways
Parameters
  • appname - name of the app to check as per hs.application:name()
Returns
  • false if the app is a known non-GUI (or not accessible) app; true otherwise
Signature
hs.window.filter.switchedToSpace(space)
Type
Function
Description
Callback to inform all windowfilters that the user initiated a switch to a (numbered) Mission Control Space.
Parameters
  • space - the Space number the user is switching to
Returns
  • None
Notes
  • Only use this function if "Displays have separate Spaces" and "Automatically rearrange Spaces" are OFF in System Preferences>Mission Control
  • Calling this function will set hs.window.filter.forceRefreshOnSpaceChange to false
  • If you defined one or more Spaces-aware windowfilters (i.e. when the currentSpace field of a filter is present), windows need refreshing at every space change anyway, so using this callback will not result in improved performance

Constructors

copy

Signature
hs.window.filter.copy(windowfilter[,logname[,loglevel]]) -> hs.window.filter object
Type
Constructor
Description
Returns a copy of an hs.window.filter object that you can further restrict or expand
Parameters
  • windowfilter - an hs.window.filter object to copy
  • logname - (optional) name of the hs.logger instance for the new windowfilter; if omitted, the class logger will be used
  • loglevel - (optional) log level for the hs.logger instance for the new windowfilter

new

Signature
hs.window.filter.new(fn[,logname[,loglevel]]) -> hs.window.filter object
Type
Constructor
Description
Creates a new hs.window.filter instance
Parameters
  • fn
  • if nil, returns a copy of the default windowfilter, including any customizations you might have applied to it so far; you can then further restrict or expand it
  • if true, returns an empty windowfilter that allows every window
  • if false, returns a windowfilter with a default rule to reject every window
  • if a string or table of strings, returns a windowfilter that only allows visible windows of the specified apps as per hs.application:name()
  • if a table, you can fully define a windowfilter without having to call any methods after construction; the table must be structured as per hs.window.filter:setFilters(); if not specified in the table, the default filter in the new windowfilter will reject all windows
  • otherwise it must be a function that accepts an hs.window object and returns true if the window is allowed or false otherwise; this way you can define a fully custom windowfilter
  • logname - (optional) name of the hs.logger instance for the new windowfilter; if omitted, the class logger will be used
  • loglevel - (optional) log level for the hs.logger instance for the new windowfilter
Returns
  • a new windowfilter instance

Methods

allowApp

Signature
hs.window.filter:allowApp(appname) -> hs.window.filter object
Type
Method
Description
Sets the windowfilter to allow all visible windows belonging to a specific app
Parameters
  • appname - app name as per hs.application:name()
Returns
  • the hs.window.filter object for method chaining
Notes
  • this is just a convenience wrapper for windowfilter:setAppFilter(appname,{visible=true})
Signature
hs.window.filter:focusWindowEast(window, frontmost, strict)
Type
Method
Description
Focuses the nearest window to the east of a given window
Parameters
  • window - (optional) an hs.window object; if nil, hs.window.frontmostWindow() will be used
  • frontmost - (optional) boolean, if true focuses the nearest window that isn't occluded by any other window in this windowfilter
  • strict - (optional) boolean, if true only consider windows at an angle between 45° and -45° on the eastward axis
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.focusWindowEast(window,self:getWindows(),...)
  • You'll likely want to add :setCurrentSpace(true) to the windowfilter used for this method call
Signature
hs.window.filter:focusWindowNorth(window, frontmost, strict)
Type
Method
Description
Focuses the nearest window to the south of a given window
Parameters
  • window - (optional) an hs.window object; if nil, hs.window.frontmostWindow() will be used
  • frontmost - (optional) boolean, if true focuses the nearest window that isn't occluded by any other window in this windowfilter
  • strict - (optional) boolean, if true only consider windows at an angle between 45° and -45° on the southward axis
Returns
  • None
Notes
  • This is a convenience wrapper that performs hs.window.focusWindowNorth(window,self:getWindows(),...)
  • You'll likely want to add :setCurrentSpace(true) to the windowfilter used for this method call