-
-
Notifications
You must be signed in to change notification settings - Fork 0
Window Effects
Glass, blur, drop shadows, rounded corners and the CRT post-process pass. All of
it is synuirc configuration, and the shipped default is conservative: as of
pkgrel 183 a fresh install boots with window effects off and the SYNAPSE
wallpaper on, because effects on a machine that cannot afford them is a bad first
impression that looks like a broken system.
A default change is invisible on an existing machine. Your
synuircis yours; new defaults only reach the ISO and fresh installs.
Rendering is scenefx 0.5 on wlroots 0.20. scenefx is a fork of wlroots' scene graph, which matters — see the HDR note below.
All of these go in ~/.config/synui/synuirc (or /etc/synui/synuirc).
| Key | Default | Range | What it does |
|---|---|---|---|
transparency |
off |
on/off | Master switch for translucent windows |
active_opacity |
— | 0–1 | Focused window opacity |
inactive_opacity |
— | 0–1 | Unfocused window opacity |
foot_alpha |
— | 0–1 |
foot's alpha, decoupled from the slider above |
corner_radius |
12 |
0–48 | Rounded window corners |
| Key | Default | Range | What it does |
|---|---|---|---|
blur |
on |
on/off | Backdrop blur behind translucent windows |
blur_passes |
3 |
1–5 | Dual-kawase passes |
blur_radius |
5 |
1–20 | Per-pass radius |
blur_noise |
0.02 |
0–1 | Dither, to break up banding |
blur_brightness |
0.90 |
Post-blur brightness | |
blur_contrast |
1.00 |
Post-blur contrast | |
blur_saturation |
1.15 |
Post-blur saturation | |
glass_halo |
0 |
0–64 px | Let the blur reach past the window as a ring |
| Key | Default | Range | What it does |
|---|---|---|---|
shadow |
on |
on/off | Drop shadow |
shadow_blur_sigma |
18 |
0–80 | Gaussian sigma |
shadow_spread |
0 |
0–64 px | Solid ring outside the window before the tail |
shadow_offset_x / _y
|
0 / 6
|
px | Offset |
shadow_opacity |
0.45 |
0–1 | |
shadow_color |
black | ||
clip_csd_margin |
on |
on/off | Crop clients to their declared xdg geometry |
| Key | Default | What it does |
|---|---|---|
effects |
off |
Master switch for the post-process pass |
effect_scanline |
0.35 |
|
effect_curvature |
0.25 |
|
effect_aberration |
0.40 |
Chromatic aberration |
effect_glitch |
0.60 |
|
effect_phosphor |
off |
Monochrome tint: amber / green / white |
effect_mono |
0.90 |
How strongly the phosphor tint desaturates |
effect_bloom |
0.55 |
Phosphor glow |
Persisted to filters.state, adjustable live with a slider per effect.
night_light / night_light_temp, or Super+Shift+B.
This is the single most useful skill for debugging "the shadow looks wrong on this app", and it took a measurement pass to establish.
synui's shadow is a gaussian. No hard edge, reach = sigma + spread, and
shadow_offset_y makes the top band differ from the sides. The shader clips at
2·σ_eff, where coverage is about 2% — never a visible step.
A hard cliff back to the wallpaper is a client margin. GTK crops its own shadow to a rectangular margin that differs per side, so the tell is a hard edge at an asymmetric distance — tens of pixels on one side, a handful on another.
So an app whose shadow looks "overgrown and square-cornered" next to everything else is drawing its own. It was never synui's shadow.
Firefox never binds xdg-decoration. synui declares SERVER_SIDE, but a
client only obeys if it binds the protocol, so Firefox keeps its GTK frame and an
invisible CSD shadow margin outside the window geometry it declares — and
paints a heavy shadow into it, on top of synui's own.
Proven by logging geometry against wlr_surface_get_extents:
firefox geo = 26,23 896x490 inside ext 948x542
foot geo == ext exactly
Measured, that margin is a ~36 px near-black slab at ~85% darkening with a hard step, against a server-decorated app's 12 px soft ring.
clip_csd_margin (default on) clips the client tree to
wlr_xdg_surface.geometry, so every window's ring is the one synui draws — same
width, same rounded corners. It also takes the margin's input region off the grab
ring, which is the documented cause of "Firefox can't be resized by its edges".
Three things that are easy to get wrong there:
-
Clip the client tree, not
view->scene_tree.wlr_scene_subsurface_tree_set_clip()recurses into every subsurface tree below the node it is given, and xdg popups are parented beside the client tree — so clipping the parent crops the menus. synui capturesview->client_treeright afterwlr_scene_xdg_surface_create(), when the tree has exactly one child. - Skip the no-op case. A clip that covers everything is not free: it puts a source box and dest size on the scene buffer, and that alone shifted the blurred backdrop around a terminal by ~7/255. Guarded on extents ⊄ geometry, so every SSD client keeps its exact pixels.
-
wlr_box_equalis NULL/empty-safe, soset_clip(NULL)on an unclipped tree early-returns — cheap enough for the per-commit path.
box_shadow.frag insets the solid rectangle by sigma, so only the gaussian
tail lands outside the window. Peak darkening at the border is therefore about
half of shadow_opacity — 50% is a hard ceiling even at opacity 1.0,
against Firefox's measured ~85%.
shadow_spread lifts that ceiling without patching scenefx: grow the node by
sigma + S instead of sigma, and the shader's own inset now lands S px
outside the window. Full alpha for S px, then the tail. Node corners grow
with it; a square window stays square; S = 0 is byte-identical to the old
geometry, which is why it is the default.
Measured: the 12 px band outside a window's border goes from 60.0 to 46.1
luminance at shadow_spread = 8.
A maximized or fullscreen window has no shadow, and the Win95 theme's
SYN_CHROME_BEVELchrome returns a shadow of 0. Both are deliberate.
glass_halo = 14 puts a blurred ring outside the window — the backdrop blur
extends past the frame instead of stopping at it.
The history is instructive, because the first two attempts both looked like config problems and were not.
pkgrel 181 — it only affected app-native-glass windows. The blur companion
node was built only for a translucent view, and transparency defaults to
off. So on a desktop with no transparency line in its config, the only window
with a blur node at all was Firefox, and no value of glass_halo could give an
ordinary app a ring.
If your config has no
transparencyline, most window-effect settings will appear to do nothing on most windows. Turn transparency on first.
pkgrel 182 then widened the gate to (translucent || halo > 0), so
server-decorated apps got the ring for the first time.
pkgrel 184 — the ring had a square top corner. The cause was the per-buffer construction. A decorated window is two stacked buffers, and their corner radii carry the seam rule: the titlebar rounds the top, the content rounds the bottom, and the join between them is straight. Growing those boxes outward inherits the seam — so the content buffer's ring covered the whole left edge with square top corners, and the titlebar contributed no ring at all. The seam rule is right inside the window and wrong outside it.
view_halo_update() now owns it: one wlr_scene_blur node on view->frame,
grown glass_halo px on every side, with corner_radii_all(radius + halo) and
the window's own rounded rect cut out via clipped_region so it is a pure ring.
Lowered under the shadow, with the same maximize/fullscreen suppression the
shadow has.
Two things fell out of moving it to the frame: the per-buffer path got its transparency mask back, and an opaque window no longer runs a full-window blur pass just to show a 14 px ring.
The halo is drawn above the shadow, so it blurs the spread into a smooth ramp rather than compounding it.
glass_halo = 14alone actually brightens the band (60 → 80.7) by covering the shadow; withshadow_spread = 8it lands back at 61.6, but smooth.
synui-firefox-glass toggles it. Firefox is treated as app-native glass: it
needs browser.tabs.allow_transparent_browser (FF 128+), which the helper sets.
Two rules:
-
offmust revert the prefs. Leaving them set means Firefox stays translucent with no glass behind it. - Page content is opaque by design. Only the chrome goes glass; a transparent web page would be unreadable.
synui implements org_kde_kwin_blur, so KDE apps can go glass at all — but note
that Dolphin never binds it. If you are testing KDE blur, confirm with
WAYLAND_DEBUG that the client actually binds the interface before concluding
anything about the compositor.
The display panel tells an HDR monitor from a merely 10-bit one by reading the EDID, not by asking the plane. Every 10-bit plane used to be labelled "HDR-ready", which is wrong: plenty of 10-bit panels are not HDR at all.
Real HDR10 output is blocked on scenefx, and scenefx is a fork of wlroots'
scene graph — so the work is not "wait for wlroots", it is "port or patch the
fork". wp_color_management_v1 is exported as of the 0.20 port.
Two things the night-light work established, which apply to anything touching output colour:
- Night light goes on the output state. Setting it anywhere else is ignored while window effects are on.
-
A NULL gamma LUT must be committed, or it never turns off. The witness is
modetest'sGAMMA_LUTproperty, not the compositor's own log.
Effects are easy to talk yourself into seeing. Two things make a comparison honest:
- Change one knob and restart the compositor between arms. Reloading config in place leaves other state moving, so a whole-image comparison tells you nothing.
- Compare a measurement, not an impression — a column of pixels walking outward from the window border, rather than two screenshots side by side.
The headless rig used for this, and its traps, are in Development Notes.
See also: The Desktop, synui, Troubleshooting.
Using it
- Installation
- Updating
- Software
- Files
- Keybindings
- Commands
- Nix
- Gaming
- DaVinci Resolve
- Secure Boot
- Troubleshooting
Customising it
Components
Apps
Hacking on it