Fix rounded corners

they were being dumb
This commit is contained in:
2026-07-02 23:00:13 -04:00
parent 8478762828
commit d3999a9d3c
6 changed files with 115 additions and 99 deletions
+1 -2
View File
@@ -58,8 +58,7 @@ monitors = {
--
effects = {
windows = {
rounded = false,
rounding = "none", -- off, light, or heavy
corner_radius = 0, -- 0 = off, or a pixel value like 6, 10, 14
shadows = {
shadows = true,
+1 -1
View File
@@ -25,7 +25,7 @@ typedef struct Client {
Monitor *mon;
struct wlr_scene_tree *scene;
struct wlr_scene_rect *border[4]; /* top, bottom, left, right */
struct wlr_scene_rect *border_bg; /* single border rect with clipped content hole */
struct wlr_scene_tree *scene_surface;
struct wl_list link;
struct wl_list flink;
+4 -10
View File
@@ -655,8 +655,7 @@ static void
parse_effects(lua_State *L, CfgEffects *e)
{
/* Defaults */
e->windows.rounded = false;
strcpy(e->windows.rounding, "off");
e->windows.corner_radius = 0;
lua_getglobal(L, "effects");
if (!lua_istable(L, -1)) {
@@ -670,14 +669,9 @@ parse_effects(lua_State *L, CfgEffects *e)
return;
}
e->windows.rounded = lua_get_bool(L, "rounded", false);
char scratch[CFG_MAX_STRLEN] = {0};
lua_get_string(L, "rounding", scratch, sizeof(scratch));
if (!strcmp(scratch, "light") || !strcmp(scratch, "heavy"))
strncpy(e->windows.rounding, scratch, CFG_MAX_STRLEN - 1);
else
strcpy(e->windows.rounding, "off");
e->windows.corner_radius = lua_get_int(L, "corner_radius", 0);
if (e->windows.corner_radius < 0)
e->windows.corner_radius = 0;
parse_shadows(L, &e->windows.shadows);
+1 -2
View File
@@ -122,8 +122,7 @@ typedef struct {
} CfgEffectsWindowShadows;
typedef struct {
bool rounded;
char rounding[CFG_MAX_STRLEN];
int corner_radius; /* 0 = off, >0 = radius in pixels */
CfgEffectsWindowShadows shadows;
} CfgEffectsWindows;
+2 -3
View File
@@ -334,9 +334,8 @@ client_send_close(Client *c)
void
client_set_border_color(Client *c, const float color[static 4])
{
int i;
for (i = 0; i < 4; i++)
wlr_scene_rect_set_color(c->border[i], color);
if (c->border_bg)
wlr_scene_rect_set_color(c->border_bg, color);
}
void
+103 -78
View File
@@ -389,13 +389,7 @@ static char config_path[1024];
static int
config_get_corner_radius(void)
{
if (!cfg.effects.windows.rounded)
return 0;
if (strcmp(cfg.effects.windows.rounding, "light") == 0)
return 6;
if (strcmp(cfg.effects.windows.rounding, "heavy") == 0)
return 14;
return 0; /* "off" or unknown */
return cfg.effects.windows.corner_radius > 0 ? cfg.effects.windows.corner_radius : 0;
}
/* Apply a workspace default layout for the given tag index (0-based) on monitor
@@ -2073,11 +2067,21 @@ static void locksession(struct wl_listener *listener, void *data) {
}
static void
set_buffer_corner_radius(struct wlr_scene_buffer *buffer,
int sx, int sy, void *user_data)
apply_surface_corners(struct wlr_scene_node *node, int radius)
{
int radius = *(int *)user_data;
wlr_scene_buffer_set_corner_radius(buffer, radius);
if (node->type == WLR_SCENE_NODE_BUFFER) {
struct wlr_scene_buffer *buffer = wlr_scene_buffer_from_node(node);
if (buffer) {
wlr_scene_buffer_set_corner_radii(buffer,
corner_radii_all(radius > 0 ? radius : 0));
}
} else if (node->type == WLR_SCENE_NODE_TREE) {
struct wlr_scene_tree *tree = wlr_scene_tree_from_node(node);
struct wlr_scene_node *child;
wl_list_for_each(child, &tree->children, link) {
apply_surface_corners(child, radius);
}
}
}
static void mapnotify(struct wl_listener *listener, void *data) {
@@ -2085,7 +2089,6 @@ static void mapnotify(struct wl_listener *listener, void *data) {
Client *p = nullptr;
Client *w, *c = wl_container_of(listener, c, map);
Monitor *m;
int i;
/* Create scene tree for this client and its border */
c->scene = client_surface(c)->data = wlr_scene_tree_create(layers[LyrTile]);
@@ -2108,23 +2111,14 @@ static void mapnotify(struct wl_listener *listener, void *data) {
goto unset_fullscreen;
}
for (i = 0; i < 4; i++) {
c->border[i] =
wlr_scene_rect_create(c->scene, 0, 0,
c->isurgent ? cfg.appearance.urgent_color
: cfg.appearance.border_color);
c->border[i]->node.data = c;
}
/* Create single border background rect */
c->border_bg = wlr_scene_rect_create(c->scene, 0, 0,
c->isurgent ? cfg.appearance.urgent_color : cfg.appearance.border_color);
c->border_bg->node.data = c;
{
int r = config_get_corner_radius();
c->corner_radius = r;
if (r > 0) {
wlr_scene_rect_set_corner_radii(c->border[0], corner_radii_top(r));
wlr_scene_rect_set_corner_radii(c->border[1], corner_radii_bottom(r));
wlr_scene_rect_set_corner_radii(c->border[2], corner_radii_left(r));
wlr_scene_rect_set_corner_radii(c->border[3], corner_radii_right(r));
}
}
/* Create drop shadow */
@@ -2146,7 +2140,7 @@ static void mapnotify(struct wl_listener *listener, void *data) {
- cfg.effects.windows.shadows.shadow_expand,
cfg.effects.windows.shadows.shadow_offset_y
- cfg.effects.windows.shadows.shadow_expand);
wlr_scene_node_place_below(&c->shadow->node, &c->border[0]->node);
wlr_scene_node_place_below(&c->shadow->node, &c->border_bg->node);
}
}
@@ -2156,10 +2150,7 @@ static void mapnotify(struct wl_listener *listener, void *data) {
: wlr_scene_subsurface_tree_create(c->scene, client_surface(c));
c->scene_surface->node.data = c;
if (c->corner_radius > 0) {
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
set_buffer_corner_radius, &c->corner_radius);
}
wlr_scene_node_raise_to_top(&c->border_bg->node);
/* Initialize client geometry with room for border */
client_set_tiled(c, WLR_EDGE_TOP | WLR_EDGE_BOTTOM | WLR_EDGE_LEFT |
@@ -2558,6 +2549,16 @@ static void rendermon(struct wl_listener *listener, void *data) {
goto skip;
}
/* Apply corner radius to all client surfaces on this monitor */
{
Client *c;
wl_list_for_each(c, &clients, link) {
if (c->mon == m && c->scene_surface && c->corner_radius > 0 && !c->isfullscreen) {
apply_surface_corners(&c->scene_surface->node, c->corner_radius);
}
}
}
wlr_scene_output_commit(m->scene_output, nullptr);
skip:
@@ -2591,7 +2592,6 @@ static void requestmonstate(struct wl_listener *listener, void *data) {
void resize(Client *c, struct wlr_box geo, int interact) {
struct wlr_box *bbox;
struct wlr_box clip;
if (!c->mon || !client_surface(c)->mapped)
return;
@@ -2602,17 +2602,51 @@ void resize(Client *c, struct wlr_box geo, int interact) {
c->geom = geo;
applybounds(c, bbox);
/* Update scene-graph, including borders */
/* Update scene-graph, including border */
wlr_scene_node_set_position(&c->scene->node, c->geom.x, c->geom.y);
wlr_scene_node_set_position(&c->scene_surface->node, c->bw, c->bw);
wlr_scene_rect_set_size(c->border[0], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[1], c->geom.width, c->bw);
wlr_scene_rect_set_size(c->border[2], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_rect_set_size(c->border[3], c->bw, c->geom.height - 2 * c->bw);
wlr_scene_node_set_position(&c->border[1]->node, 0, c->geom.height - c->bw);
wlr_scene_node_set_position(&c->border[2]->node, 0, c->bw);
wlr_scene_node_set_position(&c->border[3]->node, c->geom.width - c->bw,
c->bw);
int effective_r = c->corner_radius;
if (effective_r > 0) {
if (!c->mon->gaps)
effective_r = 0;
else if (cfg.appearance.smart_gaps && !c->isfloating
&& c->geom.x == c->mon->w.x && c->geom.y == c->mon->w.y
&& c->geom.x + c->geom.width == c->mon->w.x + c->mon->w.width
&& c->geom.y + c->geom.height == c->mon->w.y + c->mon->w.height)
effective_r = 0;
}
/* Update single border rect with clipped region hole */
if (c->border_bg) {
int bw_i = (int)c->bw;
int cw = (int)c->geom.width;
int ch = (int)c->geom.height;
wlr_scene_rect_set_size(c->border_bg, cw, ch);
wlr_scene_node_set_position(&c->border_bg->node, 0, 0);
if (effective_r > 0 && !c->isfullscreen) {
int r = effective_r;
struct clipped_region cr = {
.area = {bw_i, bw_i, cw - 2*bw_i, ch - 2*bw_i},
.corners = corner_radii_all(r),
};
wlr_scene_rect_set_clipped_region(c->border_bg, cr);
wlr_scene_rect_set_corner_radii(c->border_bg, corner_radii_all(r));
wlr_scene_node_set_enabled(&c->border_bg->node, true);
apply_surface_corners(&c->scene_surface->node, r);
} else {
struct clipped_region cr = {
.area = {bw_i, bw_i, cw - 2*bw_i, ch - 2*bw_i},
.corners = corner_radii_new(1, 1, 1, 1), /* non-zero to keep GLES2 shader clip active */
};
wlr_scene_rect_set_clipped_region(c->border_bg, cr);
wlr_scene_rect_set_corner_radius(c->border_bg, 0);
wlr_scene_node_set_enabled(&c->border_bg->node, true);
apply_surface_corners(&c->scene_surface->node, 0);
}
}
/* Update drop shadow */
if (c->shadow) {
@@ -2621,6 +2655,12 @@ void resize(Client *c, struct wlr_box geo, int interact) {
&& (!cfg.effects.windows.shadows.only_floating || c->isfloating);
if (!cfg.effects.windows.shadows.nogaps_shadows && !c->mon->gaps)
shadow_visible = false;
else if (!cfg.effects.windows.shadows.nogaps_shadows
&& cfg.appearance.smart_gaps && !c->isfloating
&& c->geom.x == c->mon->w.x && c->geom.y == c->mon->w.y
&& c->geom.x + c->geom.width == c->mon->w.x + c->mon->w.width
&& c->geom.y + c->geom.height == c->mon->w.y + c->mon->w.height)
shadow_visible = false;
wlr_scene_node_set_enabled(&c->shadow->node, shadow_visible);
if (shadow_visible) {
@@ -2631,7 +2671,7 @@ void resize(Client *c, struct wlr_box geo, int interact) {
cfg.effects.windows.shadows.shadow_offset_x - cfg.effects.windows.shadows.shadow_expand + (int)c->bw,
cfg.effects.windows.shadows.shadow_offset_y - cfg.effects.windows.shadows.shadow_expand + (int)c->bw);
int effective_r_for_shadow = c->corner_radius > 0 ? c->corner_radius : 0;
int effective_r_for_shadow = effective_r > 0 ? effective_r : c->corner_radius;
wlr_scene_shadow_set_corner_radius(c->shadow, effective_r_for_shadow);
wlr_scene_shadow_set_blur_sigma(c->shadow,
(float)cfg.effects.windows.shadows.shadow_radius);
@@ -2645,7 +2685,12 @@ void resize(Client *c, struct wlr_box geo, int interact) {
if (cfg.effects.windows.shadows.shadow_clip) {
int cr_val = effective_r_for_shadow;
struct clipped_region cr = {
.area = {(int)c->bw, (int)c->bw, (int)(c->geom.width - 2 * (int)c->bw), (int)(c->geom.height - 2 * (int)c->bw)},
.area = {
cfg.effects.windows.shadows.shadow_expand - cfg.effects.windows.shadows.shadow_offset_x,
cfg.effects.windows.shadows.shadow_expand - cfg.effects.windows.shadows.shadow_offset_y,
(int)(c->geom.width - 2 * (int)c->bw),
(int)(c->geom.height - 2 * (int)c->bw)
},
.corners = corner_radii_new(cr_val, cr_val, cr_val, cr_val),
};
wlr_scene_shadow_set_clipped_region(c->shadow, cr);
@@ -2653,19 +2698,9 @@ void resize(Client *c, struct wlr_box geo, int interact) {
}
}
if (c->corner_radius > 0 && !c->isfullscreen) {
int r = c->corner_radius;
wlr_scene_rect_set_corner_radii(c->border[0], corner_radii_top(r));
wlr_scene_rect_set_corner_radii(c->border[1], corner_radii_bottom(r));
wlr_scene_rect_set_corner_radii(c->border[2], corner_radii_left(r));
wlr_scene_rect_set_corner_radii(c->border[3], corner_radii_right(r));
}
/* this is a no-op if size hasn't changed */
c->resize =
client_set_size(c, c->geom.width - 2 * c->bw, c->geom.height - 2 * c->bw);
client_get_clip(c, &clip);
wlr_scene_subsurface_tree_set_clip(&c->scene_surface->node, &clip);
}
static void run(char *startup_cmd) {
@@ -2785,21 +2820,13 @@ static void setfullscreen(Client *c, int fullscreen) {
c->bw = fullscreen ? 0 : cfg.appearance.border_px;
if (fullscreen) {
c->corner_radius = 0;
int zero = 0;
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
set_buffer_corner_radius, &zero);
for (int i = 0; i < 4; i++)
wlr_scene_rect_set_corner_radius(c->border[i], 0);
if (c->border_bg) {
wlr_scene_node_set_enabled(&c->border_bg->node, false);
}
} else {
int r = config_get_corner_radius();
c->corner_radius = r;
if (r > 0) {
wlr_scene_rect_set_corner_radii(c->border[0], corner_radii_top(r));
wlr_scene_rect_set_corner_radii(c->border[1], corner_radii_bottom(r));
wlr_scene_rect_set_corner_radii(c->border[2], corner_radii_left(r));
wlr_scene_rect_set_corner_radii(c->border[3], corner_radii_right(r));
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
set_buffer_corner_radius, &r);
c->corner_radius = config_get_corner_radius();
if (c->border_bg) {
wlr_scene_node_set_enabled(&c->border_bg->node, true);
}
}
client_set_fullscreen(c, fullscreen);
@@ -3041,18 +3068,17 @@ reapply_client_appearance(void)
if (!client_is_unmanaged(c)) {
int r = c->isfullscreen ? 0 : config_get_corner_radius();
c->corner_radius = r;
if (c->border_bg) {
if (r > 0 && !c->isfullscreen) {
wlr_scene_rect_set_corner_radii(c->border[0], corner_radii_top(r));
wlr_scene_rect_set_corner_radii(c->border[1], corner_radii_bottom(r));
wlr_scene_rect_set_corner_radii(c->border[2], corner_radii_left(r));
wlr_scene_rect_set_corner_radii(c->border[3], corner_radii_right(r));
wlr_scene_rect_set_corner_radii(c->border_bg, corner_radii_all(r));
wlr_scene_node_set_enabled(&c->border_bg->node, true);
} else if (c->isfullscreen) {
wlr_scene_node_set_enabled(&c->border_bg->node, false);
} else {
for (int i = 0; i < 4; i++)
wlr_scene_rect_set_corner_radius(c->border[i], 0);
wlr_scene_rect_set_corner_radius(c->border_bg, 0);
wlr_scene_node_set_enabled(&c->border_bg->node, true);
}
}
int radius = r;
wlr_scene_node_for_each_buffer(&c->scene_surface->node,
set_buffer_corner_radius, &radius);
/* Shadow re-apply on config reload */
if (cfg.effects.windows.shadows.shadows) {
if (!c->shadow) {
@@ -3074,7 +3100,7 @@ reapply_client_appearance(void)
cfg.effects.windows.shadows.shadow_offset_y
- cfg.effects.windows.shadows.shadow_expand);
wlr_scene_node_place_below(&c->shadow->node,
&c->border[0]->node);
&c->border_bg->node);
}
}
} else {
@@ -3088,8 +3114,7 @@ reapply_client_appearance(void)
float *color = c->isurgent ? cfg.appearance.urgent_color
: c == focustop(c->mon) ? cfg.appearance.focus_color
: cfg.appearance.border_color;
for (int i = 0; i < 4; i++)
wlr_scene_rect_set_color(c->border[i], color);
client_set_border_color(c, color);
}
}
}