git.lucas.co / cce-compositor
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 }