# Actions

"Every layout action with arguments: focus, move, fullscreen, floating, spawn."

Every layout action, with its arguments — the same grammar `scootctl --help` prints, a config file's `[binds]` values use, and `[autostart]` entries run.

## Actions

The same grammar `scootctl --help` prints -- and `scoot --help` embeds --
and the same one a config file's `[binds]` values use: one parser handles
all three.

```
focus-column|move-column|consume-or-expel   left|right
focus-window|move-window                    up|down
focus-workspace|move-window-to-workspace    up|down
focus-window-id ID | focus-workspace-index N [--output ID] | move-window-to-workspace-index N | focus-output ID | move-window-to-output ID | focus-output-index N | move-window-to-output-index N | focus-output-left | focus-output-right | move-window-to-output-left | move-window-to-output-right | cycle-column-width | set-column-width N | toggle-fullscreen | set-fullscreen ID on|off | toggle-maximize | set-maximized ID on|off | close | spawn COMMAND... | quit
toggle-floating | set-floating ID on|off | toggle-floating-focus
move-floating ID X Y | resize-floating ID WIDTH HEIGHT
```

`focus-workspace-index N` and `move-window-to-workspace-index N` are 0-based; out of range does nothing (a move leaves the window where it is). With `--output ID`, `focus-workspace-index N` switches that output's workspace instead of the focused output's -- what a per-output bar's workspace buttons drive, and what an agent uses to address a monitor by id without a separate focus-output step first. The switch moves keyboard focus to that output, the way a click on a window focuses its output -- even onto the already-active workspace -- while an unknown id or a stale index does nothing at all, not even moving focus. The id is the output id `scootctl outputs` reports (stable for the session, never reused, so a replugged monitor comes back under a fresh one). On the wire this is a separate additive action tag, so no protocol bump was needed: `{"action":"focus_output_workspace_index","output":2,"index":1}`. Without the flag the request is byte-for-byte what it always was. `set-column-width N` is 0-based into the session's `[layout] column_widths`; out of range does nothing, like a stale workspace index. `focus-output ID` and `move-window-to-output ID` take an output id as `scootctl outputs` reports it (output ids are stable for the session, unlike workspace positions); an unknown id does nothing. `focus-output-index N` and `move-window-to-output-index N` take a 0-based position in the output list in creation order instead -- the first screen, the second screen -- so they keep reaching a monitor that was unplugged and plugged back in (which comes back under a fresh id); out of range does nothing. `focus-output-left`, `focus-output-right`, `move-window-to-output-left` and `move-window-to-output-right` step to the neighbouring output in geometry order (x, then y), wrapping around: left of the leftmost is the rightmost, and the reverse. With one output there is nowhere to go, so either direction does nothing; with two, either names the other. These four are what the default `Super+comma` / `Super+period` binds send (see [configuration.md](../scoot/outputs.md#moving-across-outputs) for the order rule and the behavior change from the old absolute binds). On the wire they are new additive action tags, so no protocol bump was needed: `{"action":"focus_output_direction","direction":"left"}` (or `"right"`) and `{"action":"move_window_to_output_direction","direction":"left"}` (or `"right"`). A move carries the focused window to the target output's active workspace and follows it there; focusing an output with no windows focuses nothing. `toggle-fullscreen` flips the focused window in and out of fullscreen (the `Super+f` bind); `set-fullscreen ID on|off` sets one window's state by id, idempotently, without moving focus — so a window whose column is not focused becomes fullscreen but does not cover the screen until its column is focused, and an unknown id, or a window stacked under another in its column, does nothing (see [configuration.md](../scoot/keybindings.md) for what fullscreen does to the layout). `toggle-maximize` flips the focused window in and out of maximized (the `Super+m` bind): it fills the output's usable area, bar visible, instead of covering the whole output like fullscreen; `set-maximized ID on|off` sets one window's state by id, idempotently, without moving focus, with the same non-focused-column and stacked-window rules. Fullscreen wins while both hold: leaving fullscreen returns to maximized. Both are refused while the session is locked, like every action. `toggle-floating` floats the focused window above its workspace's strip or puts it back as a column right of the strip's focused one (the `Super+Shift+Space` bind); `set-floating ID on|off` does the same to one window by id, idempotently, without moving focus; `toggle-floating-focus` moves focus between the focused workspace's floating windows and its strip (`Super+Space`), and does nothing when the other side is empty. With a floating window focused, `focus-column` returns to the strip, `focus-window` cycles the floating windows, and `move-column`, `move-window`, `consume-or-expel`, `cycle-column-width` and `set-column-width` do nothing (see [configuration.md](../scoot/windows.md). `move-floating ID X Y` puts a floating window's top-left corner at `X`, `Y` in the same global logical coordinates `windows` reports `rect` in, clamped inside the usable area of the output it lands on; a position whose middle is over another output moves the window to that output's active workspace (and focus with it, if it was focused). `resize-floating ID WIDTH HEIGHT` asks a floating window to take that size, keeping its top-left corner: clamped to the window's own minimum and maximum size, to the room between that corner and the usable area's bottom-right (the room wins over a minimum that does not fit), and to at least 1. The window draws the new size when it answers the configure, so `windows` reports it a frame or so later (`wait-idle` first when that matters). Both are kept -- the window stays where it was put until something moves it (a user's drag, another action, its output changing size, which re-centres it) -- and both do nothing (and answer `ok`, like every by-id action) for an unknown id, a tiled window, a fullscreen one or a maximized one. Like every action they are refused while the session is locked. The `move_floating`, `resize_floating`, `focus_output_index`, `move_focused_window_to_output_index`, `toggle_maximize` and `set_maximized` wire actions are additive (`{"action":"move_floating","id":7,"x":10,"y":12}`, `{"action":"resize_floating","id":7,"width":640,"height":480}`, `{"action":"focus_output_index","index":1}`, `{"action":"toggle_maximize"}`, `{"action":"set_maximized","id":7,"maximized":true}`; a negative size is a decode error); `PROTOCOL_VERSION` did not change. `spawn` is
split on whitespace and not run through a shell, so an argument containing a
space can't be expressed this way. Every action is refused while the session
is locked.
