| 1 |
# Window Rules |
| 2 |
|
| 3 |
Window rules let you match newly opened windows and automatically apply actions: force floating, assign to a workspace, or set initial geometry. |
| 4 |
|
| 5 |
## Defining rules |
| 6 |
|
| 7 |
```lua |
| 8 |
gar.rule(match_table, actions_table) |
| 9 |
``` |
| 10 |
|
| 11 |
Both arguments are Lua tables. |
| 12 |
|
| 13 |
## Match criteria |
| 14 |
|
| 15 |
| Key | Type | Description | |
| 16 |
|-----|------|-------------| |
| 17 |
| `app_name` | string | Application name (e.g., `"Calculator"`, `"Firefox"`) | |
| 18 |
| `app_bundle` | string | Bundle identifier (e.g., `"com.apple.calculator"`) | |
| 19 |
| `title` | string | Window title | |
| 20 |
|
| 21 |
If multiple criteria are specified, all must match (AND logic). |
| 22 |
|
| 23 |
### Finding app names and bundle IDs |
| 24 |
|
| 25 |
Use the IPC to inspect running windows: |
| 26 |
|
| 27 |
```bash |
| 28 |
tarmacctl get-windows | jq '.data[] | {app_name, app_bundle: .app_bundle, title}' |
| 29 |
``` |
| 30 |
|
| 31 |
Or check with macOS tools: |
| 32 |
|
| 33 |
```bash |
| 34 |
# Bundle ID |
| 35 |
mdls -name kMDItemCFBundleIdentifier /Applications/Safari.app |
| 36 |
|
| 37 |
# App name (usually the .app filename without .app) |
| 38 |
``` |
| 39 |
|
| 40 |
## Actions |
| 41 |
|
| 42 |
| Key | Type | Description | |
| 43 |
|-----|------|-------------| |
| 44 |
| `floating` | bool | Force into floating state | |
| 45 |
| `workspace` | number/string | Assign to workspace. Number (1-10) or `"special:name"`. | |
| 46 |
| `geometry` | table | Set initial position and size: `{x, y, width, height}` | |
| 47 |
|
| 48 |
## Examples |
| 49 |
|
| 50 |
### Float specific apps |
| 51 |
|
| 52 |
```lua |
| 53 |
gar.rule({ app_name = "Calculator" }, { floating = true }) |
| 54 |
gar.rule({ app_name = "System Settings" }, { floating = true }) |
| 55 |
gar.rule({ app_name = "Finder" }, { floating = true }) |
| 56 |
gar.rule({ app_name = "Archive Utility" }, { floating = true }) |
| 57 |
gar.rule({ title = "Picture in Picture" }, { floating = true }) |
| 58 |
``` |
| 59 |
|
| 60 |
### Assign apps to workspaces |
| 61 |
|
| 62 |
```lua |
| 63 |
gar.rule({ app_bundle = "com.apple.Safari" }, { workspace = 2 }) |
| 64 |
gar.rule({ app_name = "Slack" }, { workspace = 3 }) |
| 65 |
gar.rule({ app_name = "Mail" }, { workspace = 4 }) |
| 66 |
gar.rule({ app_name = "Spotify" }, { workspace = "special:music" }) |
| 67 |
``` |
| 68 |
|
| 69 |
### Set initial geometry for floating windows |
| 70 |
|
| 71 |
```lua |
| 72 |
gar.rule( |
| 73 |
{ app_name = "Music" }, |
| 74 |
{ floating = true, geometry = { 200, 200, 800, 600 } } |
| 75 |
) |
| 76 |
``` |
| 77 |
|
| 78 |
The geometry table is `{x, y, width, height}` in pixels. |
| 79 |
|
| 80 |
### Combine criteria |
| 81 |
|
| 82 |
```lua |
| 83 |
-- Only match Safari windows with a specific title |
| 84 |
gar.rule( |
| 85 |
{ app_bundle = "com.apple.Safari", title = "Developer Tools" }, |
| 86 |
{ floating = true } |
| 87 |
) |
| 88 |
``` |
| 89 |
|
| 90 |
## Rule evaluation |
| 91 |
|
| 92 |
Rules are evaluated in the order they're defined in the config. When a new window appears, tarmac checks each rule against the window's properties. The first matching rule wins — subsequent rules are not checked. |
| 93 |
|
| 94 |
Rules are checked when: |
| 95 |
- A new window is detected during polling |
| 96 |
- An existing window changes its title (re-evaluated) |
| 97 |
|
| 98 |
## Rules and config reload |
| 99 |
|
| 100 |
On config reload, all rules are replaced by the new config's rules. Existing windows are not re-evaluated against the new rules — they only apply to newly opened windows. |
| 101 |
|
| 102 |
## Debugging rules |
| 103 |
|
| 104 |
Use `RUST_LOG=tarmac=debug` to see which rules match: |
| 105 |
|
| 106 |
```bash |
| 107 |
RUST_LOG=tarmac=debug tarmac |
| 108 |
``` |
| 109 |
|
| 110 |
You'll see log lines like: |
| 111 |
|
| 112 |
``` |
| 113 |
DEBUG tarmac: rule matched: app_name="Calculator" → floating=true |
| 114 |
``` |