cow(1)

NAME

cow - Compositor On Wayland

SYNOPSIS

cow [-hnv] [-c config] [-l level] [-o file] [-C auto|always|never]

DESCRIPTION

cow is a window manager using River as the compositor.

A large virtual desktop can be created, effectively splitting the screen into an N x M matrix, called pages. These pages can then be viewed by using the mouse on the edge of the screen, or by using the scroll command.

Desktops provide a means of aggregating open applications into different areas.

cow provides commands which can be used to change settings, setup key/mouse bindings, etc.

OPTIONS

-c config

Load the configuration from config instead of searching the default locations.

-h

Print usage and exit.

-n

Skip the installed defaults. Built-in fallback values and the selected user configuration still apply. This also applies to configuration reloads.

-l level

Set the minimum logging level. Valid levels are trace, debug, info, warn, error, and fatal. The default is info. The COW_LOG_LEVEL environment variable supplies the default when this option is omitted.

-o file

Append log messages to file instead of standard error. Specify - for standard error. The COW_LOG_FILE environment variable supplies the default when this option is omitted.

-C auto|always|never

Control ANSI colour in log output. auto, the default, enables colour only when logging to a terminal and NO_COLOR is unset. Automatic mode never colours a log file. The COW_LOG_COLOR environment variable supplies the default when this option is omitted.

-v

Print version and exit.

LOGGING

Log records contain a timestamp, severity, source file, line number, function, and message. For example:

15:02:37.916 ERROR [src/wm.c:1542 cow_wm_init] failed to connect to Wayland display

The selected level includes messages at that level and every more severe level:

Level Purpose
trace High-volume diagnostics: commands and their execution times, rule matching, and manage-cycle requests and timings.
debug Routine cow activity: window mapping, seats, desk and page changes, usable-area and output geometry, focus, pointer operations, sockets, client connections, and transient state details.
info (default) Concise lifecycle information: configuration and state loads, the compositor connection, output topology, window opening and closing, desk and page switches, reloads, restarts, and shutdown.
warn Recoverable problems.
error Operation failures which do not necessarily terminate cow.
fatal Unrecoverable failures before termination.

Raw Wayland protocol traffic is left to WAYLAND_DEBUG and River’s logging.

Trace output contains complete command strings and may therefore expose sensitive command arguments. Enable it only while diagnosing a problem and handle trace files accordingly.

Log files are opened in append mode. Colour is enabled automatically only for terminal output; redirected output and explicit log files are plain text unless overridden with -C always.

COLOUR VALUES

Colour settings accept 0xRRGGBB for an opaque colour or 0xRRGGBBAA for RGBA. In the latter form, 00 is fully transparent and FF is fully opaque. For example, 0x282828CC is dark grey at 80 percent opacity.

Alpha applies to surfaces drawn by CoW and its companion programs, including window decorations, menus, icons, cowpager, and cowbuttons. It does not change the opacity of application content. A translucent output background reveals the compositor’s background below CoW’s background surface.

OUTPUT MANAGEMENT

CoW does not manage outputs directly, this is the job of river(1).

Tools such as wlr-randr, wdisplays, or kanshi can be used to manage this.

cow will assign all outputs a number to a tree with a defined order. That order is top-down, left-to-right, so far example, the following diagram illustrates output configuration and their assigned number:

out1 (1)
out2 (2)    out3 (3)    out4 (4)

ANATOMY OF A WINDOW

cow puts a decorative border around windows. This border consists of a bar on each side, and a small L-shaped section on each corner. There is an additional top bar called the titlebar which is used to display the name of the window.

In addition, there are ten titlebar buttons which can be defined and bound to any cow command, such as close, maximize, or iconify.

The diagram below shows an outline of a typical window in cow:

   NW                              N                             NE
   +++──────────────────────────────────────────────────────────+++
   + [1][3][5][7][9]          Titlebar            [2][4][6][8][0] +
   +──────────────────────────────────────────────────────────────+
   │                                                              │
   │                                                              │
   │                                                              │
 W │                                                              │ E
   │                                                              │
   │                                                              │
   │                                                              │
   │                                                              │
   +                                                              +
   +++──────────────────────────────────────────────────────────+++
   SW                              S                             SE

The labels of the window indicate the different components that can be changed.

A window is made up of sides – and are referenced like that of a compass.

All eight parts of the window border (including corners) can be styled via the decor command.

The titlebar can also be styled or disabled completely via the decor command.

CONTAINERS

Any window can become a container.

A container groups windows within one outer frame. It can hold tabs, split panes, or panes with their own tabs. The outer titlebar and border operate on the whole container; each pane has its own titlebar and controls.

This example has two columns: one tall pane on the left and two stacked panes on the right. The inner titlebars show the default pane-button symbols; outer buttons are numbered as in ANATOMY OF A WINDOW.

+++───────────────────────────────────────────────────────────────+++
+ [1] Container title                                         [2][0]+
+─────────────────────────────────┬─────────────────────────────────+
│[↻][↓] Pane 1 [□][▯][-][↗][×]    │[↻][↓] Pane 2 [□][▯][-][↗][×]    │
├─────────────────────────────────┼─────────────────────────────────┤
│                                 │                                 │
│                                 │    Application B                │
│    Application A                │                                 │
│                                 ├─────────────────────────────────┤
│                                 │[↻][↓] Pane 3 [□][▯][-][↗][×]    │
│                                 ├─────────────────────────────────┤
│                                 │                                 │
│                                 │    Application C                │
+                                 │                                 +
+++───────────────────────────────┴───────────────────────────────+++

The outer titlebar keeps the normal window buttons. Inner titlebars use separate bind-decoration pane: bindings, with these default actions:

  • ↻ (button 1): rotate clockwise; double-click for anticlockwise.
  • ↓ (button 3): hide the pane, leaving its applications running.
  • □ (button 8): toggle zoom to fill the container.
  • ▯ (button 6): toggle zoom to fill the pane’s column.
  • - (button 4): hide this pane’s titlebar.
  • ↗ (button 2): detach the active window from the container.
  • × (button 0): close the active window in the pane.

Pane buttons use the decoration’s colours and relief. A pane containing multiple tabs keeps its titlebar visible and omits button 4.

Drag a pane titlebar to rearrange it within the container, or outside to detach its active window. Double-click a pane titlebar to collapse or restore its row. Drag the outer titlebar to move the whole container.

See the WINDOW CONTAINERS section for more information,

THE VIRTUAL DESKTOP

cow provides up to ten virtual desktops. The screen is a viewport onto a desktop which may be larger than the screen. Several distinct desktops can be accessed (for example, having different desktops for different projects).

Because the desktop can be larger than the physical screen, it can therefore be divided into N x M regions, called pages. This configuration is then applied to all desktops.

For example, the following would define nine pages, arranged in a square:

set desktop_size 3 3

Which would conceptually look like this:

+----+----+----+
|    |    |    |
|    |    |    |
+----+----+----*
|    |    |    |
|    |    |    |
+----+----+----+
|    |    |    |
|    |    |    |
+----+----+----+

All pages are the same size.

Pages can be moved to/from by enabling edge_scroll by resting the mouse pointer at the screen edges. See the set command.

Windows on pages can be be viewed using the cowpager(1) command.

Windows can also be moved between pages by dragging them to the edge of the output, assuming edge_scroll has been enabled. See the set command.

cow keeps its windows in a Z-ordered stacking order. That is to say, windows can be raised or lowered over one another. This can be controlled via the winops command.

LAYERS

Every window belongs to a numbered layer. The layer range is [0, max_layer] (default 16) and newly mapped windows receive default_layer (default 4).

A window in layer 8 is always above every window in layer 4 regardless of focus, raise, or lower. winops -r and winops -l operate within the window’s own layer: they reorder among windows in that layer but never push the window across a layer boundary.

For instance:

  • stay-on-top – winops -L top (toggles between max_layer and default_layer).

  • stay-on-bottom – winops -L bottom (toggles between 0 and default_layer).

  • Always-on-top from launch – bind a rule:

rule -g -n mpv-pip -T on-map -s %mpv winops -t %mpv -L =top

Use =top (rather than bare top) inside rules so a re-map event never accidentally toggles the layer back to default.

Iconified windows’ icons are pinned to layer 0 regardless of the parent window’s layer.

Focus-on-raise (the raise_on_focus setting) is layer-aware: focusing a layer-4 window with a layer-8 window present never visually pops the layer-4 window above the layer-8 one. The titlebar/border colours remain the only cross-layer signal of focus.

The two layer-aware settings are:

  • set default_layer N – layer assigned to newly mapped windows.

  • set max_layer N – cap for -L; raising the cap re-permits bigger numbers, lowering it clamps every existing window’s layer.

The on-layer-change rule event fires whenever a window’s layer changes – this is the hook for visual cues that depend on layer. For example, to tint the titlebar of any window in the top layer:

decor -d top-layer titlebar.active_colour 0xC04030
rule -g -n top-layer-tint -T on-layer-change
    decor -a top-layer

The rule receives the same per-view scope as other view-bearing events, so -s / -t selectors and per-view decor applies all work as expected.

When the rule body is an exec line, the per-firing JSON payload is available to the spawned shell as the COW_EVENT environment variable. For on-layer-change the payload is:

{"prev": P, "current": N}

so a rule like:

rule -g -n log-layer -T on-layer-change
    exec sh -c 'printf "%s\n" "$COW_EVENT" >> ~/cow-layer.log'

writes one line per layer transition. jq makes parsing trivial (jq .current <<<"$COW_EVENT"). Other rule events without structured payload leave COW_EVENT unset; the variable is only visible inside the exec’d shell and never affects cow itself or other concurrent rules.

SCRIPTING

Beyond the line-oriented command language, cow understands script { … } blocks: small embedded scripts that can iterate collections, filter them, branch on conditions, and invoke any cow command on the results. A script block is itself a cow command, so it can appear anywhere a command is accepted: the top-level configuration file, or the body of a rule.

Statements

A block contains zero or more statements; whitespace (including newlines) separates them.

let NAME = EXPR

Bind NAME to the value of EXPR in the current scope. Names are visible to subsequent statements in the same block and to nested blocks.

for NAME in (SOURCE [where EXPR]) { … }

Snapshot SOURCE at loop entry and execute the body once per matching element with NAME bound to the current record. SOURCE is one of windows, containers, panes, desks, outputs (or screens), rules, or selections. An optional where clause filters the snapshot with the supplied expression (evaluated per element with NAME in scope). The body sees a fresh sub-scope; let bindings inside the body don’t leak out.

if EXPR { … } [else { … } | else if … ]

Branch on the truthiness of EXPR (numbers are false when zero; strings are false when empty; lists are false when empty; records are always true; nil and false are false).

break

Exit the nearest enclosing for loop.

continue

Skip to the next iteration of the nearest enclosing for loop.

COMMAND [ARGS …]

Invoke any registered cow command. Each argument is either a raw text chunk (passed verbatim) or an interpolation of the form #{EXPR} which is replaced with the stringified result before the command runs. Commands run in order, and later statements see changes made by earlier commands.

Single quotes group literal command arguments; double quotes also allow interpolation. For example: focus -n -F ‘self.current_output’.

Expressions

The expression language has no arithmetic; it covers what’s needed to choose work to do.

Form Meaning
N, N.N numeric literal (double-precision)
“…” string literal (escapes: \n, \t, \r, \", \\)
true, false boolean literal
nil absence of a value; missing record fields also evaluate to nil
[A, B, …] list literal, commonly used with in and not in
NAME variable in the current or enclosing scope
NAME.FIELD.FIELD record field access (chained for nested records)
A == B comparison (also !=, <, <=, >, >=); cross-type compares are evaluation errors; ordering on booleans is an evaluation error; nil supports only == and !=
A in [B, C], A not in [B, C] membership tests; list elements compare with exact scalar equality
A matches PATTERN, A not matches PATTERN match a string against a shell pattern using fnmatch(3); matching is case-sensitive
not A unary negation
A and B, A or B boolean operators (short-circuit)
(EXPR) grouping

For example, match every named cowiconman instance on a particular output:

if self.title matches "cowiconman:*" and self.output == "eDP-1" {
    # commands
}

Sources

Iterating over windows uses the insertion order. It is possible to use a MRU (Most Recently Used) order, with order mru. For example@

menu-clear RecentWindows
script {
    for w in (windows where w.current_desk order mru) {
        menu-add RecentWindows "#{w.title}" "focus -t '#{w.id}' -f"
    }
}

Looping over windows with MRU does not modify the window ordering.

order mru applies only to windows.

Each iterable source yields records whose fields match the corresponding show -a topic JSON. Common window fields include id, app_id, title, desk, output, layer, focused, iconified, keyboard_layout, circulate_skip, selected, selections, maximized, width, height, page_col, page_row, current_page, current_desk, current_output, sticky_desk, sticky_page, border_width, title_thickness, squeeze, and squeezed_rect. Common desk fields include nr, name, output, current, active, collected, window_count, page_col, page_row, page_cols, page_rows, page_vx, and page_vy. Common output fields include name, number, geometry, usable geometry, current, current_desk, current_desk_name, and page state. Common rule fields include name, type, global, output, desk, match, and body. Selection records contain the unique selection name and its window count.

For window records, current_page is relative to that window’s output. Use current_output and current_page when matching windows only on the current output’s current page.

Inside a rule body, the identifier self resolves to a record for the event’s target view; outside a rule body it is unbound.

Examples

Tint titlebars of windows promoted to the top layer:

decor -d top-tint titlebar.active_colour 0xC04030
decor -d normal-tint titlebar.active_colour 0x303030
rule -g -n top-tint -T on-layer-change script {
    if self.layer == 16 {
        decor -a top-tint
    } else {
        decor -a normal-tint
    }
}

Move every mpv window to the top layer:

script {
    for w in (windows where w.app_id == "mpv") {
        winops -t #{w.id} -L =top
    }
}

Iconify every window not on the currently-focused desk:

script {
    for w in (windows where w.focused != true and w.iconified != true) {
        winops -t #{w.id} -i
    }
}

When cow starts, for the output (screen) HDMI-A-1, set every desk to start on page 1,1:

rule -g -T on-start -n startup-pages script {
    let output = "HDMI-A-1"
    for d in (desks where d.output == output) {
        desk -t '#{output}' -d #{d.nr}
        scroll -t '#{output}' -a 1,1
    }
}

Start and position a separate cowpager for every output. The pager configuration used here should contain top-level settings rather than show pager instances, so -m determines each window’s title:

rule -g -T on-start -n output-pagers script {
    for o in (outputs) {
        style -g -n pager-#{o.name} -s /cowpager:#{o.name}/ {
            placement.output #{o.name}
            placement.anchor top-right
        }
        exec cowpager -f ~/.config/cow/cowpager-single.conf -m #{o.name}
    }
}

If that configuration selects a named pager such as fvwm, include it in the selector instead:

-s /cowpager:fvwm:#{o.name}/

DESKTOP BEHAVIOUR

Desktops can be configured either independently of each output, or desktops are the same across all outputs. See the deskstop_configuration setting.

CONFIGURATION

CoW first loads /share/cow/cow-defaults.conf, then your cow.conf. The installed defaults provide decorations, container controls, bindings and menus, but do not start applications. Package upgrades update this baseline; put your changes in cow.conf, not the installed defaults file.

The defaults use a teal gradient theme. Setting a solid menu or decoration colour replaces its inherited default gradient. Explicitly configured gradients are retained, so their colours can still have a separate fallback. Changing a titlebar colour also updates inherited tab colours in that decor profile; explicit tab settings are left alone.

Settings and matching bindings in your file override the defaults. Use bind -d or mouse -d to remove unwanted bindings. The first menu-add for a shipped menu replaces its default entries; subsequent additions append normally. menu-style alone leaves the entries intact. Use menu-clear or menu-destroy to remove them explicitly.

quit -r reloads both layers, even without a user file. -c selects the user layer, while -n skips the installed defaults. If the defaults file is missing, CoW warns and continues with built-in fallback values.

Inspect the defaults and override just what you need:

moocow show -D -f config config | less
moocow show config step
moocow show -a -f config binding

For example, a user cow.conf can contain:

set step 40
alias terminal exec foot
decor -d default titlebar.font 'sans-serif Bold 12'
decor -a default

Search Order

Unless overridden with -c or $COW_CONFIG, the configuration file is located by searching the following paths in order, stopping at the first that exists:

  1. $COW_CONFIG

  2. ~/cow.conf

  3. $XDG_CONFIG_HOME/cow/cow.conf

  4. ~/.config/cow/cow.conf

  5. /<PREFIX>/etc/cow/cow.conf

File Format

The configuration file is plain text. Each line is a cow command, using the Lines beginning with # are comments. A backslash (\) at the end of a line continues the command on the next line.

The include command reads additional commands from another file. Absolute paths are used as-is, paths beginning with ~/ are resolved relative to $HOME, and other relative paths are resolved relative to $XDG_RUNTIME_DIR.

Example:

# This is a comment which cow ignores.
decor -d default border.width 10
decor -d default border.style fvwm
decor -a default

# This command is split over multiple lines for clarity.
bind \
    L+Return \
    exec foot

COMMAND REFERENCING

Commands in cow can operate on outputs, desktops, pages, and windows. The commands in cow are therefore pre-disposed to understand that they need one or more of those contexts to operate on.

In some cases that’s automatic (such as operating on the focused window, or the current output, etc.) but commands can be told to override that.

Source and Target Definitions

Most commands have a target on which the command is going to operate on, and for consistency across those commands, that is the -t flag. The same concept exists for for a source window, via -s.

The format for both is the following:

output:desk.window

That is to say, there’s three components – the delimiters between the parts are literal and must be specified:

output

Output name, @N by output number, @next, or @prev.

desk

Desk number (0-9), @next, or @prev.

window

One of:

  • %app_id - match by app_id

  • /substring/ - match by title substring

  • #id - match by River identifier

  • @focused - the currently focused window (default)

  • @next - next window in stacking order

  • @prev - previous window in stacking order

In the case of using a substring, this is run through fnmatch(3).

COMMAND CHAINING

With any given command, it’s possible to chain them together. For example:

exec xterm \; exec notify-send "xterm executed"

The command divider is \; and must be escaped in this way so as not to incur any shell interpretation.

Also, line continuation between changed commands is also possible, and looks like this:

rule -g -T on-restart -n cleanup \
    exec killall waybar \; \
    exec killall conky \; \
    exec killall pasystray

COMMAND OUTPUT

Certain commands in cow are designed to return information. In such cases, the output is in JSON format.

This may be subject to change in the future.

GLOBAL COMMANDS

The following details those commands which operate globally within cow. Note that the same command might also contain window/output settings, which will be also be added under the relevant section in this man page.

background [-t output[:desk]] [-P col,row] colour|image VALUE|inherit

Set a background colour or image globally, for an output or desk, or for a page. Without -t, the setting applies to every output and desk. Use :desk for a desk on all outputs. Output names and @N are accepted, including when the configuration is read before outputs exist.

-P behaves like page option. Within the same scope, the last setting wins. inherit removes the property at that scope. Colours and images inherit settings.

Images may be PNG, SVG or XPM and tile over the page colour. Relative paths are resolved from the configuration file. image none disables an inherited image; image inherit restores it. Decoded images are cached during scrolling.

background colour 0x303030
background -t @1:0 colour 0x304050
background -t @1:0 -P 1,0 colour 0x503040
background -t @1:0 -P 1,0 colour inherit
background -t @1:0 -P 0,0 image textures/cork.xpm
background -t @1:0 -P 1,0 image none

The old set output.colour.background and set output.image.background settings are deprecated. CoW logs a warning and translates them to background colour and background image. An output suffix becomes -t output; for example, set output.colour.background.DP-1 0x303030 becomes background -t DP-1 colour 0x303030.

Deprecated commands still run, but new configurations should use the replacement shown in the warning. Repeated uses of the same deprecated form at the same location warn only once per loaded configuration. Executed commands are saved in their new form by show config -f config. Stored script and binding bodies retain their original text.

alias [-p PARAM,…] name command … | alias -d name

Define name as an alias for a CoW command. An alias can be used anywhere a built-in command is accepted, including directly through moocow(1), in a binding, menu, rule, or script block. It inherits the window, desk, and output context of its caller. Without -p, it takes no arguments.

Defining an existing alias replaces its command. Existing bindings and menus resolve aliases when invoked, so they use the replacement without being redefined. Built-in command names and scripting-language keywords cannot be used as alias names. Define an alias before referring to it from a rule or script body. Recursive aliases are stopped with an error.

-d

Delete name.

-p PARAM,…

Define named parameters for a script { … } body. Supply a comma-separated list of distinct variable names, without spaces. Names cannot be commands, aliases, DSL keywords, or the alias’s own name. Calls must supply exactly one argument per parameter. Values are strings use #{PARAM} to pass them to commands. Quoted spaces, semicolons and braces remain argument data, not DSL code. Ordinary shell commands invoked with exec retain their shell semantics.

Parameterized aliases serve as reusable script functions. They do not capture variables from their caller or return expression values. show alias NAME includes the parameter list, and configuration export preserves it. Redefining an alias without -p removes its parameters.

Examples:

alias terminal exec foot
alias -p output move-to script {
    window-move -o #{output}
}
bind L+F2 move-to HDMI-A-1
# Or: moocow move-to HDMI-A-1

alias tidy-desktop script {
    winops -F 'self.current_desk and self.iconified' -i
    focus -n -w
}

bind L+Return terminal
menu-add RootMenu "Tidy desktop" tidy-desktop

Aliases are included in show config. Use show alias name or show -a alias to inspect active definitions.

bind [-r] [-x release-action] [contexts:]modifier+key[,modifier+key…] action … | bind -d pattern

Key bindings are specified as a modifier string immediately followed by + and a keysym name, or - and a numeric keycode:

bind MODIFIERS+keysym  command
bind MODIFIERS-keycode command
bind CONTEXTS:MODIFIERS+keysym command
bind MODIFIERS+keysym,MODIFIERS+keysym command

Without a context prefix the binding is global. A context-qualified binding is enabled only while the pointer is in one of its regions. If the same key has both global and matching contextual bindings, the contextual binding wins; the global binding is the fallback.

For overlapping contextual bindings, the most specific context wins.

Context characters:

  • A: any context (equivalent to omitting the prefix).

  • R: output background (root).

  • W: application window content.

  • I: an iconified-window icon.

  • 0 to 9: the corresponding titlebar button.

Multiple characters combine regions. The context window becomes the command’s default target, making window commands act on the window under that region rather than whichever window was previously focused.

-r

Mark the binding as repeatable. While the triggering key is held the action re-fires every repeat_interval_ms milliseconds after an initial repeat_delay_ms dwell (see set).

-d pattern

Remove configured keyboard bindings whose complete key specification matches the shell-style pattern. Quote patterns in configuration files, for example bind -d “L+[0-9]”. This is useful before regenerating a group of bindings from a script.

-x action

Perform this action after the key and every modifier named by the binding have been released. The definition still requires a key-press action (even if nop). This allows a menu opened by A+Tab, for example, to remain open through repeated Tab presses and be destroyed when Alt is released:

bind -x “menu-destroy AllWindows” A+Tab menu-show AllWindows

All bind options must appear before the key specification. On versions of the River XKB bindings protocol before version 3, modifier release cannot be observed, so the action instead runs when the main key is released.

Modifier characters:

Character Modifier
L Logo (Super / Windows key).
S Shift.
C Control.
A / M Alt (Mod1). M (for meta) is a synonym.
5 AltGr (Mod5).

Modifier letters are case-insensitive and combined by concatenation: LS means Logo+Shift, LCA means Logo+Control+Alt, and CM means Control+Meta. Use 0 to specify no modifier.

Key symbols and codes can be determined with wev(1).

Separate keys with commas to define a chorded binding. Every key except the last is a prefix: pressing it temporarily replaces the normal key bindings with the bindings below that prefix. A matching key continues the sequence; the final key executes the command and restores the normal bindings. An unmatched non-modifier key, including Escape unless explicitly bound, is consumed and cancels the sequence.

Prefixes are shared, so several bindings may begin with the same keys:

bind C+c,C+a window-move -x 0 -y 0
bind C+c,C+i winops -i
bind C+c,C+c winops -c
bind C+x,0+4,C+f window-maximize

Only the first key may have a binding context. Its context window remains the default command target throughout the sequence. A key cannot be both a complete binding and a prefix. The -r and -x options apply to the final key. Chorded bindings require version 2 or newer of River’s XKB bindings protocol.

Examples:

bind L+Return     exec xterm
bind LS+a         winops -c
bind LCA+q        quit
bind 0+F1         action-help
bind -r L+Right   scroll -n           # hold Super+Right to keep paging
bind R:L+Return   exec xterm          # only over the background
bind I:0+Return   winops -i           # restore the icon under the pointer
bind 0:L+Return   window-maximize     # over titlebar button 0

mouse [contexts:]key action …

mouse -d [contexts:]key

With -d, remove the binding with that exact key specification. This can remove a mouse binding inherited from the installed defaults.

Mouse bindings use the same modifier format as the bind command. The button name replaces the keysym:

mouse MODIFIERS+button command
mouse CONTEXTS:MODIFIERS+button command

Button names: left, right, middle, side, extra, forward, back, task. Button names are case-insensitive.

Bind a pointer button combination to a command. The key format is the same as for bind, but the key name is a button name (see MOUSE BINDINGS). The optional contexts and their precedence are the same as for bind. Contextual commands target the window under the pointer. A matching contextual mouse binding consumes the click before normal pointer interaction, so it overrides a bind-decoration action or titlebar-button action in that region. Without a matching binding, the normal decoration or client click behaviour is unchanged.

In addition to the contexts accepted by bind, mouse bindings accept these non-button SSD decoration contexts:

  • T: window titlebar, excluding its buttons.

  • [, ], -, : left, right, top, or bottom frame border.

  • S: any frame side; equivalent to []-.

  • <, ^, >, v: northwest, northeast, southeast, or southwest frame corner.

  • F: any frame corner; equivalent to <^>v.

  • R: “root window” (output background)

Multiple characters combine regions, so TF: applies to the titlebar or any frame corner.

Example:

mouse L+left   window-move
mouse L+right  resize
mouse L+middle window-move -C
mouse T:0+left window-move
mouse <:0+left window-resize -e nw
mouse ]:0+left window-resize -e e
mouse I:0+left winops -i
mouse R:0+left menu-show RootMenu

The R context applies to cow’s output background surface, even when no background colour or image is configured, and does not capture clicks on client windows. It supports the same buttons and modifiers as other mouse contexts and is useful for root-window style menus. See the MENU section for more information.

bind-decoration [-2] region command …

Bind a primary-button click on a window’s decoration to a command. Each window’s frame is divided into regions. See the ANATOMY_OF_A_WINDOW section.

When a bound region of the window is clicked on, the attached action runs against that window.

-2

Double-click binding (default is single-click). A region can carry one single-click binding and one double-click binding simultaneously.

The region argument is case-insensitive and accepts both the compass shorthands and the long form:

Region Aliases
N north
S south
E east
W west
NE north-east
NW north-west
SE south-east
SW south-west
titlebar title
0 to 9 titlebar buttons 0 to 9
pane:0 to pane:9 inner pane titlebar buttons
pane-border any edge or corner of a split pane
pane-titlebar inner pane titlebar (excluding buttons)

A pane-border binding takes precedence over the corresponding edge or corner binding for the same click count. It does not match the outer container frame or ordinary windows. Without one, normal edge bindings apply. On a shared divider, a double-click targets the adjoining pane with a hidden title if the pane drawing the divider has its title visible.

Pane titlebar and button bindings are separate from ordinary titlebar and button bindings, with no fallback between them. Outer container frames and plain tab groups use ordinary bindings; tabs within split panes use pane-titlebar and pane: bindings. Single- and double-click actions are independent in each set.

The default bindings are:

Region Clicks Default action
N / S / E / W single window-resize -e dir (interactive one-axis resize)
NE / NW / SE / SW single window-resize -e dir (interactive diagonal resize)
titlebar single window-move (interactive title-drag move)
titlebar double window-shade -d titlebar (shade / unshade toward the titlebar)
pane-titlebar single window-move (rearrange panes or detach outside the container)
pane-titlebar double window-container -c (collapse / restore a row)
0 single window-maximize -T (toggle vertical maximisation)
1 single winops -c (close)
2 single winops -i (iconify)

The installed defaults restore a hidden pane title on a pane-border double-click. Other edges and corners have no double-click defaults.

If a button has both single- and double-click bindings, CoW waits until the double-click interval has elapsed before running the single-click action. A second click on the same button runs only the double-click action. Other decoration regions retain immediate press-and-hold handling for interactive move and resize actions.

Limitation: River does not expose which button was used when clicking on the window decoration, so currently, only mouse button 1 is usable.

Examples:

# Maximise a window by clicking anywhere on its titlebar
# (replaces the default window-move).
bind-decoration titlebar window-maximize -m

# Re-express the default double-click-shade explicitly.
bind-decoration -2 titlebar window-shade -d titlebar

# Maximise on a click on the north edge.
bind-decoration N window-maximize -m

# Double-click NE corner closes the window (the single-click default
# of starting a corner resize still fires on the first click; bind
# the single-click pair too if you want to suppress it).
bind-decoration -2 NE    winops -c

# Button 0 toggles vertical maximisation on one click and full
# maximisation on two clicks.
bind-decoration 0       window-maximize -T
bind-decoration -2 0    window-maximize -m

# Chain: start a resize then focus the next window.
bind-decoration SE       window-resize -e se ; focus -n

rule -d -n <name> | -g | -t output[:desk] -n <name> -T <event> [-s sel] cmd…

Apply rules (groups of commands) to certain conditions (events).

A rule has a name (via -n) which must be specified. Note that rules are globally stored, so any conflicting names will mean that rule will be rejected. It also has a type (via -T) which specifies when to run the rule. Multiple rules can be defined against the same type as long as their names are distinct.

The following table shows which events are available:

Event Description
on-map Fired when a window appears on the screen.
on-move Fired when a window is moved.
on-resize Fired when a window is resized.
on-shade Fired when a window is shaded.
on-unshade Fired when a window is unshaded.
on-fullscreen Fired when a window enters fullscreen mode.
on-unfullscreen Fired when a window leaves fullscreen mode.
on-iconify Fired when a window is iconified.
on-deiconify Fired when a window is deiconified.
on-layer-change Fired when a window’s layer changes (see LAYERS). Fires per-window for container members. No-op assignments do not fire.
on-focus-change Fired when the focused window changes.
on-new-desk Fired a new desk is switched to.
on-new-page Fired when a new page is switched to.
on-start Fired once after cow has completed startup. Use with -g.
on-restart Fired when cow is restarted (can also happen via -USR1 signal).
on-title-change Fired whenever a window’s title changes to something different to what it used to be.
on-outputs-changed Fired when the set of available outputs changes – a monitor is plugged in or unplugged, or every output is destroyed and recreated. Fires once per change. Useful to re-apply a monitor layouts.
on-desks-changed Fired after set desks.count successfully resizes the live desk sets. Fires once per command. Use with -g to rebuild menus, bindings, or other configuration derived from the available desks.
on-desk Used to change window styles on a particular desk.
on-edgedrop-DIR Fired when a window is held at an output edge, then dropped. DIR is n, s, e, or w.

Focus changes

on-focus-change supplies previous and current, containing window IDs or JSON null when no window was focused. Re-focusing the same window and opening a CoW menu do not fire this rule.

The target (self), selectors and output/desk scope refer to the newly focused window.

rule -g -T on-focus-change -n focus-log exec sh -c 'echo "$COW_EVENT"'

Scripts can be used. For example, tint the focused window without changing other decoration settings:

decor -d focus-tint titlebar.active_colour 0x406080
rule -g -T on-focus-change -n focus-tint script {
    if event.current {
        decor -a focus-tint
    }
}

Edge drops

While dragging a window, hold the pointer at an output edge until it highlights, then release to run the rule. Move away to cancel.

A matching drop rule replaces drag-to-page scrolling on that edge.

The rule acts on the dragged window. Its output and desk scope follow the pointer, which may be on a different output. To move the window onto DP-1 before maximising it:

rule -t DP-1 -T on-edgedrop-n -n maximise-on-dp1 script {
    window-move -o DP-1
    window-maximize
}

External commands receive the output and edge in COW_EVENT, for example {"output":"DP-1","edge":"n"}.

Specificity of rules are such that global rules (via -g) run before any specific defined rules. For example:

# Define a global rule -- this will always run.
rule -g -Ton-map -ng-on-map exec notify-send "A window has appeared."

# Define a rule to change a window property when windows appears on desk 2
decor -d desk2 titlebar.active_colour 0xFF8C00
decor -d desk2 titlebar.inactive_colour 0xB28E4C
decor -d desk2 border.inactive 0xB28E4C
rule -t :2 -Ton-map -ndesk2 decor -a desk2

Fullscreen transitions can change global settings or run more selective rules. For example, this disables edge scrolling while a window is fullscreen and restores it afterwards:

rule -g -T on-fullscreen -n fullscreen-edge-scroll-off \
    set edge_scroll false
rule -g -T on-unfullscreen -n fullscreen-edge-scroll-on \
    set edge_scroll true

The setting is global. The example therefore assumes only one fullscreen window at a time. A selector or output scope can be added when the policy should apply only to particular windows or outputs.

Rules can be deleted with the -d option. Additionally, -n is needed to specify the name, example:

rule -d -ndesk2

Defined rules can be seen with the show command.

style -d -n <name> | [-r] -g | -t output[:desk] -n <name> [-s sel]

Define states for windows. Styles reuse the same names, scopes, and selectors as rules, but describe window state rather than executing commands in response to an event. Styles can override one another, the last match wins, with theat style overriding any previous properties within it/

The selector forms are the same as for rule: %app-id, /title/, and #identifier. A global style uses -g. With -t output[:desk], either part may be omitted to match any output or desk.

Use -r to replace an existing named style, keeping its position in the style order, or create it if absent. The replacement is validated before the old style is removed. Without -r, duplicate names are an error.

The following properties are reapplied when a window’s title or scope changes and when the configuration is reloaded:

Property Value
layer An absolute layer number, top, or bottom.
circulate.skip A boolean controlling whether ordinary next/previous focus skips the window.
snap.target Whether other windows snap to this window’s edges when moved or resized. Defaults to true.
sticky.desk, sticky.page Booleans controlling desk and page stickiness.
decor name Overlay a named sparse decoration profile.
decor { … } Overlay decoration keys local to this style. The keys and values are the same as for decor -d.

These placement and state properties apply only when the window is first managed. They are not replayed by a configuration reload or title change:

Property Value
placement.output An output name or selector such as @2.
placement.desk A desk number or name.
placement.page An absolute column,row page.
placement.x, placement.y Initial position, using the units accepted by window-move.
placement.width, placement.height Initial size, using the units accepted by window-resize.
placement.anchor Align the outer frame within the usable area after applying its initial size. One of top-left, top, top-right, left, centre, right, bottom-left, bottom, or bottom-right. Do not combine this with placement.x or placement.y. The alignment follows later changes to the output’s usable area, such as a layer-shell panel appearing, and remains attached to its selected output across output reconfiguration. An explicit window-move releases the window from this alignment.
state.maximized none, horizontal, vertical, or both.
state.maximized.ignore_reserved Whether initial maximisation ignores reserved areas.
state.fullscreen, state.shaded, state.iconified Initial boolean window states.

Styles are resolved before an event rule is executed. If a rule or later targeted command changes a live style field, that field becomes an explicit per-window override and is not subsequently replaced by style recomputation.

For example, give terminals a reusable base theme with a local border change, keep them in the normal layer, and initially place them on the left half of the work desk:

style -g -n terminal -s %foot {
    decor motif
    decor {
        border.width 4
        titlebar.active_colour 0x303030
    }
    layer 4
    circulate.skip false
    placement.desk work
    placement.x 0vw
    placement.y 0vh
    placement.width 50vw
    placement.height 100vh
}

To stop other windows snapping to a decorative window, without changing its own snapping or focus behaviour:

style -g -n teddy-no-snap -s %wteddy {
    snap.target false
}

Window queries and DSL window records expose this as snap_target. Output boundaries remain snap targets regardless of this setting.

Squeezed titlebars also provide snap targets at their ends and along the exposed frame edge. These extra targets disappear when the titlebar fills the edge or the window is shaded.

An event rule can then make a particular terminal an explicit exception:

rule -g -T on-map -n build-log -s '/build log/' script {
    winops -L =top
    decor -a alert
}

Delete a style with style -d -n name.

Styles can be inspected with the show style name and show -a style commands.

exec command …

Execute a shell command. During configuration loading, exec commands are deferred until cow has fully connected to River.

draw-exec [-F expr] command …

Start an interactive pointer drag. On button release, run command and apply the drawn frame to the first newly mapped window which matches expr. Without -F, the first newly mapped window consumes the frame.

The command is parsed as a CoW command, so launching an application usually uses exec:

mouse M+left draw-exec exec foot
mouse M+middle draw-exec -F 'self.app_id == "foot"' exec foot

If the command is a brace-delimited script block, the surrounding braces are removed before the block is evaluated.

show [-D] [-f json|config] [-t target] topic [name]

show -a [-f json|config] [-F expr] topic

Query window manager state and configured objects. Without -a, a topic returns one current or named record. With -a, it returns an array containing all records for that topic. When used via moocow(1), JSON is printed to standard output.

Place options before the topic.

-a

Return all records for the topic. This is supported by window, output, desk, rule, style, decor, menu, binding, and command, selection, alias, container, and pane. It cannot be combined with -t or a resource name.

-f format

Select the output format. json is the default. config emits commands accepted by cow.conf and is available for config, rule, style, decor, menu, binding, and alias. With -a, filters are applied before the collection is serialized.

-t target

Select the resource returned by show window, show output, or show desk. It uses the normal command context syntax described in COMMAND REFERENCING. Without -t, the focused window, current output, or current desk is used respectively. For show container and show pane, select a member window with -t, or omit it to use the focused window.

-F expr

Filter a topic selected with -a using a DSL expression. self is bound to each candidate record. A query with no matches returns an empty array. -F requires -a.

Topics:

show container, show -a container

Return the target window’s container or all containers. A target outside a container produces an error. Records contain id, name, activewindowid, pane_count, window_count, persistent, split, shaded, iconified, output, output_number, desk, and frame (x, y, width, height).

show pane, show -a pane

Return the target window’s pane or all container panes. Records contain id, name, container_id, activewindowid, tab_count, collapsed, empty, title_hidden, title_visible, output, output_number, desk, and frame. Each pane appears once, regardless of its number of tabs.

title_hidden is the requested hiding preference; title_visible is the effective policy. A protected handle or multi-tab pane can have both set. Shading a split container does not change the reported layout frames. Collapsed pane frames describe their allocated titlebar area. Coordinates are global screen coordinates. Container and pane IDs are session-local.

The same records are available in the DSL as containers and panes. Join them to windows using container_id and pane_id. These loops do not change the command target implicitly: use activewindowid with -t or focus -i. As with other collections, each loop snapshots its source at entry.

Iconify each container on the focused window’s output once:

script {
    for c in (containers where c.output == self.output) {
        winops -t "##{c.active_window_id}" -i
    }
}

Build a menu with one entry per pane in the focused container:

script {
    menu-clear Panes
    for p in (panes where p.container_id == self.container_id) {
        for w in (windows where w.id == p.active_window_id) {
            menu-add Panes "#{w.title} (#{p.tab_count} tabs)" "focus -i #{w.id}"
        }
    }
    menu-show Panes
}
show window, show -a window

Return the target managed window or all managed windows. Window records include identity, content and frame geometry, icon geometry, placement, page state, stacking state, window flags, decorations, size hints, and per-window style fields.

Examples: show window show -t ‘#0123abcd’ window show -a -F ‘self.current_page and not self.iconified’ window

show output, show -a output

Return the target output or all outputs. Output records contain name, number, geometry, usable geometry, current desk, and page state.

Examples: show -t @2 output show -a -F ‘self.current’ output

show desk, show -a desk

Return the target desk or all desks on every output. Desk records contain their number, name, output, page state, collection state, and window count.

Examples: show -t HDMI-A-1:2 desk show -a -F ‘self.output == “HDMI-A-1”’ desk

show rule name, show -a rule

Return one exactly named rule or all rules. Rules can be filtered by fields such as name, type, global, output, desk, and match.

Example: show -a -F ‘self.type == “on-map”’ rule

show style name, show -a style

Return one exactly named style or all styles. Records contain the shared rule-style scope and selector fields plus the style body.

Examples: show style terminal show -a -F ‘self.global’ style show -a -f config style

show decor name, show -a decor

Return one exactly named decoration profile or every profile. Each profile contains its name, whether it is globally active, and its sparse settings object. The collection may be filtered with -F.

Examples: show -a decor show decor fvwm show -a -F ‘self.active’ decor

show menu name, show -a menu

Return one named menu or every menu definition. Records contain the selection modes, resolved style, and ordered item definitions.

Examples: show menu RootMenu show -a menu

show -a binding

Return every keyboard, mouse, and decoration binding. Bindings have no singular selector. The type field is keyboard, mouse, or decoration; -F can select one kind.

Example: show -a -F ‘self.type == “keyboard”’ binding

show -a selection

Return every non-empty named window selection. Records contain its unique name and window count.

Example: show -a -F ‘self.count > 1’ selection

show alias name, show -a alias

Return one alias or every alias with its name and expanded command.

show command name, show -a command

Return one named command or every registered command with its name and usage. This replaces the former list-commands command and supports filtering with -a -F.

show keyboard-layout

Return the active keyboard layout’s name, caps_lock, num_lock, and scroll_lock states, or null if no layout is set. A lock state is null until River has reported it.

show config [key]

With no key, return all serializable configuration grouped into commands, settings, decor, menu_style, menus, bindings, rules, styles, and aliases. The commands array is the ordered, directly reusable configuration record. With a key, return only that setting. Supported setting keys: border.width, step, border.corner_length, border.style, focus, geometry_window, clamp_mode, placement, page and edge-scroll settings, titlebar.font, title_format, cursor and repeat settings, keyboard_layout.per_window, default_layer, max_layer, preserve_desk_on_output_loss, and desks.count. With desks.count, -t may select the output to inspect.

Examples: show config show config border.width show -t @2 config desks.count show -f config config show -a -f config binding

The config form of show config is built from the successful persistent commands seen while loading the configuration and from later configuration changes made through moocow(1). This includes settings, decorations, bindings, menus, rules, icon placement and startup exec commands without relying on a separate list of setting names. Per-window commands fired by rules are session state and are not included.

moocow(1) prints this form without a JSON envelope, so it can be written directly:

moocow show -f config config > cow.generated.conf

show -D config

Inspect the installed defaults without executing them. JSON contains path, enabled (whether loading defaults is enabled), and config (the file text). With -f config, print the file directly, including comments. This shows the installed baseline, not user overrides or built-in fallback values, and also works when CoW was started with -n.

quit [-r]

Without any options, cow quits. With -r, reload the configuration from the file cow was started with, instead of quitting.

Keybindings and window decorations are updated immediately on reload.

include _file_

Read and execute commands from file. Absolute paths are used as-is, ~/ is resolved relative to $HOME, and other relative paths are resolved relative to $XDG_RUNTIME_DIR.

keyboard-layout [NAME]

Set the active XKB layout on every keyboard. NAME must match a descriptive layout name in the keyboard’s current keymap. River ignores a name which does not exist. Without NAME, display a numbered typeahead menu of layout names observed by CoW during this session. River reports layout changes but does not expose the complete list of names in a keymap, so layouts appear in the chooser after they have been active at least once.

keyboard-layout 'English (UK)'

When keyboard_layout.per_window is enabled, running this command while a window is focused also records NAME for that window. The layout can instead be changed using the keyboard’s normal XKB layout-switching keys; CoW observes the change and records it in the same way. It restores the remembered layout whenever that window receives focus again.

Layouts are remembered per window, not per physical keyboard. The command currently sends NAME to every keyboard exposed by River; independently selecting a different layout for each keyboard is not supported.

send-key [MODIFIERS+]KEY

Send a key press and release to the focused application. The options are the same as the bind command.

For example, to clear the terminal:

focus -t %foot
send-key C+l

For XWayland applications, synthetic events are often disabled due to security concerns. So this might not work unless the application can be told to accept them. XTerm, for example, does allow this via its allowSendEvents resource setting.

nop

Do nothing.

decor -d name key value

Define or replace one key in a named, sparse decoration profile. The key is a decoration key without the set prefix.

decor -d alert border.width 8
decor -d alert titlebar.active_colour 0xC04030
decor -d alert border.inactive 0x303030

Theme packs can be split into files and loaded with include, for example:

include themes/fvwm
decor -a fvwm

Titlebar buttons may set their face and vector foreground colours separately for focused and unfocused windows:

decor -d motif titlebar.button.0.active_colour 0xC0C0C0
decor -d motif titlebar.button.0.inactive_colour 0xC0C0C0
decor -d motif titlebar.button.0.active_fg 0x000000
decor -d motif titlebar.button.0.inactive_fg 0xFFFFFF

The foreground properties affect native vector buttons. Image buttons render the colours contained in the image itself.

Image values in a profile may name PNG, SVG, or XPM files. Relative image paths are resolved against the file containing the decor -d command, so a theme directory can keep its config and image assets together.

XPM support covers the XPM3 format, including transparent None pixels, hexadecimal colours, the basic named colours, and gray0 through gray100 (also spelled grey).

decor -a name [-t target]

Apply a named decoration profile. Switching to a different global profile, or applying a profile to an explicit target, resets the target decoration to the base defaults before applying the profile entries. Reapplying the active global profile patches the current decoration in place. Profiles containing only icon.* keys also patch in place. Profiles applied by rules are overlays, so layered rules can compose sparse profiles.

With -t, the target is that window. Without -t, the target is the global decoration defaults and existing windows. Rule bodies use the rule’s default window target.

set [-v] key value

Set a configuration option at runtime. -v prints the resulting value.

The specific keys/values for this command are detailed under relevant section in this man page.

However, there are global settings which don’t fit into other command settings. These are listed below:

Key Description
cursor.theme name Set the XCursor theme. CoW applies the theme immediately. The XCURSOR_THEME environment variable supplies the initial value; if it is unset, the default is default. Already-running native Wayland clients which draw their own cursor may not update it.
cursor.size pixels Set the XCursor size from 1 to 1024 pixels. The initial value comes from XCURSOR_SIZE, or defaults to 24.
cursor.hide_timeout milliseconds Hide the pointer after the given period without input. A value of 0, disables pointer hiding. Default value is 0.
page_visibility origin|overlap Control window placement on page boundary when edge scrolling is enabled. origin assigns the window to the page containing its content origin; this is the default. overlap renders the window on every page intersecting its outer frame.

WINDOW COMMANDS

The following details commands which operate on windows directly.

winops [-cilrs] [-C on|off|toggle] [-L layer] [-t target] [-F expr]

Operate on a window.

-t target

Target window (see COMMAND REFERENCING). Defaults to the focused window. It cannot be combined with -F.

-F expr

Apply the operations to every window matching the DSL expression instead of a single target. self is bound to each candidate window.

-c

Close the window.

-r

Raise the window to the top of the stacking order within its layer. See LAYERS below for the cross-layer invariant.

-l

Lower the window to the bottom of the stacking order within its layer.

-s

Toggle desk-sticky: window appears on all desks.

-S

Toggle page-sticky: window appears on all pages of the current desk. When enabled for a window on another page, the window is first moved to the current page while retaining its within-page position. Combine -s and -S to toggle full stickiness: enable both unless both are already enabled, in which case disable both.

-i

Toggle the window’s iconified (minimised) state. Click an iconified window to restore it; drag it to remember a manual icon position for later iconify operations.

-C on|off|toggle

Set, clear, or toggle CirculateSkip. Skipped windows are ignored by focus -n, focus -p, @next, and @prev; explicit focus and mouse focus still work.

CirculateSkip is window state. Once set, it applies to ordinary next/previous circulation regardless of which key binding or command started the focus change. This is useful for panels, pagers, docks, and other utility windows which should generally remain reachable by direct focus, but not appear in normal circulation.

-L layer

Set the window’s layer (see LAYERS). Accepted forms:

  • N – absolute layer (clamped to [0, max_layer]).

  • =N – absolute layer, explicit (never toggles).

  • +N / -N – relative bump, clamped at edges.

  • top – shorthand for max_layer; toggles back to default_layer when the window is already at max_layer.

  • bottom – shorthand for 0; toggles back to default_layer when the window is already at 0.

  • =top / =bottom – non-toggling variants, useful inside a rule command.

Container members share a single layer: setting -L on any member updates every member of the container.

For example, iconify the default selection or make every member of a named selection page-sticky:

winops -F 'self.selected' -i
winops -F '"work" in self.selections' -S

window-move [-C] [-G [selection]] [-w] [-t target] [-d dir [-n]] [-o output] [-a anchor] [-c] [-k 0-9] [-x X] [-y Y] [-P col[,row]]

Move a window. With no flags, begin an interactive move using the mouse.

-t target

Target window.

-d dir

Move one step in direction dir (up, down, left, right).

-n

With -d, snap to the nearest edge instead of stepping. It may be combined with -w as -nw. Window edges provide alignment targets along the movement path, allowing windows to fit beside squeezed titlebars. The exposed border beside a squeezed titlebar also provides an alignment target, so windows can line up before moving into the gap. Repeat the move to pass a window already reached.

-w

Warp the pointer to the centre of the moved window after a non-interactive move. This is independent of -n: -w follows a stepped move, while -nw follows a snapped move. No warp is performed when the moved window is not visible on its destination.

-o output

Move the window to the named output or output number (@N). May be combined with -k to land the window on a specific desk of that output; other flags (-d, -c, -x, -y, -P) are rejected when given with -o.

-C

Enable container-formation on drop. When the interactive move ends with the pointer over another window’s titlebar, the two windows are grouped into a container instead of completing a normal move. Releasing over empty space performs a normal move. The target window’s titlebar highlights in orange while the pointer is over it.

-G [selection]

Interactively move all windows in the named selection as a rigid group. The target window, when selected, is the group leader; otherwise the first movable selected window is used. Snapping and screen clamping apply to the leader and the resulting movement is applied to every group member. When selection is omitted, the only non-empty selection is used automatically, or a numbered typeahead menu is shown when several are available. Give default explicitly to bypass the chooser. This option cannot be combined with other move operations.

-P col[,row]

Move the window to the page at column col, row row (0-indexed), preserving its within-page position. Negative indices count from the end of the corresponding axis. A signed value suffixed with p is relative to the window’s current page, for example -P +1p,+0p moves it one page to the right. row defaults to 0.

The space-separated -P col row form is deprecated and translated to -P col,row with a warning.

Only meaningful when desktop_size is larger than 1x1.

-c

Centre the window on its current output.

-a anchor

Align the window’s outer frame within the current output’s usable area without changing its size. anchor is one of top-left, top, top-right, left, centre, right, bottom-left, bottom, or bottom-right.

-k 0-9

Move the window to desk number 0-9.

-x X

Set the window’s X position. A leading + or - makes the value relative to the current position. A value suffixed with vw is a percentage of the current output’s usable width; an absolute vw position places the outer frame relative to the usable area’s left edge.

-y Y

Set the window’s Y position. A leading + or - makes the value relative to the current position. A value suffixed with vh is a percentage of the current output’s usable height; an absolute vh position places the outer frame relative to the usable area’s top edge.

window-resize [-t target] [-gs] [-d dir] [-e edges|automatic|automatic-corner] [-W] [-w width] [-h height]

Resize a window. With no flags, begin an interactive resize operation with the mouse, from the bottom-right corner.

-t target

Target window.

-g -d dir

Grow the window in direction dir (up, down, left, right) by one step.

-s -d dir

Shrink the window in direction dir by one step.

-e edges|automatic|automatic-corner

Begin an interactive resize anchored on the named edges – a compass spec built from the letters n, s, e, w. Single letters select an edge (n, s, e, w); two-letter combos select a corner (ne, nw, se, sw). Used by bind-decoration to express per-edge / per-corner resize defaults as ordinary commands; default if -e is omitted is the historic se (bottom-right) corner. With automatic, the pointer position in the target window’s 3x3 grid selects the corresponding edge or corner. No resize starts from the centre cell or when the pointer is outside the target window. automatic-corner uses a 2x2 grid and always selects a corner, with no centre dead zone. The pointer must still be inside the window. If border.resize is configured, the selected borders use that colour until the operation ends. This visual feedback applies only when -e is present.

-W

Warp the pointer to the selected border before starting an interactive resize. On a single edge, its position along that edge is retained; at a corner, it is warped to the corner. This can be combined with either an explicit -e direction or -e automatic.

-w width

Set the client width in pixels. A value suffixed with vw sets the outer frame width to that percentage of the current output’s usable width, accounting for borders and titlebar.

-h height

Set the client height in pixels. A value suffixed with vh sets the outer frame height to that percentage of the current output’s usable height, accounting for borders and titlebar.

Viewport units may contain decimals. For example, these bindings place the focused window in the left half and bottom-right quarter respectively:

bind L+Left  window-move -x 0vw -y 0vh ; window-resize -w 50vw -h 100vh
bind L+3     window-move -x 50vw -y 50vh ; window-resize -w 50vw -h 50vh

Automatic resize direction works identically with keyboard and mouse bindings. The first example also warps the pointer to the chosen border:

bind L+r       window-resize -e automatic -W
mouse L+right  window-resize -e automatic

focus [-npfwL] [-O normal|mru] [-F expr] [-l N] [-t target] [-d dir] [-o output] [-i id]

Move keyboard focus.

With -n or -p, no eligible matching window is an error; moocow exits with status 1. If the current window is the only match, focus stays there and the command succeeds. This supports run-or-focus shell commands:

moocow focus -nf -F 'self.app_id == "emacs"' || emacs
-n

Focus the next window in the selected order.

-p

Focus the previous window in the selected order.

-O normal|mru

Choose the order for -n and -p. normal, the default, uses window-list order. mru uses most recently focused first; -n moves towards older entries and -p reverses that order. Filtering with -F, layer restrictions, and CirculateSkip still apply.

-f

Switch to the desk/page which the window to be focused is on, before focusing the window. If the window is iconified, it’s deiconified before the focus is applied. This option cannot be used with -o.

bind A+Tab focus -nf bind AS+Tab focus -pf

-F expr

Restrict -n and -p circulation with a DSL expression. self is bound to each candidate window. CirculateSkip still applies, so utility windows marked with winops -C on are skipped independently from the expression.

The filter is local to this focus invocation. Use it for predicates which are part of a particular focus action, such as limiting circulation to the current output and page. Use winops -C instead when a window should be skipped by normal circulation in general.

Examples:

  • focus -nw -F ‘self.current_output and self.current_page’

  • focus -nw -F ‘self.current_desk’

  • focus -nw -F ‘self.current_output’

  • focus -nw -F ‘self.current_output and self.current_page and self.app_id not in [“cowpager”]’

  • focus -nw -F ‘self.current_output and self.current_page and self.app_id not in [“cowpager”, “appA”, “appB”]’

-w

Warp the pointer to the centre of the focused window after focus is applied. May be combined with any other focus flag. When used alone, warps to the currently focused window without changing focus.

-d dir

Focus the nearest window in direction dir (up, down, left, right).

-o output

Warp the pointer to the named output. The -w flag is ignored when -o is given, as -o performs its own pointer movement.

-t selector

Select a window across all outputs and desks: %app_id matches an exact app ID, /text/ matches a title substring, and #id matches a River identifier. Add -f to follow it to its desk and page. Cannot be combined with -n or -p; use -F for filtered cycling.

moocow focus -f -t %foot moocow focus -nf -F ‘self.app_id == “foot”’

-i id

Focus the window with River identifier id.

-L

Restrict traversal to the focused window’s layer (see LAYERS). Combine with -n, -p, or -d; standalone -L is an error. When no window is focused, the current layer is taken as default_layer.

-l N

Restrict to layer N. Without -n or -p, focuses the topmost focusable window in layer N; if the focused window is already in layer N, cycles to the next focusable window in that layer. Combine with -n/-p to walk only within N. Mutually exclusive with -L.

window-list [-I] [-n] [-p] [-w] [-F expr] [-f format] [-S sort]

Open a transient list of managed windows, centred on the target output unless -p is used. Press 1 through 9, or 0 for the tenth entry, to select immediately. For longer lists use the arrow keys and Enter. Typing letters performs an incremental, case-insensitive filter over the displayed labels; only matching windows remain visible and the first match is selected. Backspace removes the last search character and expands the list again.

Containers appear once in the main list, with their pane and window counts. Open a container’s submenu to choose a window, grouped by pane, or browse a nested container. Typing in the main list searches inside containers too; matches include their container and pane names. -F still limits which windows can be selected, and -f formats the individual window entries.

Selecting an entry closes the list, switches to its output, desk, and page, restores it if iconified, activates its container tab if necessary, and focuses it.

-I

Show each window’s resolved application icon beside its label.

-n

Disable type-ahead searching. Number keys continue to select the first ten entries, and the arrow keys and Enter continue to navigate and select.

-p

Open the menu at the pointer instead of centring it on the target output.

-F expr

Only include windows matching the DSL expression. self is bound to each candidate window, as with show -a -F and focus -F.

-f format

Format each window label. The following conversions are supported:

  • %n: entry number (0 is the shortcut for the tenth entry)

  • %t: displayed title, including any title.format expansion

  • %T: raw client title

  • %a: application ID

  • %i / %I: full / first eight characters of the window identifier

  • %o / %O: output name / number

  • %d / %D: desk number / name

  • %x, %y, %w, %h: window position and content size

  • %l: window layer

  • %%: a literal percent sign

Without -f, the default is %o:%d %a - %T (output, desk, application ID, and raw window title). Type-ahead searches the resulting formatted label, so omitted fields are not included in searches. Filtered results retain the configured sort order.

-S sort

Sort windows by title, app, id, output, or desk. location sorts by output, desk, application ID, then title. none preserves CoW’s window order and is the default. Sorting is case-insensitive and stable.

-w

Warp the pointer to the selected window.

Examples:

bind L+w window-list
bind LS+w "window-list -F 'self.current_output and self.current_desk'"
bind L+a "window-list -f '%n  [%a] %t'"
bind L+o "window-list -f '%n  %o:%d  %t'"
bind L+s "window-list -S location"
bind L+n "window-list -n"
bind L+i "window-list -I -p"

The popup takes keyboard focus while it is open, so no additional bindings are needed for these keys and normal keyboard input resumes when it closes.

window-expand [-t target] [-d dir] [-T]

Expand a window to fill available space between windows, or the whole monitor if the window is the only window on that monitor.

Expansion respects squeezed titlebars and their exposed frame edges. When expanding in all directions, horizontal growth is resolved before vertical growth to avoid overlapping neighbouring windows.

Use window-maximize -k after expanding to retain the original window position so that it can be unmaximized.

-t target

Target window.

-d dir

Expand in direction dir (up, down, left, right). Omit to expand in all directions.

-T

Toggle: restore original geometry if the window is already expanded.

window-maximize [-t target] [-T] [-m] [-u] [-h] [-v] [-k] [-R]

Maximize or restore a window.

-t target

Target window.

-T

Toggle between maximized and restored. Combined with -h / -v, toggles only that axis; toggling an unmaxed axis maximises it.

-m

Maximize the window (default if no flag is given).

-u

Restore (unmaximize) the window. Combined with -h / -v, restores only that axis; a window can stay maximised on the other axis.

-h

Maximize horizontally only – the window’s width grows to the output’s usable area.

-v

Maximize vertically only – the window’s height grows to the output’s usable area.

-k

Keep the current geometry while marking the window as maximized. Combined with -T, restore if the requested axis is already maximized; otherwise mark it maximized without changing size.

-R

Ignore reserved areas and maximize to the full output dimensions. This includes both areas configured with reserved.EDGE and areas reported automatically by River for layer-shell surfaces. Combined with -h or -v, it applies only to the requested axis.

Per-axis maximised state is independent: window-maximize -h followed by window-maximize -v leaves the window maximised on both axes, and each axis can be restored separately with -T -h and -T -v.

Examples:

window-maximize        # full maximise
window-maximize -T     # toggle full maximise
window-maximize -h     # horizontal only
window-maximize -v     # vertical only
window-maximize -T -h  # toggle horizontal maximised state only
window-maximize -u -v  # un-maximise vertical only, keep horizontal
window-maximize -k     # mark maximised without changing size
window-maximize -R     # maximise over panels and other reserved areas

pick-window [-F expr] command … | pick-window -m [-F expr] [-n name]

Pick one or more windows with the mouse. The cursor changes to a crosshair icon while the picker is active.

Without -m, pick one window and run command targeting it. Right-click or Escape cancels. The picked window is passed to command as the default target, so a command without an explicit -t operates on the picked view. Any explicit -t in command takes precedence.

-m

Edit a named window selection. Left-click toggles a window and selected windows are outlined. Press Enter or right-click to accept the new selection. Press Escape to cancel and preserve the previous selection. The current selection is used as the starting point. If named selections already exist and -n is omitted, a numbered typeahead menu chooses which selection to edit. The menu includes the default selection. If no named selection exists, editing begins immediately with default as before.

-n name

With -m, edit name rather than the default selection.

-F expr

Only allow matching windows to be picked. self is bound to each candidate window.

Examples:

bind Super+p pick-window winops -c
bind Super+P pick-window { winops -c ; focus -L }
bind Super+o pick-window -F 'self.current_output' focus
bind Super+g pick-window -m
bind Super+G pick-window -m -n work

window-select [-t target] [-n name] [add|remove|toggle|clear]

Change membership of a named window selection without opening the interactive picker. The operation defaults to toggle, and name defaults to default. add, remove, and toggle act on the target window. clear empties the selection and does not require a target.

The selected field in window snapshots reports membership of the default selection. selections contains all selection names for the window. Selections are preserved across a CoW state restart, but are not configuration and therefore do not appear in show config.

The DSL can apply any command to the selected windows. Test selected for the default selection, or membership of selections for a named one. For example:

bind L+g  pick-window -m
bind LS+g window-move -G
bind LA+g winops -F 'self.selected' -i

script {
    for w in (windows where "work" in w.selections) {
        winops -t #{w.id} -sS
    }
}

Selection names can also populate a typeahead menu dynamically:

menu-add -T SelectionMenu "Window selections" title

script {
    menu-clear SelectionMenu
    menu-add SelectionMenu "Window selections" title
    for s in (selections) {
        menu-add SelectionMenu "#{s.name} (#{s.count})" \
            "window-move -G #{s.name}"
    }
    menu-show SelectionMenu
}

A multi-select menu can instead choose several windows at once. This example builds a menu from the current window list; accepting it toggles membership of each checked window in the work selection:

bind L+w script {
    menu-clear WorkWindows
    menu-add -MT WorkWindows "Toggle work windows" title
    for w in (windows) {
        menu-add WorkWindows \
            "#{w.output}:#{w.desk} - #{w.title}" \
            "window-select -n work -t #{w.id} toggle"
    }
    menu-add WorkWindows - -
    menu-add WorkWindows "Apply changes" accept
    menu-show WorkWindows
}

The resulting selection can then be acted upon as a group, for example:

window-move -G work
winops -F '"work" in self.selections' -i

window-shade [-t target] [-d dir]

Shade (roll up) a window toward its titlebar, or unshade it if already shaded. Use -d to choose another direction.

-t target

Target window.

-d dir

Shade direction: titlebar (default), north, south, east, west, nw, ne, sw, se. With titlebar, shading follows the titlebar when its position changes; compass directions remain fixed.

WINDOW DECORATION SETTINGS

The following tables show which settings are available to control the look and feel of a window. All of these can be used with decor -d.

General Decoration Settings:

Key Description
border.width Border thickness in pixels (default: 1).
border.corner_length Length of border corner handles in pixels (default: 20).
titlebar.font Pango font string for titlebars.
border.handles true | false. Draw handle marks on border corners.

Border Colour Settings (values are hexadecimal RGB or RGBA):

Key Description
border.active Active (focused) window border colour.
border.resize Temporary colour for borders selected by an interactive window-resize -e operation. When unset, no resize indication is drawn.
border.active.gradient Border gradient. Value is HGradient colour colour, VGradient colour colour, or none.
border.active.image PNG, SVG, or XPM image painted over the active border. Use none to clear.
border.active.n Active border: north edge.
border.active.n.gradient Active north edge gradient. The same .gradient suffix is available for s, e, w, nw, ne, sw, and se.
border.active.n.image Active north edge image. The same .image suffix is available for s, e, w, nw, ne, sw, and se.
border.active.s Active border: south edge.
border.active.e Active border: east edge.
border.active.w Active border: west edge.
border.active.nw Active border: north-west corner.
border.active.ne Active border: north-east corner.
border.active.sw Active border: south-west corner.
border.active.se Active border: south-east corner.
border.inactive Inactive (unfocused) border colour.
border.inactive.gradient Inactive border gradient.
border.inactive.image PNG, SVG, or XPM image painted over the inactive border. Use none to clear.
border.inactive.n Inactive border: north edge.
border.inactive.n.gradient Inactive north edge gradient. The same .gradient suffix is available for s, e, w, nw, ne, sw, and se.
border.inactive.n.image Inactive north edge image. The same .image suffix is available for s, e, w, nw, ne, sw, and se.
border.inactive.s Inactive border: south edge.
border.inactive.e Inactive border: east edge.
border.inactive.w Inactive border: west edge.

Titlebar Colour Settings (values are hexadecimal RGB or RGBA):

Key Description
titlebar.active_colour Titlebar background when focused.
titlebar.active_gradient Titlebar gradient when focused. Value is HGradient colour colour, VGradient colour colour, or none. Gradients are painted over the titlebar colour.
titlebar.active_image PNG, SVG, or XPM image painted over the focused titlebar. Use none to clear.
titlebar.inactive_colour Titlebar background when unfocused.
titlebar.inactive_gradient Titlebar gradient when unfocused. Use none for a solid colour.
titlebar.inactive_image PNG, SVG, or XPM image painted over the unfocused titlebar. Use none to clear.
titlebar.fg_active Titlebar foreground (text) when focused.
titlebar.fg_inactive Titlebar foreground (text) when unfocused.

Titlebar and Frame Settings:

Key Values Description
border.style fvwm | mwm | none Border rendering style. Default: none.
focus sloppy | click Focus model. Default: click.
clamp_mode output | all_outputs | none Constrain interactive window movement to the output under the pointer, the bounding rectangle containing all_outputs, or no boundary. Gaps between outputs are traversable with all_outputs. This setting does not affect edge snapping. Default: output.
geometry_window both | move | resize | none Show the geometry overlay while interactively moving or resizing windows. Default: both.
placement cascade | undermouse | centre | center | empty New window placement. empty chooses the first available position in top-to-bottom, left-to-right order and falls back to cascade if the full frame cannot fit without overlapping a visible window. Default: cascade.
titlebar.enabled true | 1 Show titlebars.
titlebar.height integer (pixels) Fix the titlebar height in pixels. When set to 0 (the default) the height is derived from the font size.
titlebar.position top | bottom | left | right Set where the titlebar is drawn. Default: top.
titlebar.active_relief raised | flat | sunken Relief of the title area when the window is focused. Default: raised.
titlebar.inactive_relief raised | flat | sunken Relief of the title area when the window is unfocused. Default: raised.
titlebar.active_title_justify left | centre | center | right Alignment of the title text when the window is focused. Default: centre.
titlebar.inactive_title_justify left | centre | center | right Alignment of the title text when the window is unfocused. Default: centre.
titlebar.button_style inherit | fvwm | mwm | none Rendering style for the title area and its buttons. inherit uses border.style, preserving the traditional coupled appearance. none draws the title area and buttons without relief. Default: inherit.
titlebar.button.N.vector vector string Geometry of titlebar button N, where N is 0 to 9. Odd buttons are placed on the left; even buttons are placed on the right, with button 0 nearest the right edge. Use none to hide a button slot unless it has an image configured.
titlebar.button.N.image PNG, SVG, or XPM image Image drawn inside titlebar button N instead of its vector glyph.
titlebar.button.N.active_relief raised | flat | sunken Relief of button N when the window is focused.
titlebar.button.N.inactive_relief raised | flat | sunken Relief of button N when the window is unfocused.
titlebar.button.N.active_colour hexadecimal RGB or RGBA Background of button N when the window is focused. If unset, the focused titlebar background is used.
titlebar.button.N.inactive_colour hexadecimal RGB or RGBA Background of button N when the window is unfocused. If unset, the unfocused titlebar background is used.
titlebar.squeeze none | natural | fixed Shrink the titlebar down to a minimum size. Remaining pixels of the titlebar strip are transparent and pass clicks through. none is the default (full-width titlebar); natural sizes the titlebar to the title text + buttons; fixed uses titlebar.squeeze.width.
titlebar.squeeze.justify left | centre | right Alignment relative to the titlebar’s orientation. On left-side titlebars, left means bottom and right means top; on right-side titlebars, they mean top and bottom. Default: left. center is accepted as a synonym for centre.
titlebar.squeeze.width integer (pixels) Width of the squeezed titlebar when titlebar.squeeze is fixed. Default: 300.
titlebar.squeeze.offset integer (pixels; may be negative) Nudge applied after justify. Positive values move right on horizontal titlebars, up on left-side titlebars and down on right-side titlebars. Default: 0.
titlebar.squeeze.min_width integer (pixels) Minimum width for natural mode (floor applied after measurement). Default: 80.
title_format string Controls the display title used in the titlebar. Default is: ‘[%t] %n’ Supported escapes are %n for the raw window title, %t for cow’s unique short window id, %I for the full window id, %c for app_id, and %% for a literal percent. The default is [%t] %n.

Text alignment and squeeze placement do not reverse for right-to-left titles. Arabic, Hebrew and mixed-direction text retain their normal writing direction within the chosen alignment.

The default decoration’s button 3 (inward chevrons beside Close) toggles the clicked window’s titlebar between full width and natural squeeze. Other decoration settings and pane buttons are unchanged.

Button vector format: N x1Xy1@c1 x2Xy2@c2 … where N is the number of points, coordinates are 0-100 (percentages of the button area), and @c is a colour index from 0 to 4. Coordinates may include a signed pixel offset with a p suffix, such as 100-5px50+1p@0.

WINDOW CONTAINERS

Group windows into a floating container of tabbed panes. Each pane shows its selected tab; panes can be split horizontally or vertically. Moving, maximizing, iconifying or changing the desk affects the whole group. Closing still affects the targeted application. Fullscreen temporarily shows one application, retaining the layout for restoration.

Each pane’s titlebar shows clickable tab labels. Clicking a label activates that tab. pane.title_format controls labels inside split containers and accepts the same substitutions as title_format, plus %p for the pane name (empty for unnamed panes). It can be set globally or in a decor profile.

The default, auto, shows named panes as [name] application title, or just the name for an empty pane. Unnamed occupied panes use title_format; unnamed empty panes show “Empty pane”. Ordinary windows and outer container titles continue to use title_format.

decor -d default pane.title_format "%p: %n"
decor -a default
decor -d editor-tab pane.title_format "%n"

For example, use the raw application title for all window and pane titles:

decor -d default title_format "%n"
decor -d default pane.title_format "%n"
decor -a default

Static text is also valid. It can be applied to a particular window using a decoration profile:

decor -d editor-tab title_format "Editor"
decor -a editor-tab -t %foot

window-container [-t target | -G name] [-b pane-name] [-s source] action

Use -t to select a target window, or -G to select an existing container by name, independently of focus. -G does not create or name a container; use -N to name one, or -E -N to create an empty named layout.

Add -b to select a named pane within that container. It also works with -t, using the selected window’s container. Unknown container or pane names produce an error.

Without -b, -G targets the outer window for adding, detaching, cycling and reordering tabs. Other actions use its active pane, or its first pane if empty. -s selects the source window to add or split into the target; defaults when selectors are omitted are described under each action.

Choose one action below. Containers contain side-by-side columns, each holding vertically stacked tabbed panes. Left/right splits add a column beside the target’s column; up/down splits add a row beside the target pane.

Split containers have a separate outer border and titlebar. Its ordinary buttons operate on the whole container; inner titlebars use pane controls. Plain tabbed groups retain their existing single titlebar.

Squeezed titlebars apply to panes with a single tab. Panes with multiple tabs use the full titlebar for their tab strip. Squeezing does not change the pane’s allocated rectangle; transparent space beside the titlebar is not a drop target.

Escape cancels a container move or resize, restoring its geometry and split proportions. If the layout or client minimum sizes change during the operation, CoW retains the valid layout rather than restoring an incompatible one.

New client minimum sizes can enlarge the container, even beyond the work area when necessary. Lowering those minima does not automatically shrink it.

window-container -e left|right|up|down

Place source in a new pane beside target. The target pane is split equally, subject to the applications’ minimum sizes. An existing tab in the target pane can be split out; otherwise the source must be uncontained. Detach a source in another pane or container with -r first. Dialogs and fullscreen windows cannot be grouped.

Adjacent panes share one border, drawn by the pane to its left or above. Handle marks appear only at the container’s outer corners. Resize a shared border to change the split; resize an outer border to resize the container. Tab cycling and reordering apply to the target pane. Empty panes disappear when their last application closes or is detached.

For example, using three distinct application IDs:

window-container -t %editor -s %terminal -e right
window-container -t %terminal -s %monitor -e down

+-------------------+-------------------+
| editor            | terminal          |
|                   +-------------------+
|                   | monitor           |
+-------------------+-------------------+
window-container -a

Add source to target’s container. If neither window is already in a container, a new container is created.

source defaults to the focused window. target defaults to the focused window when -t is given explicitly.

When neither -t nor -s is given, source is the focused window and target is the next focusable window in stacking order. This means window-container -a without selectors is a self-contained keyboard-only action: it groups the focused window with the one below it in the stack. This is useful in environments where mouse modifier combinations are unavailable (e.g. VNC).

A pane container’s outer window can itself be a tab. Its panes remain together: switching away hides the layout, and switching back restores it. The selector-free -a action uses the whole layout when a pane has focus. Explicit pane targets still add tabs within that pane.

For a layout named work and an uncontained browser window:

window-container -G work -s %firefox -a
window-container -G work -n
window-container -G work -r

These commands join the layout and browser as outer tabs, switch tabs, then detach the complete layout. Titlebar dragging with window-move -C also joins outer windows as tabs. Drop on the outer titlebar to join the whole layout, or inside a pane to tab or split there. Dropping on the content edge of a single-window outer tab gives that tab its own pane layout, leaving the other tabs unchanged. Layouts cannot be placed inside other layouts’ panes. Outer tab groups survive restart. When cycling from a pane without its own tabs, -n and -p switch the outer tabs instead.

window-container -N name [-t target]

Name or rename the target’s container. If the window is not contained, create a named, single-window container around it. Names are unique across containers and do not change application titles. Split containers display this name in their outer titlebar. An empty name removes the name.

Named containers survive with one window so mapping rules can add others later. Unless made persistent with -E, they disappear when their final window closes or is detached, and anonymous groups dissolve when only one window remains. Names survive a CoW restart. Use -E to define a layout before opening applications.

window-container -B name [-t target]

Name or rename the target pane. Pane names are unique within their container and survive restart. An empty name clears the pane name. The name stays with the pane when its active tab changes, and disappears when the pane is removed. Splitting retains the target pane’s name; the new pane starts unnamed.

window-container -G name [-b pane-name] action

-b selects a pane by name, or by #id using its numeric pane ID. Quote numeric selectors so # is not treated as a comment. For example, name an editor’s container and pane, then add other windows using those names:

window-container -t %editor -N work
window-container -G work -B editor
window-container -G work -b editor -s %terminal -e down
window-container -t %terminal -B terminal
window-container -G work -b terminal -s %monitor -a

Rules can create a group on its first matching window, then split later windows into rows. Use an explicit source so pointer focus does not select the wrong application. This example gives matching foot windows the application ID work-terminal (for example, foot -a work-terminal):

rule -g -T on-map -n work-terminals -s %work-terminal script {
    for c in (containers where c.name == "work") {
        window-container -G work -s "##{self.id}" -e down
        break
    }
    window-container -t "##{self.id}" -N work
}

The first window establishes the name; subsequent windows join before the idempotent naming command runs. To target a fixed pane instead of the active one, name that pane with -B and add -b to the split command. Use -a instead of -e down to add tabs rather than rows.

window-container -E -N name [-B pane-name]

Create a persistent container with one empty pane, optionally named with -B. An empty container opens as a normal decorated window, using the usual placement policy. Names must be unique. Unlike ordinary containers, its panes remain when their last windows close or are detached. Repeating -E declarations leaves existing named panes and their applications unchanged, so the same configuration can be reloaded.

window-container -G name -b pane -E -e direction [-B new-pane]

Add an empty pane beside the named pane. direction is left, right, up, or down. Left/right adds a column; up/down adds a row within the column. Adding an empty pane makes the container persistent. -B assigns a name for later commands; without it, each invocation adds a new unnamed pane. Omit -G and -b to split beside the focused pane, for example:

window-container -E -e left -B spare

If the target is an ordinary window, it becomes a container with the existing window beside the new empty pane, keeping its position and size. This also works for an application tab beside another container: the tab becomes its own layout without changing the neighbouring container.

The outer window keeps its ordinary border, titlebar and buttons, whether panes are empty or occupied. Move, resize, shade and iconify operate on the group; closing it asks its applications to close and removes the empty layout. Each vacant pane has its own decoration and labelled drop area. Inserting an application replaces that pane’s contents, not the outer window. Closing or detaching the application restores the vacant pane. Vacant panes can be focused, hidden, detached or closed using their pane controls, just like occupied panes.

Drag an independent window into a placeholder using window-move -C (the default Alt+Super+left-drag binding), or insert it explicitly:

window-container -G work -b terminal -s %foot -a

Or select the source with the pointer:

pick-window window-container -G work -b terminal -a

Dropping into a placeholder highlights and fills that pane, without creating another split. Further insertions with -a add tabs. Normal container operations become available through its populated panes. Layouts, empty panes and their names survive restart.

For example, define a workspace before launching any programs:

window-container -E -N work -B editor
window-container -G work -b editor -E -e right -B terminal
window-container -G work -b terminal -E -e down -B monitor

+------------------+------------------+
|                  | terminal         |
| editor           +------------------+
|                  | monitor          |
+------------------+------------------+

Use an on-map rule to fill a pane when a matching application opens:

rule -g -T on-map -n work-terminal -s %work-terminal script {
    window-container -G work -b terminal -s "##{self.id}" -a
}
bind L+Return exec foot -a work-terminal

The rule matches the application ID, not the next window to appear. Launching the same application from a terminal works too. Choose distinct IDs for different destinations where the application supports them. See examples/persistent-containers.conf for a complete example.

window-container -G name -D

Dissolve the container and remove its empty panes. Existing application windows remain open and independent. This also removes a wholly empty layout. -t may select a member instead of naming the container.

window-container -r [-t target]

Remove target from its container and restore its saved floating geometry. An anonymous container dissolves when only one member remains. Named containers retain one member; persistent layouts also retain empty panes.

window-container -f left|right|up|down

Focus the nearest pane in that direction within the same container.

window-container -c [-t target]

Collapse or restore the target pane to its titlebar. Space is given to adjacent panes along the split axis. The original split proportion is retained for restoration. A titlebar must be enabled.

Collapse requires a horizontal titlebar and at least one other expanded row in the column. The last expanded row is left unchanged.

window-shade is separate: it gathers the whole container’s titlebars into a strip. Shading again restores the columns, row sizes and collapsed rows. All panes must use horizontal titlebars. Hidden pane titles are temporarily shown while the container is shaded.

Separate pane-titlebar bindings collapse rows without changing shade/unshade for ordinary windows and outer container frames. These are the defaults:

bind-decoration -2 titlebar window-shade -d titlebar
bind-decoration -2 pane-titlebar window-container -c
window-container -v on|off|toggle|menu [-G container] [-b pane]

Show or hide a pane without closing its windows. Hidden panes reallocate space to the remaining panes. At least one pane must remain visible.

The outer titlebar shows a stacked-pane vector button while panes are hidden. Click it to choose a pane to restore, or use -v menu to open the same searchable menu from a binding. Adding panes while another is hidden preserves both: restoring it redistributes space within the updated layout.

The restore button is additional to the ten configurable button positions; it never replaces a user button. If there is insufficient titlebar space, it is omitted. Use Restore hidden pane… in the default Window Ops menu, or bind window-container -v menu, instead.

The system defaults mark borders violet where panes are hidden, preserving their relief. The indication follows layout rotation. Override the colour through the active decor:

decor -d default container.hidden_border 0xb090d0
decor -a default

container.hidden_border none disables the colour. Neither indicator applies to module-owned panels.

window-container -z column|container|off [-t target]

Temporarily enlarge the target pane to fill its column or the whole container. Its tabs stay available; the other panes in that area are hidden. Repeat the same scope to restore the layout, or use off. An outer frame targets its active pane. -G and -b may also select a named container and pane.

Zoom preserves the split proportions and outer window geometry, including across restarts. Focusing a hidden pane ends zoom. Unzoom before resizing internal dividers, rearranging panes or collapsing rows. A collapsed row must be expanded before zooming. Windows can still be detached or closed.

bind LA+z window-container -z column
bind LAS+z window-container -z container

Window records expose pane_zoom as off, column or container; pane records expose the same value as zoom.

window-container -H [-t target]

Hide the target pane’s titlebar without changing its decoration profile. Panes containing multiple tabs cannot hide their tab strip. Adding a tab to a hidden-title pane makes its strip visible. Collapsed rows must be expanded first. The outer container titlebar remains available when all inner titles are hidden. Groups without an outer titlebar retain a title on at least one pane.

window-container -T [-A] [-t target]

Toggle the target pane’s titlebar. An outer container window targets its active pane. With -A, restore all titles if any are hidden; otherwise hide every eligible pane title in that container. Required titles, tab strips and collapsed-row titles remain visible. Decoration profiles are unchanged.

bind LCA+t window-container -T
bind LCAS+t window-container -TA

The defaults also bind Super+Shift+middle-click over a pane to toggle its title, and double-clicking its border restores a hidden title. Border dragging still resizes. The latter binding uses self.panetitlehidden, which reports whether the pane title was explicitly hidden:

bind-decoration -2 pane-border script {
    if self.pane_title_hidden {
        window-container -T
    }
}
window-container -U [-t target]

Restore the hidden pane titles in the target’s container. Windows whose decoration profile disables titles retain that setting, except for the protected container handle.

The default configuration uses five pane buttons on the right:

[ □ ]       [ ▯ ]    [ - ]  [ ↗ ]  [ × ]
container   column   hide   detach  close
   zoom      zoom

On the left, button 1 rotates the layout and button 3 (a downward arrow) hides the entire pane. This is separate from button 4, which only hides its titlebar.

Buttons 8 and 6 toggle container and column zoom respectively. Their vectors show a square and a narrow rectangle; the active zoom button is sunken. Click it again to restore the layout.

Buttons 4, 2 and 0 respectively hide the pane title, detach the active tab, and close the active window. They retain the theme’s button colours and relief. Panes with multiple tabs omit the hide button so their tab strip remains visible, even if the decoration profile disables titlebars. Ordinary windows and plain tabbed groups keep their normal buttons. Tabs within a split container use the pane buttons. Button actions remain configurable with bind-decoration pane: bindings, independently of ordinary window buttons.

Double-click a pane title to collapse or restore its row. Middle-click a title to shade or unshade the whole container. Super+Ctrl+Alt+t restores hidden pane titles. With an existing configuration, add:

mouse T:0+middle window-shade -d titlebar
bind LCA+t window-container -U
bind-decoration pane:4 window-container -H
bind-decoration pane:3 window-container -v off
bind-decoration pane:6 window-container -z column
bind-decoration pane:8 window-container -z container
bind-decoration pane:2 window-container -r
bind-decoration pane:0 winops -c
window-container -q [-t target]

Equalise the split immediately containing the target pane, subject to application minimum sizes.

window-container -o clockwise|anticlockwise|180 [-t target | -G name]

Rotate the whole pane layout inside a container. The outer window keeps its position and size; panes retain their names, tabs and title settings. Split proportions rotate with the layout, subject to application minimum sizes. Applications and titlebar text are not rotated.

Pane titlebars have a clockwise-arrow button on the left (button 1). Single-click rotates clockwise; double-click rotates anticlockwise. These defaults are independent of ordinary window button 1:

bind-decoration pane:1 window-container -o clockwise
bind-decoration -2 pane:1 window-container -o anticlockwise

Unzoom, expand visible collapsed rows and leave fullscreen or shaded mode first. Hidden panes do not prevent rotation; after rotating, they restore expanded rather than collapsed. If the rotated layout cannot fit, nothing moves. Plain tab groups have no split layout to rotate. Named empty layouts can also be rotated.

window-container -o clockwise
window-container -G work -o anticlockwise

Before           Clockwise
+-----+-----+    +-----------+
|     |  B  |    |     A     |
|  A  +-----+    +-----+-----+
|     |  C  |    |  C  |  B  |
+-----+-----+    +-----+-----+
window-container -n [-t target]

Activate the next tab in the pane that holds target.

window-container -p [-t target]

Activate the previous tab in the pane that holds target.

window-container -R [-t target]

Move the tab containing target one position to the right in the tab strip. The tab remains active. No-op if it is already the last tab.

window-container -P [-t target]

Move the tab containing target one position to the left in the tab strip. The tab remains active. No-op if it is already the first tab.

With bind-decoration pane-titlebar window-move, dragging an embedded pane’s titlebar rearranges that pane and its tabs inside the container. Drop on another pane’s edge to place it alongside, or in its centre to join its tabs. Releasing outside the container detaches the active window at the pointer. Pressing Escape cancels. The outer titlebar still moves the whole container.

See also window-move -C, which allows containers to be formed interactively by dragging an uncontained window onto another. The titlebar or central area joins its tab set; the content edges create a split, indicated by an orange border on the destination side. A window already in a container is detached at the pointer, using its previous size and the destination output’s current desk and page. In environments where mouse modifier combinations are unreliable (e.g. VNC), bind container add to a key instead:

bind L+g   window-container -a
bind LS+g  window-container -r
bind L+Tab window-container -n

Container Settings

The following options can be used inside decor -d profile blocks to style container tabs:

Key Values Description
container.tab.active_colour hex colour (0xRRGGBB or 0xRRGGBBAA) Background colour of the active tab label. Default: 0xC0C0C0.
container.tab.active_gradient HGradient colour colour | VGradient colour colour | none Background gradient of the active tab label.
container.tab.active_image PNG, SVG, or XPM image Image painted over the active tab label. Use none to clear.
container.tab.inactive_colour hex colour (0xRRGGBB or 0xRRGGBBAA) Background colour of inactive tab labels. Default: 0x808080.
container.tab.inactive_gradient HGradient colour colour | VGradient colour colour | none Background gradient of inactive tab labels.
container.tab.inactive_image PNG, SVG, or XPM image Image painted over inactive tab labels. Use none to clear.
container.tab.fg_active hex colour (0xRRGGBB or 0xRRGGBBAA) Foreground (text) colour of the active tab label. Default: 0x000000.
container.tab.fg_inactive hex colour (0xRRGGBB or 0xRRGGBBAA) Foreground (text) colour of inactive tab labels. Default: 0x202020.
container.tab.sunken true | 1 | false | 0 Draw inactive tabs with a sunken (depressed) bevel. Default: false (flat).

Icons

Minimised windows can be represented as icons on the root window. These can have an image associated with them, or not. If there’s no image associated with an icon, a blank rectangle is drawn, and the window’s title appears underneath it.

Icons can be moved around the screen with the mouse. In doing so, a window will continue to iconify to that dragged position. Icons which are not moved manually will be placed automatically along the bottom of the output.

The following commands control icons:

icon-placement [-o OUTPUT] box full|WIDTHxHEIGHT±X±Y

icon-placement [-o OUTPUT] fill PRIMARY SECONDARY

icon-placement [-o OUTPUT] grid X Y

icon-placement [-o OUTPUT] compact BOOLEAN

icon-placement [-o OUTPUT] reset

Configure automatic icon placement. Without -o, the setting applies to all outputs. With -o, OUTPUT may be an output name or ordinal such as @2; only properties explicitly set for that output override the global policy.

box GEOMETRY

Constrain automatic placement to a rectangle within the output’s usable area. full uses the complete usable area. An X-style geometry uses pixel dimensions and offsets, for example 300x800-0+0. A negative X or Y offset measures from the right or bottom edge. Geometry outside the usable area is clipped.

fill PRIMARY SECONDARY

Set the movement direction within a row or column and the direction used when wrapping. Directions are left, right, up, and down. One must be horizontal and the other vertical. For example, right up fills a bottom row from left to right before wrapping upward; down left starts at the top-right, fills downward, then wraps leftward.

grid X Y

Set the positive pixel increments used while looking for a free position. The built-in automatic grid uses the icon dimensions plus the traditional 8-pixel gap. Explicit smaller increments allow tighter searching; occupied positions are skipped.

compact BOOLEAN

When true (the default), close gaps by arranging every automatic icon again whenever the set of icons changes. When false, keep surviving icons in place when another window is deiconified and use the first free position for the next icon.

reset

Restore the built-in global policy. With -o, remove all placement overrides for that output so it inherits the global policy again.

Manually dragged icons retain their positions and reserve their rectangles so automatic icons do not overlap them. Placement changes immediately reflow only automatic icons. If a configured box is full, cow searches the full usable area; if that too is full, it uses a deterministic overlapping fallback and logs a warning.

Examples:

The numbered boxes in these diagrams show the order in which free icon positions are considered.

The traditional policy starts at the bottom-left. right is the primary direction and up is the wrapping direction:

icon-placement fill right up

+---------------------------------------+
|                                       |
| [7] [8] [9]                           |
| [4] [5] [6]                 wrap: up  |
| [1] [2] [3]  primary: right           |
+---------------------------------------+

To start at the top-right, fill from top to bottom, and then add columns towards the left:

icon-placement box full
icon-placement fill down left
icon-placement compact false

+---------------------------------------+
|                           [7] [4] [1] |
|                  down: v  [8] [5] [2] |
|       <-- wrap left       [9] [6] [3] |
+---------------------------------------+

The same fill policy can be constrained to a box. In this example, 96x900-0+0 makes a 96 by 900 pixel strip at the top-right of the usable area; -0 anchors its right edge and +0 anchors its top edge:

icon-placement box 96x900-0+0
icon-placement fill down left

+-----------------------------------+-----+
|                                   | [1] |
|                                   | [2] |
|          rest of usable area      | [3] |
|                                   | [4] |
|                                   |  v  |
+-----------------------------------+-----+
                              placement box

Output-specific settings override only the named properties and inherit the remaining global policy:

icon-placement -o @2 box full
icon-placement -o @2 fill right down

# Remove all overrides for the second output.
icon-placement -o @2 reset

icon-reset [-F expr] [-t target]

Reset manual icon positions. Without -t, all saved icon positions are cleared and iconified windows are returned to automatic placement. With -t, the target may be a window, output, or desk; for example -t #id, -t @1, or -t :2.

-F expr

Only reset matching windows. self is bound to each candidate window. With a single target window, the expression is a guard.

Example:

icon-reset -F 'self.current_desk and self.iconified'

ICON SETTINGS

The following table shows which settings can be applied to a given (named) decor to change aspects of how icons are controlled:

Key Description
icon.enabled true | false. Show desktop icons for iconified windows. If disabled, windows can still be iconified, but no desktop icon is created.
icon.size Iconified window icon size in pixels (default: 64).
icon.label_area Height in pixels reserved below the icon image for the centred window title (default: 20).
icon.image auto, none, or a PNG, SVG, or XPM path drawn inside the iconified window icon box. The default, auto, resolves the application’s desktop-entry icon from its app ID, searching the XDG data directories, the hicolor icon theme, and pixmaps. CoW’s installed icon is used when no application icon can be found. An explicit path overrides the application icon; none draws no image. Decoration profiles and rules can therefore select icons for individual applications.
icon.background Icon box and label background when not hovered. Defaults to titlebar.inactive_colour; use default to restore this.
icon.foreground Label colour when not hovered. Defaults to titlebar.fg_inactive; use default to restore this.
icon.active.background Icon box and label background while hovered. Defaults to titlebar.active_colour; use default to restore this.
icon.active.foreground Label colour while hovered. Defaults to titlebar.fg_active; use default to restore this.
icon.title_relief raised | sunken | flat | none. Set the panel behind the icon label. flat fills the panel without a bevel, while none leaves the label background transparent (default: raised).
icon.format Controls the title used for window icons. Supports the same format expressions as title_format: %n for the window title, %t for the short window id, %I for the full window id, %c for the app_id, and %% for a literal percent sign. Defaults to ‘%c’.

Hovering over the image or label uses the active icon colours. This only changes the icon’s appearance: it does not focus or restore the application. Moving away restores icon.background and icon.foreground. Application images themselves are not recoloured, and icon.title_relief none keeps the label background transparent in both states.

MENUS

cow supports named popup menus. A menu is defined by appending items with menu-add, then opened with menu-show. Items may be cow commands, titles, separators, or links to sub-menus.

Defining menus

menu-add [-M] [-T] [-i ICON] [-d] NAME LABEL ACTION

Append an item to menu NAME, creating the menu on first reference. Special ACTION values:

  • title – LABEL becomes a non-selectable title row.

  • - (with LABEL also -) – adds a separator.

  • accept – LABEL accepts a multi-selection.

  • popup OTHER – the item opens sub-menu OTHER.

  • popup-pick OTHER – the item opens sub-menu OTHER; when no window context is known, the selected child action first picks a target window.

  • anything else – a cow command line run when the item is selected.

-d marks the entry as disabled. It cannot be selected or filtered by typeahead, and the entry’s text uses the disabled colour.

-i adds a PNG, SVG, or XPM icon to a selectable or disabled item. Absolute paths and paths beginning with ~/, $HOME/, or ${HOME}/ are used directly. Other names are searched for in icon.path. Titles and separators cannot have icons. If an icon cannot be found or read, cow logs a warning and retains the item without an icon.

-T enables typeahead for the containing menu. Typing filters its selectable items by label; Backspace removes the last character. The setting belongs to the named menu, so it need only be given on one menu-add command. It survives menu-clear and is removed by menu-destroy.

-M makes the containing menu multi-select. Clicking an ordinary item or pressing Space toggles its checkbox without running its action. Enter or an accept item closes the menu and runs every checked action in menu order. Escape closes it without running anything. Checked items remain selected when hidden by a typeahead filter. Like -T, this setting belongs to the menu and only needs to appear on one menu-add command.

For example, this menu launches any chosen combination of applications:

menu-add -MT Session “Start a session” title menu-add Session “Terminal” “exec foot” menu-add Session “Browser” “exec firefox” menu-add Session “Editor” “exec gvim” menu-add Session “Files” “exec thunar” menu-add Session - - menu-add Session “Launch selected” accept

bind L+s menu-show Session

menu-clear NAME

Empty the named menu without destroying it.

menu-destroy NAME

Remove the named menu and close any open popup of it.

Showing menus

menu-show NAME [-x N] [-y N] [-c [-o OUTPUT]]

Open the menu as a popup. Without explicit coordinates, the menu is centred horizontally on the current pointer location, with the pointer placed over the top/title row. With -x and -y, those coordinates are used directly. With -c, centres the menu on the current output, or the one specified by -o. The command queue’s explicit target window is captured as the menu’s “context view” so window-operation items target the right window even if focus drifts during navigation.

Menus take keyboard focus while open. Arrow keys navigate, Enter selects, Escape closes, and a menu item’s alphanumeric hotkey activates it. Keys listed in menu.repeat_keys repeat while held, using repeat_delay_ms and repeat_interval_ms. Backspace edits the search text in menus such as window-list which support type-ahead. The first visible actionable item is initially highlighted. Pointer hover does not replace that selection until the pointer moves after the menu opens. Selecting a submenu item with Up or Down opens it after the same short delay used for pointer hover; Right continues to open it immediately.

The mouse command can bind a menu to cow’s output background with the R context:

mouse R:0+left "menu-show RootMenu"

Styling

menu-style KEY VALUE
menu-style -m NAME KEY VALUE
menu-style -m NAME -r KEY

Without -m, sets the global menu style. With -m, sets an override for the named menu; the menu is created if it does not exist yet, so style declarations may precede menu-add. Properties not overridden by a named menu use the current global value.

-r KEY

Remove the named menu’s override for KEY, returning that property to the global menu style. This requires -m.

All style properties may be overridden per menu. Submenus resolve their own named overrides directly against the global style rather than inheriting their parent menu’s overrides. menu-clear preserves overrides; menu-destroy removes them with the menu.

The table below shows the possible values:

Key Default
bg 0xbebebe
bg.gradient none
fg near-black
font the current global font (a Pango font description)
title.bg 0xbebebe
title.bg.gradient none
title.fg near-black
separator mid-grey (title underline colour)
disabled mid-grey (disabled menu item colour)
border.colour 0x000000 (solid outer border colour)
border.width 2 (outer border thickness)
border.relief flat (use raised or sunken for a three-dimensional border)
padding.x 6 (horizontal item padding)
padding.y 2 (vertical item padding)
icon.size 16 (menu item icon width and height in pixels; 0 hides icons)
icon.gap 4 (space between an icon and its label)
icon.path $XDG_CONFIG_HOME/cow/icons or $HOME/.config/cow/icons. A colon-separated search path used by menu-add -i. A leading ~/, $HOME/, or ${HOME}/ in each entry is expanded when the setting is read; other relative entries use the directory containing the configuration file.
side.colour none (background colour for the menu’s side column; a colour or none)
side.width 24 (minimum width of the menu’s side column in pixels)
side.image none (PNG, SVG, or XPM image drawn in a column along the menu’s left side)
title.underlines 2
automatic.hotkeys true
selection.relief raised (the traditional two-pixel relief around the selected item); use none for a flat highlight or sunken for an inverted two-pixel relief

The default raised selection retains the menu background and derives a lighter top/left edge and darker bottom/right edge from the resolved bg colour. This also applies to per-menu background overrides, including very light and very dark colours; no selection.relief setting is required to enable it. The sunken selection applies the darker colour for top/left and the lighter one for bottom/right.

The flat outer menu border is a solid border.colour. Raised and sunken borders derive light and dark edges from border.colour.

The side column is present when either side.colour or side.image is set. Its minimum width is side.width, and it grows to the image’s natural width when necessary. Thus, side.colour and side.image can each be used on their own. side.image accepts an explicit path or searches icon.path for a bare file name. It is aligned with the bottom of the menu and cropped at the top if it is taller than the available space. Use 0x00000000 or none for side.colour to leave the menu background visible, and set side.image to none to remove it. For example:

menu-style icon.path ~/.config/cow/icons:/usr/local/share/cow/icons
menu-style side.colour 0x303060
menu-style side.width 32
menu-style side.image menu-side.xpm

Gradient values use the same syntax as decoration gradients: none, HGradient COLOUR COLOUR, or VGradient COLOUR COLOUR.

For hotkeys, a literal & before a label character marks that character as the item hotkey and underlines it. Use && for a literal ampersand.

When automatic.hotkeys is enabled, cow assigns unique alphanumeric hotkeys to selectable items that do not have an explicit & marker.

When font is not set, menus use the global font.

The menu font can be set globally or per menu. For example:

menu-style font 'Bitstream Vera Sans 17'
menu-style -m WindowOpsMenu font 'monospace 10'

This allows menu and titlebar fonts to be selected independently:

decor -d default titlebar.font 'Bitstream Vera Sans 10'
menu-style font 'Bitstream Vera Sans 17'

For example, these menus share the global geometry and default raised selection relief, but use different colour palettes:

menu-style bg 0xbebebe
menu-style fg 0x000000
menu-style icon.size 16
menu-style icon.gap 4
menu-style icon.path ~/.config/cow/icons

menu-style -m Applications bg        0xd8e0ef
menu-style -m Applications fg        0x182238
menu-style -m Applications title.bg  0x435a82
menu-style -m Applications title.fg  0xffffff
menu-style -m Applications separator 0x8798b8
menu-style -m Applications border.colour 0x293852

menu-style -m WindowOps bg        0xe7d8d2
menu-style -m WindowOps fg        0x321915
menu-style -m WindowOps title.bg  0x8b3a2f
menu-style -m WindowOps title.fg  0xffffff
menu-style -m WindowOps separator 0xb78378
menu-style -m WindowOps border.colour 0x57231d

menu-style -m RootMenu bg        0xd7e4e2
menu-style -m RootMenu fg        0x102b29
menu-style -m RootMenu title.bg  0x0d988c
menu-style -m RootMenu title.fg  0xffffff
menu-style -m RootMenu separator 0x72aaa5
menu-style -m RootMenu border.colour 0x084b46

# Remove only RootMenu's foreground override.
menu-style -m RootMenu -r fg

Example

# Relative icon names are searched below this directory.  This is already
# the default and is shown here to make the lookup explicit.
menu-style icon.path ~/.config/cow/icons

# A "send to desk" sub-menu and a window-ops menu that uses it.
menu-add SendToDesk "Send to Desk" title
menu-add SendToDesk "Desk 1"       "desk 1"
menu-add SendToDesk "Desk 2"       "desk 2"

menu-add WindowOps  "Window"        title
menu-add -i move.svg WindowOps "Move" "window-move"
menu-add WindowOps  "Maximise"      "window-maximize"
menu-add WindowOps  -               -
menu-add WindowOps  "Send To Desk"  "popup SendToDesk"
menu-add WindowOps  "Close"         "winops -c"

menu-add -i windows.svg RootMenu "Window Ops" "popup-pick WindowOps"
menu-add -i terminal.svg RootMenu "Terminal" "exec xterm"
mouse R:0+left "menu-show RootMenu"

# Arrow keys, Enter, Escape, and item hotkeys work while the menu is open.

DESKTOP / OUTPUT COMMANDS

This section details commands which affect desktops/pages often per-output.

scroll [-t output] [-w] [-p] _H_ _V_

scroll [-t output] [-a] _col_,_row_

scroll [-t output] -d _left_|_right_|_up_|_down_

scroll [-t output] -n

scroll [-t output] -P

scroll [-t output] [-R] -r

Pan the virtual page viewport for the current desk.

Each page has the logical width and height of its output. Reserved and layer-shell areas affect window placement and maximization, but do not change page dimensions or scroll distances.

Pages can be scrolled in either full pages, or part increments. Additionally, it’s possible to in-effect, “drag” the viewport with the mouse to any position.

In the deltas form (H V), H is the horizontal scroll and V the vertical. By default each value is a percentage of one page in that axis: 100 is one full page right (or down), -100 one page left (or up), 50 is half a page, and so on. With -p the values are raw pixels instead.

Options are:

-t output

Target the named output instead of the current one.

-w

Wrap at the desk edges. Without -w, the viewport is clamped to the desk extent. The old implicit-wrap form (a percentage of magnitude 1000 or more) is deprecated. CoW warns and converts it to -w, keeping the remainder of each value after division by 1000. For example, scroll 1100 0 becomes scroll -w 100 0. Explicit -w and pixel scrolling are unchanged.

-p

Treat H and V as pixels rather than percent of a page.

-a col,row

Page jump: land the viewport at page col,row. The pair are a single comma-separated token. Each axis accepts an absolute page index (0-indexed), a negative absolute index from the end (-1 is the last page in that axis), or a signed relative page offset with a p suffix (+0p means the current page in that axis). The -a flag may be omitted when the command has a single comma-separated argument.

-d dir

Jump one page in direction dir: left, right, up, or down, wrapping at desk edges.

-n

Next page (right, wrapping at the end of a row to the start of the next row). Shorthand for scroll -w 100 0.

-P

Previous page (left, wrapping).

-r

Start an interactive drag-the-viewport pan. The viewport follows the pointer until the triggering mouse button is released. Only valid when invoked from a mouse or key binding. Bind it to a mouse button to “grab and drag” the desk. The -w and -p flags are ignored with -r since interactive drag works in raw pixels and never wraps.

-R

Paired with -r, makes the viewport follow the pointer in the same direction instead of opposite.

Pages exist only when desktop_size N M is configured with N > 1 or M > 1.

Examples:

# One full page right (clamps at the last column).
scroll 100 0

# Half a page right, a quarter page down.
scroll 50 25

# 200 pixels right.
scroll -p 200 0

# One page right, wrapping at the desk edge.
scroll -w 100 0

# Jump straight to page (col=2, row=0).
scroll -a 2,0

# Jump to the last column in the current row.
scroll -a -1,+0p

# One page right, wrapping; convenient for keybindings.
scroll -d right

# Next / previous page with wrap.
scroll -n
scroll -P

# Bind Ctrl+Meta+Button1 to grab and drag the viewport.
mouse CM+left scroll -r

desk [-t output] [-d N] [-c N] [-np]

Switch or manipulate virtual desks.

With no operation, display a numbered typeahead menu of desks on the target output. If only one desk exists, it is selected directly. A bare desk command at the top level of the configuration has no effect; use it as a binding action to open the chooser interactively.

-t output

Target the named output instead of the current one.

-d N

Switch to desk number N.

-n

Switch to the next desk, wrapping around.

-p

Switch to the previous desk, wrapping around.

-c N

Toggle collection of desk N: overlay its windows on top of the current desk without switching.

For example, bind a key to choose a desk by name or number:

bind L+d desk

desk-name [-t output] N NAME | -d N | -l | -r

Manage the naming of virtual desktops. By default, cow sets ten desktops, whose names default to ‘0’..’9’. With this command it is possible to change that, either by increasing or decreasing the number of desktops, and/or renaming existing desks.

-t output

Target the named output instead of the current one. In global desktop mode the operation is mirrored across every output.

N NAME

Ensure desk N exists and set its name to NAME. Gaps below N are auto-filled with default-named desks. Names must be non-empty, printable, and contain no whitespace or :; duplicates within one set are rejected.

-d N

Delete desk N. Refused if any window is on it.

-l

List the current desks as JSON, per output.

-r

Reset every desk set to the default set, as if no manipulation had ever occurred.

The following examples show how this command can be used:

# Three named desks instead of the default ten.
desk-name 0 main
desk-name 1 web
desk-name 2 dev

# Now both forms select the same desk.
focus -t :main
focus -t :0

The minimum number of desks can be set with desks.count. Note that it is possible with the desk-name command to name desks greater than desks.count number, and that this will effectively create this desk.

Desktop / Page Settings

The following options can be used with the set command to change the behaviour of desktops and pages:

Key Values Description
desktop-configuration global | per-output | shared Desk layout mode.
desktop_size N M N × M integer Virtual page grid: N columns × M rows per desk. Default: 1 1 (no pages). Set e.g. 3 2 for three columns and two rows.
desks.count 0-32 Sets the number of available virtual desktops. Defaults: 10.
page_scroll_animate true | false Animate page transitions. Default: false (instant cut).
page_scroll_ms integer (ms) Page scroll animation duration. Default: 200.
edge_scroll true | false Enable edge scroll: hovering the cursor at a screen edge switches the page after edge_scroll_delay_ms milliseconds. Default: false.
edge_scroll_delay_ms integer (ms, 50..5000) Cursor hover time at an edge before the page switches. Default: 300.
edge_scroll_modifiers modifier combination Modifiers which must be held to activate edge scrolling. This uses the same L, S, A/M, C, and 5 notation as bind. Additional held modifiers do not prevent activation. Use 0 (the default) to require no modifier.
reserved.top integer (default: 0) Pixels reserved at the top of each output.
reserved.bottom integer (default: 0) Pixels reserved at the bottom of each output.
reserved.left integer (default: 0) Pixels reserved at the left of each output.
reserved.right integer (default: 0) Pixels reserved at the right of each output.

For example, require Alt while dwelling at an edge:

set edge_scroll true
set edge_scroll_modifiers A

The modifier applies both while moving a window and during ordinary pointer movement. Pressing it while the pointer is already at an edge starts the dwell, while releasing it cancels a pending page switch immediately. Without the required modifier, edge strips pass pointer input through to the windows beneath them.

NOT£: The reserved. settings are akin to X11’s concept of a working-area. That is to say, these settings will reserve parts of the screen to be able to put taskbars, etc.

Keyboard Settings

The following options can be used with the set command to tune keyboard input and layout behaviour:

Key Values Description
repeat_delay_ms integer (ms) Time the user must hold a bind -r repeatable key before the action starts repeating after its first fire. This also controls keys listed in menu.repeat_keys. Default: 400.
repeat_interval_ms integer (ms, >= 1) Gap between consecutive repeats of a held bind -r binding once the initial dwell has elapsed, and between held repeating keys in menus. Default: 40 (= 25 fires / s).
menu.repeat_keys comma-separated keysyms Keys which repeat while held when a menu has keyboard focus. The default is Up,Down; for example, use Up,Down,BackSpace to repeat type-ahead deletion, or none to disable menu key repetition.
keyboard_layout.per_window true | false Remember the active keyboard layout for each window and ask River to restore it when that window receives focus. A window without a remembered layout inherits the current layout. Default: false.

A binding registered without -r fires exactly once per press, regardless of the repeat timing settings.

For example, enable per-window keyboard layouts with:

set keyboard_layout.per_window true

SIGNALS

SIGTERM, SIGINT

Save state and exit cleanly.

SIGUSR1

Restart: execute the rule restart hook command (if configured), then re-execute cow itself via execvp(3) without tearing down the River session.

SIGUSR2

Toggle an additional log at $XDG_DATA_HOME/cow/cow.log, falling back to ~/.local/share/cow/cow.log. The normal configured log destination and level remain active, and the additional log uses the same level. Thus it defaults to info; use -l trace or COW_LOG_LEVEL=trace for a full trace. CoW creates the directory when needed, and writes both the enabling and disabling transitions to both destinations.

SIGPIPE

Ignored.

ENVIRONMENT

COW_CONFIG

If set, use this path as the configuration file, bypassing the default search order.

XDG_CONFIG_HOME

Base directory for the user configuration search ($XDG_CONFIG_HOME/cow/cow.conf). Defaults to ~/.config.

XDG_RUNTIME_DIR

Directory in which the IPC command and status sockets are created.

IPC PROTOCOL

Every command or status client must identify itself immediately after connecting by sending:

cow-client NAME

The line is terminated by a newline. NAME is 1 to 63 characters and may contain ASCII letters, digits, dots, hyphens, and underscores. A command client then sends one command and receives its JSON response before the connection closes. A status client sends no further input and receives an initial JSON snapshot followed by a snapshot after each state change.

FILES

/share/cow/cow-defaults.conf

Package-owned defaults, loaded before the selected user configuration unless -n is given. Inspect with show -D config.

~/cow.conf

User configuration (second search location).

$XDG_CONFIG_HOME/cow/cow.conf

User configuration in XDG base directory (third search location).

~/.config/cow/cow.conf

Fallback user configuration (fourth search location).

/etc/cow/cow.conf

System-wide configuration (fifth search location).

$XDG_RUNTIME_DIR/cow-$WAYLAND_DISPLAY-cmd.sock

IPC command socket. Requires the client-identification line, then accepts one command per connection and returns JSON. If WAYLAND_DISPLAY is unset, the fallback path is $XDG_RUNTIME_DIR/cow-cmd.sock.

$XDG_RUNTIME_DIR/cow-$WAYLAND_DISPLAY-status.sock

Status broadcast socket. Requires the client-identification line, then emits a JSON state snapshot on each change. If WAYLAND_DISPLAY is unset, the fallback path is $XDG_RUNTIME_DIR/cow-status.sock.

SEE ALSO

moocow(1), cow-module-cmd(1), cowbar(1), cowiconman(1), cowpager(1), river(1), wev(1), xdg-desktop-portal(1), xdg-desktop-portal-wlr(1), pipewire(1), wl-clip-persist(1)

AUTHORS

Thomas Adam <thomas@xteddy.org>

Parts of cow were derived from code under copyright by:

Nicholas Marriott