| 1 |
# Lua API (gar) |
| 2 |
|
| 3 |
tarmac exposes a global `gar` table in the Lua config environment. This is the sole interface for configuring tarmac from `init.lua`. |
| 4 |
|
| 5 |
## gar.set(key, value) |
| 6 |
|
| 7 |
Set a configuration option. Both arguments are strings. |
| 8 |
|
| 9 |
```lua |
| 10 |
gar.set("gap_inner", "8") |
| 11 |
gar.set("mod_key", "command") |
| 12 |
gar.set("focus_follows_mouse", "true") |
| 13 |
``` |
| 14 |
|
| 15 |
See [Settings Reference](/docs/configuration/settings) for all available keys. |
| 16 |
|
| 17 |
## gar.bind(keys, action) |
| 18 |
|
| 19 |
Register a global hotkey. The `keys` string uses `+` to combine modifiers and a key name. The `action` string specifies what happens when the key is pressed. |
| 20 |
|
| 21 |
```lua |
| 22 |
gar.bind("mod+h", "focus left") |
| 23 |
gar.bind("mod+shift+q", "close") |
| 24 |
gar.bind("mod+return", "spawn terminal") |
| 25 |
``` |
| 26 |
|
| 27 |
### Key string format |
| 28 |
|
| 29 |
Modifiers: |
| 30 |
- `mod` — replaced by the current `mod_key` setting |
| 31 |
- `shift` — Shift key |
| 32 |
- `ctrl` — Control key |
| 33 |
- `alt` — Option key |
| 34 |
- `fn` — Function key |
| 35 |
|
| 36 |
Key names: `a`-`z`, `0`-`9`, `return`, `space`, `tab`, `escape`, `delete`, `grave`, `minus`, `equal`, `leftbracket`, `rightbracket`, `semicolon`, `quote`, `comma`, `period`, `slash`, `backslash`, `left`, `right`, `up`, `down`, `f1`-`f12`. |
| 37 |
|
| 38 |
### Action strings |
| 39 |
|
| 40 |
| Action | Description | |
| 41 |
|--------|-------------| |
| 42 |
| `focus left\|right\|up\|down` | Move focus in a direction | |
| 43 |
| `swap left\|right\|up\|down` | Swap the focused window with its neighbor | |
| 44 |
| `resize left\|right\|up\|down` | Adjust the split ratio toward a direction | |
| 45 |
| `close` | Close the focused window | |
| 46 |
| `equalize` | Reset all split ratios to 50/50 | |
| 47 |
| `toggle-floating` | Toggle the focused window between tiled and floating | |
| 48 |
| `workspace N` | Switch to workspace N (1-10) | |
| 49 |
| `move-to-workspace N` | Move focused window to workspace N | |
| 50 |
| `toggle-special NAME` | Toggle a named scratchpad's visibility | |
| 51 |
| `move-to-special NAME` | Move focused window to a named scratchpad | |
| 52 |
| `focus-monitor next\|prev` | Focus the next or previous monitor | |
| 53 |
| `move-to-monitor next\|prev` | Move focused window to the next or previous monitor | |
| 54 |
| `workspace next\|prev` | Cycle to next or previous workspace | |
| 55 |
| `spawn terminal` | Run the `terminal` command | |
| 56 |
| `reload` | Hot reload the config | |
| 57 |
|
| 58 |
### Dynamic keybinds with Lua loops |
| 59 |
|
| 60 |
Since the config is Lua, you can generate repetitive keybinds programmatically: |
| 61 |
|
| 62 |
```lua |
| 63 |
for i = 1, 9 do |
| 64 |
gar.bind("mod+" .. i, "workspace " .. i) |
| 65 |
gar.bind("mod+shift+" .. i, "move-to-workspace " .. i) |
| 66 |
end |
| 67 |
gar.bind("mod+0", "workspace 10") |
| 68 |
gar.bind("mod+shift+0", "move-to-workspace 10") |
| 69 |
``` |
| 70 |
|
| 71 |
## gar.rule(match, actions) |
| 72 |
|
| 73 |
Define a window rule. The first argument is a table of match criteria, the second is a table of actions to apply. |
| 74 |
|
| 75 |
```lua |
| 76 |
gar.rule({ app_name = "Calculator" }, { floating = true }) |
| 77 |
gar.rule({ app_bundle = "com.apple.Safari" }, { workspace = 2 }) |
| 78 |
gar.rule({ app_name = "System Settings" }, { floating = true }) |
| 79 |
gar.rule({ title = "Picture in Picture" }, { floating = true }) |
| 80 |
``` |
| 81 |
|
| 82 |
### Match criteria |
| 83 |
|
| 84 |
| Key | Type | Description | |
| 85 |
|-----|------|-------------| |
| 86 |
| `app_name` | string | Match by application name (e.g., `"Firefox"`) | |
| 87 |
| `app_bundle` | string | Match by bundle identifier (e.g., `"com.apple.Safari"`) | |
| 88 |
| `title` | string | Match by window title | |
| 89 |
|
| 90 |
All specified criteria must match (AND logic). If multiple criteria are set, all must be true. |
| 91 |
|
| 92 |
### Rule actions |
| 93 |
|
| 94 |
| Key | Type | Description | |
| 95 |
|-----|------|-------------| |
| 96 |
| `floating` | bool | Force window into floating state | |
| 97 |
| `workspace` | string/number | Assign to workspace: `1`-`10` or `"special:name"` | |
| 98 |
| `geometry` | table | Initial position and size: `{x, y, width, height}` | |
| 99 |
|
| 100 |
Geometry example: |
| 101 |
|
| 102 |
```lua |
| 103 |
gar.rule( |
| 104 |
{ app_name = "Music" }, |
| 105 |
{ floating = true, geometry = { 100, 100, 800, 600 } } |
| 106 |
) |
| 107 |
``` |
| 108 |
|
| 109 |
See [Window Rules](/docs/window-rules) for more details. |
| 110 |
|
| 111 |
## gar.on(event, callback) |
| 112 |
|
| 113 |
Register a Lua callback for a window manager event. Callbacks receive rich data as Lua tables. |
| 114 |
|
| 115 |
```lua |
| 116 |
gar.on("window_focused", function(info) |
| 117 |
print("focused: " .. info.app_name .. " - " .. info.title) |
| 118 |
end) |
| 119 |
|
| 120 |
gar.on("workspace_changed", function(old, new) |
| 121 |
gar.exec("echo '" .. new .. "' > /tmp/tarmac-workspace") |
| 122 |
end) |
| 123 |
|
| 124 |
gar.on("window_created", function(info) |
| 125 |
print("new window: " .. info.app_name) |
| 126 |
end) |
| 127 |
|
| 128 |
gar.on("layout_changed", function(info) |
| 129 |
print("workspace " .. info.workspace .. ": " .. info.window_count .. " windows") |
| 130 |
end) |
| 131 |
|
| 132 |
gar.on("monitor_changed", function(info) |
| 133 |
print("monitor " .. info.index) |
| 134 |
end) |
| 135 |
``` |
| 136 |
|
| 137 |
Available events: `workspace_changed`, `window_focused`, `window_created`, `window_closed`, `layout_changed`, `monitor_changed`. |
| 138 |
|
| 139 |
See [Events & Hooks](/docs/events-hooks) for the full event reference. |
| 140 |
|
| 141 |
## gar.exec(command) |
| 142 |
|
| 143 |
Execute a shell command via `/bin/sh -c`. Runs asynchronously (does not block tarmac). |
| 144 |
|
| 145 |
```lua |
| 146 |
gar.exec("open -a Safari") |
| 147 |
gar.exec("osascript -e 'display notification \"tarmac reloaded\"'") |
| 148 |
``` |
| 149 |
|
| 150 |
## gar.exec_once(command) |
| 151 |
|
| 152 |
Like `gar.exec()`, but first checks if a process matching the command is already running. If it is, the command is skipped. Useful for autostart programs that shouldn't spawn duplicates on config reload. |
| 153 |
|
| 154 |
```lua |
| 155 |
gar.exec_once("open -a WezTerm") |
| 156 |
gar.exec_once("open -a Firefox") |
| 157 |
``` |
| 158 |
|
| 159 |
## gar.special_workspace(name, options) |
| 160 |
|
| 161 |
Configure a named special workspace (scratchpad). These are overlay workspaces that can be toggled on any monitor. |
| 162 |
|
| 163 |
```lua |
| 164 |
gar.special_workspace("term", { |
| 165 |
position = "center", |
| 166 |
width = 0.8, |
| 167 |
height = 0.8, |
| 168 |
}) |
| 169 |
|
| 170 |
gar.special_workspace("music", { |
| 171 |
position = "bottom", |
| 172 |
width = 1.0, |
| 173 |
height = 0.4, |
| 174 |
}) |
| 175 |
``` |
| 176 |
|
| 177 |
### Options |
| 178 |
|
| 179 |
| Key | Type | Default | Description | |
| 180 |
|-----|------|---------|-------------| |
| 181 |
| `position` | string | `"center"` | Where to place the overlay: `"center"`, `"top"`, `"bottom"` | |
| 182 |
| `width` | number | `0.7` | Width as fraction of screen (0.0 - 1.0) | |
| 183 |
| `height` | number | `0.7` | Height as fraction of screen (0.0 - 1.0) | |
| 184 |
|
| 185 |
After defining a special workspace, bind a key to toggle it: |
| 186 |
|
| 187 |
```lua |
| 188 |
gar.bind("mod+grave", "toggle-special term") |
| 189 |
gar.bind("mod+shift+grave", "move-to-special term") |
| 190 |
``` |
| 191 |
|
| 192 |
See [Special Workspaces](/docs/workspaces/special-workspaces) for usage details. |
| 193 |
|