Gnoblin
Docs

gnoblin.window_rule

Add an ordered window or layer-surface rule. All fields in match must match. Later matching rules override only the fields they provide.

See the window rules guide for usage patterns.

Lua
gnoblin.window_rule {
    match = {type = "window", app_id = "^org.example.App$"},
    opacity = 0.9,
}

Match fields

Field Values
type "window" or "layer"
focused Boolean
app_id Lua pattern against GTK app ID, falling back to WM class
title Lua pattern against the window title
layer Lua pattern against the layer-shell namespace
workspace_id Exact workspace ID; declared or explicitly assigned at runtime
workspace_number Current one-based workspace position, 1–1024
client_type "wayland" or "x11"; "x11" is an Xwayland client

All supplied match fields must match. Text patterns use Lua 5.4 string.find syntax. They are case-sensitive and search anywhere by default. Use ^ and $ to match the whole value.

In Lua patterns, . matches any character and %. matches a literal dot. For example, ^org%.example%.App$ matches the complete ID org.example.App. JavaScript and PCRE regex syntax is not supported. workspace_id matches an exact, case-sensitive ID.

See the window rules guide for examples and ways to find a live app ID, title, or layer namespace.

Declare persistent workspace IDs in gnoblin.configure.workspaces. A matcher can also name an explicitly assigned runtime ID. Generated @session-N IDs are temporary and should not be saved in configuration. A {id = ...} placement target must exist when a matching window opens; if it does not, Gnoblin leaves the window in place and logs a warning.

Rule fields

Field Values
blur Number 0–100; blur radius in pixels; 0 disables blur
opacity Number 0–1; affects content and text
blur_ignore_shadows Boolean; default false; limits blur to the window frame
corners Corner fields below
shader GLSL file path; "" clears it
shader_uniforms Up to 64 uniform names mapped to finite numeric values
animation Built-in or registered animation name, or an event map
workspace {id = "code"} or {number = 1..1024}; place a new window on a workspace
frame Frame fields below

To change one of these effects for one window while the session runs, use window:set_effect.

Blur follows the opacity of the surface it sits behind. When a layer-shell panel fades in or out, whether through a layer-open animation or its own alpha, the blur fades with it. Panels need no extra protocol or setting for this.

If a backdrop capture fails, Gnoblin shows the current backdrop without blur for that frame. Blur resumes after the next successful capture.

Uniform names must be valid GLSL-style identifiers; the gnoblin_ prefix is reserved. Values must fit in a finite 32-bit float.

Workspace match fields select a window's current workspace and update when the window changes workspaces.

The workspace placement effect runs once when a normal window is created. It is not reapplied when its title or focus changes or when the configuration reloads. Transient and modal windows stay with their parent. See the workspaces section of the window rules guide.

Corners

Field Default Values
radius 0 0–200 logical pixels
smoothing 0 0–1; circular to a squarer curve
mode "auto" auto, force, off; off disables corner, border, and shadow effects
padding {0, 0, 0, 0} Top/right/bottom/left inset, −128–128 logical pixels
keep_maximized true Keep rounding when maximised
keep_fullscreen, keep_tiled false Keep rounding in those states
skip_libadwaita true Preserve libadwaita corners in auto mode
skip_libhandy false Skip libhandy windows in auto mode
remove_csd false Detect and replace client-drawn rounded corners
border_width, border_color 0, #808080ff Single native outline; width −40–40 logical pixels
shadow false true for the default shadow, one table, or a list of 1–4 layers
keep_shadow false Keep replacement shadows in maximised/fullscreen/tiled states

Shadows

The single native outline is configured by corners.border_width and corners.border_color above. Positive widths draw inward; negative widths draw outward where the client's existing buffer has room. The corners.shadow field accepts false, true for the default, one table, or a list of 1–4 shadow layers.

Field Values / default
corners.shadow.{x, y, spread} Defaults 0, 4, 4; −100–100
corners.shadow.blur Default 28; 0–100
corners.shadow.opacity Default 0.6; 0–1
corners.shadow.color Default #000000ff; #RRGGBB or #RRGGBBAA
corners.shadow_animation.animation Animation name that supports the shadow-change event
corners.shadow_animation.duration 0–2000 ms; default 0
corners.shadow_animation.easing Same easing values as gnoblin.configure shell animations

Animation fields

Choose a built-in or registered animation name. To select names by event, use a table:

  • Use layer_open and layer_close for layer surfaces.
  • duration and easing override the selected animation.

See the animation guide for supported events and presets.

Field Values
animation.open, animation.close, animation.layer_open, animation.layer_close Built-in or registered animation name
animation.duration 0–5000 ms
animation.easing Same easing values as gnoblin.configure shell animations

Omitted values inherit shell settings. in needs brackets because it is a Lua keyword.

Frame fields

Field Default Values
mode "off" "off", "auto", "prefer-server", "replace"
extents {32, 1, 1, 1} Top/right/bottom/left frame size; integers 0–256
crop {0, 0, 0, 0} Removed client margins; same order and range
renderer "native" Registered service name
style "default" Style understood by that renderer
background "#242424" Active background colour
foreground "#eeeeee" Foreground colour
inactive_background "#303030" Unfocused background colour
button_layout {"minimize", "maximize", "close"} Native-frame buttons, without duplicates; {} hides them

The generic default mode is off. GTK Wayland windows that create a KDE server-decoration object use auto behavior by default and receive Gnoblin's native frame. X11 apps running through Xwayland use auto when no rule sets frame.mode, because an X11 app has always left decorations to the window manager. A matching rule's frame.mode overrides that behavior.

An X11 app asks for a server frame when it is a normal, dialog or utility window and does not turn decorations off. An app that sets its Motif decorations hint to 0 draws its own frame and gets none under auto.

  • _GTK_FRAME_EXTENTS declares the visible bounds, like xdg_surface.set_window_geometry.
  • client_type = "x11" in match gives X11 apps their own frame rule.

auto supplies SSD only for explicit client requests. replace crops client pixels and adds a frame.

With prefer-server, Gnoblin clips CSD buffer margins to the app's declared visible bounds and applies Gnoblin's configured border and shadow there. It does not estimate margin sizes. An app that omits visible bounds uses its full surface.

frame.button_layout sets the right-side buttons in Gnoblin's built-in native frame, including the fallback shown while an external renderer is unavailable. It is not sent to external renderers. Those renderers use their own layout settings; Bingux follows GTK settings or its --button-layout argument.

The global window_management.button_layout preference does not control these frames. See titlebar button placement.

See the window frames guide for frame modes and extents. Register renderers with gnoblin.configure.

remove_csd

Enable remove_csd when an application draws its own rounded corners and you want Gnoblin's configured shape to control the result. Gnoblin inspects the window's rendered pixels to detect the corner cutouts, then fills those gaps from the app's nearby background before applying the configured corners.

Gnoblin adapts to each window's actual content instead of assuming a particular toolkit, corner radius, or background colour.

It also handles the narrow antialiased edge around the detected curve while preserving opaque content.

Lua
gnoblin.window_rule {
    match = {type = "window", app_id = "^org.example.App$"},
    corners = {
        radius = 12,
        mode = "force",
        remove_csd = true,
    },
}

This is opt-in because detecting and sampling the window image has a cost. Use it on rules for apps whose own rounded corners conflict with Gnoblin's shape; leave it off for other windows. The option changes corner rendering only. It does not remove client-side titlebars or other decorations.

If the app's corner background varies sharply near the edge, Gnoblin may not find a safe fill, so the original corner can remain visible.

Type definition

This is schema pseudocode in Lua table form. ? marks an optional field; | separates accepted alternatives. Every supplied match field must match.

Lua
gnoblin.window_rule {
    match = {
        type = "window" | "layer"?,
        focused = boolean?,
        app_id = string?, -- Lua 5.4 pattern
        title = string?, -- Lua 5.4 pattern
        layer = string?, -- Lua 5.4 pattern
        client_type = "wayland" | "x11"?,
        workspace_id = string?,
        workspace_number = integer?, -- 1–1024
    },
    blur = integer?, -- 0–100
    opacity = number?, -- 0–1
    blur_ignore_shadows = boolean?,
    shader = string?,
    shader_uniforms = {string = number}?,
    animation = string | {
        open = string?, close = string?,
        dialog_open = string?, dialog_close = string?,
        layer_open = string?, layer_close = string?,
        minimize = string?, restore = string?, workspace_switch = string?,
        shadow_change = string?,
        resize = string?,
        duration = integer?, -- 0–5000 ms
        easing = string?,
    }?,
    workspace = {id = string} | {number = integer}?,
    corners = {
        radius = number?, -- 0–200
        smoothing = number?, -- 0–1
        mode = "auto" | "force" | "off"?,
        padding = {number, number, number, number}?,
        border_width = number?, -- -40 to 40
        border_color = string?, -- #RRGGBB or #RRGGBBAA
        keep_maximized = boolean?,
        keep_fullscreen = boolean?,
        keep_tiled = boolean?,
        skip_libadwaita = boolean?,
        skip_libhandy = boolean?,
        remove_csd = boolean?,
        shadow = false | Shadow | {Shadow, ...}?,
        keep_shadow = boolean?,
        shadow_animation = {
        animation = string?,
            duration = integer?, -- 0–2000 ms
            easing = "linear" | "ease-out-cubic" | "ease-out-quad" | "ease-in-out-cubic"?,
        }?,
    }?,
    frame = {
        mode = "off" | "auto" | "prefer-server" | "replace"?,
        extents = {integer, integer, integer, integer}?, -- 0–256 each
        crop = {integer, integer, integer, integer}?, -- 0–256 each
        renderer = string?,
        style = string?,
        background = string?,
        foreground = string?,
        inactive_background = string?,
        button_layout = {"minimize" | "maximize" | "close", ...} | {}?,
    }?,
}

-- Shadow = {x = number?, y = number?, blur = number?, spread = number?,
--           opacity = number?, color = string?}
Esc

Try