Window Rules

Window rules allow you to automatically configure how specific applications behave when they open. You can control which desktop a window appears on, whether it floats, its layer, and much more.

Understanding Window Rules

When a window opens, bspwm checks its class name and instance name against your rules. If there's a match, the rule's settings are applied automatically.

Finding Window Class and Instance Names

Before creating rules, you need to identify windows. Use xprop:

xprop | grep WM_CLASS

Click on a window, and you'll see output like:

WM_CLASS(STRING) = "Navigator", "firefox"

The format is "instance_name", "class_name". In this example: - Instance name: Navigator - Class name: firefox

Alternative methods:

# Using xdotool
xdotool selectwindow getwindowclassname

# Using bspwm's query (for existing windows)
bspc query -T -n focused | jq '.client.className'

Creating Rules

Basic Syntax

bspc rule -a CLASS_NAME[:INSTANCE_NAME] [CONSEQUENCES...]

Examples:

# Match by class name only
bspc rule -a Firefox desktop='^2'

# Match by class and instance
bspc rule -a Firefox:Navigator desktop='^2'

# Wildcard matching
bspc rule -a '*:*:Picture-in-Picture' state=floating

Rule Matching

  • Class only: Firefox - Matches any window with class "Firefox"
  • Class:Instance: Firefox:Navigator - Matches class "Firefox" AND instance "Navigator"
  • Wildcards: * matches anything
  • Title matching: CLASS:INSTANCE:TITLE - Some special windows need title matching

Rule Consequences

Desktop Placement

Control which desktop windows appear on.

# Send to desktop by index
bspc rule -a Firefox desktop='^2'

# Send to desktop by name
bspc rule -a Firefox desktop='Web'

# Send to specific monitor's desktop
bspc rule -a Slack desktop='HDMI-1:^3'

Window State

Control the window's tiling state.

# Always float
bspc rule -a Gimp state=floating

# Always tile (override floating defaults)
bspc rule -a Steam state=tiled

# Pseudo-tiled (maintains preferred size)
bspc rule -a Pavucontrol state=pseudo_tiled

# Fullscreen
bspc rule -a mpv state=fullscreen
State Effect
tiled Window is managed by the tiling tree
pseudo_tiled Window maintains its size, centered in tile space
floating Window floats freely above tiled windows
fullscreen Window fills the screen, no borders

Stacking Layer

Control the window's stacking order.

# Always on top
bspc rule -a 'Picture-in-Picture' layer=above

# Always below other windows
bspc rule -a Conky layer=below

# Normal layer (default)
bspc rule -a Firefox layer=normal

Flags

Set special window flags.

# Sticky (follows you to all desktops)
bspc rule -a Spotify sticky=on

# Hidden (starts hidden, for scratchpads)
bspc rule -a scratchterm hidden=on

# Locked (can't be closed with bspc node -c)
bspc rule -a important locked=on

# Private (maintains position)
bspc rule -a terminal private=on

Focus Behavior

Control focus when the window opens.

# Follow focus to the new window (default)
bspc rule -a Firefox follow=on

# Don't steal focus when opening
bspc rule -a Slack follow=off

# Focus the window (bring it to front)
bspc rule -a urgent_app focus=on

Centering Floating Windows

# Center a floating window on the screen
bspc rule -a Pavucontrol state=floating center=on

Split Direction and Ratio

Control how the tree splits when the window is inserted.

# Split to the right
bspc rule -a terminal split_dir=east

# Split below
bspc rule -a terminal split_dir=south

# Custom split ratio (0-1)
bspc rule -a sidebar split_ratio=0.3 split_dir=west

Border Control

# Borderless window
bspc rule -a Conky border=off

# Force border
bspc rule -a Firefox border=on

Management Control

# Don't manage this window (bspwm ignores it)
bspc rule -a Conky manage=off

# Manage it (useful for override rules)
bspc rule -a special manage=on

One-Shot Rules

One-shot rules apply only to the next window matching the pattern, then automatically delete themselves.

# The next Firefox window will float, then this rule is removed
bspc rule -a Firefox --one-shot state=floating

# Short form
bspc rule -a Firefox -o state=floating

Use cases:

  • Temporarily opening a window differently
  • Scripted window placement
  • Dialogs from scripts

Managing Rules

List All Rules

bspc rule -l

Output shows rule index and configuration:

Navigator:firefox => desktop=^2 follow=on
Gimp:* => state=floating
*:*:Picture-in-Picture => state=floating sticky=on layer=above

Remove Rules

# Remove by class/instance pattern
bspc rule -r Firefox
bspc rule -r Firefox:Navigator

# Remove by index (from rule -l output)
bspc rule -r head    # First rule
bspc rule -r tail    # Last rule
bspc rule -r '^3'    # Third rule

# Remove all rules
bspc rule -r '*'

External Rules Command

For complex, dynamic rules, use an external script instead of static rules.

Setup

bspc config external_rules_command "$HOME/.config/bspwm/external_rules"

Script Structure

The script receives these arguments:

  1. Window ID
  2. Class name
  3. Instance name
  4. Monitor, desktop, and node selector consequences (from matching rules)

It outputs rule consequences in key=value format.

Example External Rules Script

#!/bin/bash
# ~/.config/bspwm/external_rules

wid=$1
class=$2
instance=$3

# Get window title for more specific matching
title=$(xdotool getwindowname "$wid" 2>/dev/null)

# Debug: uncomment to log window info
# echo "Window: $wid, Class: $class, Instance: $instance, Title: $title" >> /tmp/bspwm_rules.log

# Float dialogs and popups
case "$instance" in
    *[Dd]ialog*|*[Pp]opup*|*[Pp]references*)
        echo "state=floating center=on"
        ;;
esac

# Float windows with specific titles
case "$title" in
    "Save As"*|"Open File"*|"Confirm"*)
        echo "state=floating center=on"
        ;;
    "Picture-in-Picture"|"Picture in picture")
        echo "state=floating sticky=on layer=above"
        ;;
esac

# Application-specific rules
case "$class" in
    Spotify)
        echo "desktop=^10 follow=off"
        ;;
    Slack)
        echo "desktop=^9 follow=off"
        ;;
    Gimp)
        echo "state=floating follow=on"
        ;;
    Steam)
        case "$title" in
            "Steam"|"Steam - News"*)
                echo "state=floating"
                ;;
            *)
                echo "state=tiled"
                ;;
        esac
        ;;
esac

# Float small windows (requires xdotool)
geometry=$(xdotool getwindowgeometry "$wid" 2>/dev/null)
if [[ "$geometry" =~ ([0-9]+)x([0-9]+) ]]; then
    width="${BASH_REMATCH[1]}"
    height="${BASH_REMATCH[2]}"
    if (( width < 400 && height < 400 )); then
        echo "state=floating center=on"
    fi
fi

Make it executable:

chmod +x ~/.config/bspwm/external_rules

Practical Rule Collections

Development Setup

# IDE on desktop 1
bspc rule -a jetbrains-idea desktop='^1' follow=on

# Terminal floats with half-screen size (pseudo-tiled)
bspc rule -a Alacritty:dev-terminal state=pseudo_tiled

# Browser for documentation on desktop 2
bspc rule -a Firefox desktop='^2'

# Database tools on desktop 3
bspc rule -a DBeaver desktop='^3'

Media and Entertainment

# Video player fullscreen
bspc rule -a mpv state=fullscreen

# Picture-in-picture always visible
bspc rule -a '*:*:Picture-in-Picture' state=floating sticky=on layer=above

# Music player on last desktop
bspc rule -a Spotify desktop='^10' follow=off

# Volume control floats and centers
bspc rule -a Pavucontrol state=floating center=on

Communication

# Chat apps on desktop 9, don't steal focus
bspc rule -a Slack desktop='^9' follow=off
bspc rule -a discord desktop='^9' follow=off
bspc rule -a TelegramDesktop desktop='^9' follow=off

# Email on desktop 8
bspc rule -a thunderbird desktop='^8'

Utility Windows

# File manager floats
bspc rule -a Pcmanfm state=floating
bspc rule -a Thunar state=floating

# System monitor floats
bspc rule -a Lxappearance state=floating
bspc rule -a Nitrogen state=floating

# Password manager floats
bspc rule -a KeePassXC state=floating center=on

Scratchpad Implementation

Scratchpads are hidden windows you can toggle on/off. Here's how to implement them:

1. Create a Rule for the Scratchpad Window

bspc rule -a Alacritty:scratchpad sticky=on state=floating hidden=on

2. Create a Toggle Script

#!/bin/bash
# ~/.local/bin/scratchpad

id=$(xdotool search --classname scratchpad)

if [ -z "$id" ]; then
    # Scratchpad doesn't exist, create it
    alacritty --class Alacritty,scratchpad &
    sleep 0.2
    id=$(xdotool search --classname scratchpad)

    # Position it (centered, 80% width, 60% height)
    eval $(xdotool getdisplaygeometry --shell)
    w=$((WIDTH * 80 / 100))
    h=$((HEIGHT * 60 / 100))
    x=$((WIDTH / 2 - w / 2))
    y=$((HEIGHT / 2 - h / 2))
    xdotool windowsize "$id" $w $h
    xdotool windowmove "$id" $x $y
fi

# Toggle visibility
bspc node "$id" -g hidden -f

3. Add a Keybinding

In sxhkdrc:

super + grave
    ~/.local/bin/scratchpad

Troubleshooting Rules

Rule Not Working?

  1. Check the class/instance name: bash xprop | grep WM_CLASS

  2. List current rules: bash bspc rule -l

  3. Check for typos - Class names are case-sensitive!

  4. Order matters - Later rules can override earlier ones

  5. Use external rules for debugging: bash #!/bin/bash echo "$(date): $@" >> /tmp/bspwm_rules.log

Window Opens on Wrong Desktop?

  • Check if multiple rules match
  • External rules run after static rules
  • Some applications spawn multiple windows (check all their classes)

Floating Windows Not Centered?

  • Add center=on to the rule
  • Some windows set their own geometry after opening
  • Try using a one-shot rule or external rules script