Wayland compositor (wlroots)
git clone https://git.lucas.co/cce-compositor.git
scenefx/render/fx_renderer/shaders/frame.frag (4.4K)
1 // Window resize handles: EIGHT DISCS inside the window's rounded
2 // silhouette — one at the midpoint of each side and one on each corner —
3 // each disc a zone of its own. `band` is the disc diameter. The side
4 // discs are tangent to their side; a corner disc sits on the corner's 45°
5 // diagonal, tangent to the rounded corner arc when that arc is wider than
6 // the disc and tucked into the two straight edges otherwise.
7 //
8 // The compositor's `window::handle_disc_layout` lays out the same discs
9 // from the same inputs (size, corner_radius, band) for the pointer hit
10 // test and the catcher rects; the two MUST stay in step, or a handle is
11 // drawn where it does not grab, or grabs where it is not drawn.
12 //
13 // Still a shader rather than eight scene rects: a rounded scene rect takes
14 // the renderer's global corner shape, a squircle, so a "rect with radius
15 // half its size" would draw as a squircle, not a circle.
16
17 #ifdef GL_FRAGMENT_PRECISION_HIGH
18 precision highp float;
19 #else
20 precision mediump float;
21 #endif
22
23 varying vec4 v_color;
24 varying vec2 v_texcoord;
25
26 uniform vec2 position;
27 uniform vec2 size;
28 uniform float corner_radius;
29 // Disc diameter.
30 uniform float band;
31 // Retired by the disc handles and ignored; kept so the node API and its
32 // callers need not change.
33 uniform float band_min;
34 uniform float corner_len;
35 uniform float gap;
36 uniform float bulge;
37 uniform float swell_curve;
38 // Node-local rect (x, y, w, h) the handles must not draw over — the client
39 // is drawing an in-surface popover there and the menu must read as in
40 // FRONT of the chrome. w or h <= 0 disables.
41 uniform vec4 exclusion;
42 // Zone under the pointer (see ZONE_* below), or < 0 for none.
43 uniform float hovered;
44 uniform vec4 hover_color;
45
46 // Defined in corner_alpha.frag, concatenated after this source — the same
47 // shared routine bevel.frag uses, so the discs are clipped to the same
48 // superellipse silhouette every other corner cut traces.
49 float corner_dist(vec2 size, vec2 position,
50 float radius_tl, float radius_tr, float radius_bl, float radius_br);
51
52 // Zone numbering MUST match the compositor's BorderElement::index():
53 // 0 Top, 1 Bottom, 2 Left, 3 Right, 4 TopLeft, 5 TopRight, 6 BottomLeft,
54 // 7 BottomRight.
55 const float SQRT2 = 1.41421356237;
56
57 void main() {
58 // Rect-local position, TOP-DOWN: gl_FragCoord minus the box position is
59 // already y-down box-local here (the pass renders under a FLIPPED_180
60 // projection, so fragment row 0 is the top of the buffer — see the same
61 // note in droplet.frag). corner_dist flips its own copy; this must not
62 // be flipped too, or the zone labels swap vertically.
63 vec2 p = gl_FragCoord.xy - position;
64
65 // A client popover owns this rect; the handles yield to it wholesale.
66 // The rect arrives top-left-origin (y down), the same space as p.
67 if (exclusion.z > 0.0 && exclusion.w > 0.0
68 && p.x >= exclusion.x && p.x < exclusion.x + exclusion.z
69 && p.y >= exclusion.y && p.y < exclusion.y + exclusion.w) {
70 discard;
71 }
72
73 float r = 0.5 * band;
74 float t = corner_radius > r
75 ? corner_radius - (corner_radius - r) / SQRT2
76 : r;
77 vec2 c[8];
78 c[0] = vec2(0.5 * size.x, r); // Top
79 c[1] = vec2(0.5 * size.x, size.y - r); // Bottom
80 c[2] = vec2(r, 0.5 * size.y); // Left
81 c[3] = vec2(size.x - r, 0.5 * size.y); // Right
82 c[4] = vec2(t, t); // TopLeft
83 c[5] = vec2(size.x - t, t); // TopRight
84 c[6] = vec2(t, size.y - t); // BottomLeft
85 c[7] = vec2(size.x - t, size.y - t); // BottomRight
86
87 // The nearest disc owns the fragment: signed distance to its rim,
88 // negative inside.
89 float d = 1e9;
90 float zone = -1.0;
91 for (int i = 0; i < 8; i++) {
92 float di = length(p - c[i]) - r;
93 if (di < d) {
94 d = di;
95 zone = float(i);
96 }
97 }
98 // Feather the rim by a pixel, and clip to the window's own rounded
99 // silhouette (a disc never crosses it, but the feather may).
100 float sil = corner_dist(size, position,
101 corner_radius, corner_radius, corner_radius, corner_radius);
102 float aa = clamp(0.5 - d, 0.0, 1.0) * clamp(-sil, 0.0, 1.0);
103 if (aa <= 0.0) {
104 discard;
105 }
106
107 vec4 base = (hovered >= 0.0 && abs(hovered - zone) < 0.5) ? hover_color : v_color;
108 // Premultiplied, like every other rect this renderer draws.
109 gl_FragColor = base * aa;
110 }