diff --git a/metadata/pixdecor.xml b/metadata/pixdecor.xml
index 2da2bc4..5ce4729 100644
--- a/metadata/pixdecor.xml
+++ b/metadata/pixdecor.xml
@@ -5,7 +5,7 @@
<_long>A highly configurable and optionally fancy decorator plugin for wayfire.
Effects
- <_short>General
+ <_short>Window frames
+
+
+
+
+
+
+
+
+
+
+
+ <_short>General
-
-
-
-
-
-
-
<_short>Buttons
diff --git a/src/deco-effects.cpp b/src/deco-effects.cpp
index 7aa6393..f092244 100644
--- a/src/deco-effects.cpp
+++ b/src/deco-effects.cpp
@@ -33,6 +33,7 @@
#include "deco-effects.hpp"
#include "smoke-shaders.hpp"
+#include "sculpted-overlay.hpp"
namespace wf
@@ -2206,6 +2207,9 @@ void smoke_t::create_programs()
if (std::string(overlay_engine) == "rounded_corners")
{
setup_shader(&render_overlay_program, rounded_corner_overlay);
+ } else if (std::string(overlay_engine) == "sculpted")
+ {
+ setup_shader(&render_overlay_program, sculpted_overlay);
} else if (std::string(overlay_engine) == "beveled_glass")
{
setup_shader(&render_overlay_program, beveled_glass_overlay);
@@ -2570,7 +2574,7 @@ void smoke_t::step_effect(const wf::scene::render_instruction_t& data, wf::geome
GL_CALL(glDeleteFramebuffers(1, &fb));
}
- if ((std::string(overlay_engine) == "rounded_corners") ||
+ if ((uses_shadow_overlay(std::string(overlay_engine))) ||
(std::string(overlay_engine) == "beveled_glass"))
{
GL_CALL(glUseProgram(render_overlay_program));
@@ -2579,13 +2583,20 @@ void smoke_t::step_effect(const wf::scene::render_instruction_t& data, wf::geome
GL_CALL(glUniform1i(5, rectangle.width));
GL_CALL(glUniform1i(6, rectangle.height));
GL_CALL(glUniform1i(7, rounded_corner_radius));
- if (std::string(overlay_engine) == "rounded_corners")
+ if (uses_shadow_overlay(std::string(overlay_engine)))
{
GLfloat shadow_color_f[4] =
{GLfloat(shadow_color.r), GLfloat(shadow_color.g),
GLfloat(shadow_color.b), GLfloat(shadow_color.a)};
GL_CALL(glUniform1i(8, radius));
GL_CALL(glUniform4fv(9, 1, shadow_color_f));
+ if (std::string(overlay_engine) == "sculpted")
+ {
+ wf::color_t rim = pinstripe_color;
+ GLfloat rim_f[4] = {GLfloat(rim.r), GLfloat(rim.g), GLfloat(rim.b), GLfloat(rim.a)};
+ GL_CALL(glUniform1f(10, border_depth));
+ GL_CALL(glUniform4fv(11, 1, rim_f));
+ }
}
GL_CALL(glDispatchCompute(round_up_div(rectangle.width, 16), round_up_div(rectangle.height, 16),
diff --git a/src/deco-effects.hpp b/src/deco-effects.hpp
index 94fa248..95e97fb 100644
--- a/src/deco-effects.hpp
+++ b/src/deco-effects.hpp
@@ -10,6 +10,11 @@ namespace wf
{
namespace pixdecor
{
+inline bool uses_shadow_overlay(const std::string& engine)
+{
+ return engine == "rounded_corners" || engine == "sculpted";
+}
+
class smoke_t
{
/** background effects */
@@ -25,6 +30,8 @@ class smoke_t
wf::option_wrapper_t effect_type{"pixdecor/effect_type"};
wf::option_wrapper_t overlay_engine{"pixdecor/overlay_engine"};
wf::option_wrapper_t effect_animate{"pixdecor/animate"};
+ wf::option_wrapper_t border_depth{"pixdecor/border_depth"};
+ wf::option_wrapper_t pinstripe_color{"pixdecor/pinstripe_color"};
wf::option_wrapper_t rounded_corner_radius{"pixdecor/rounded_corner_radius"};
public:
diff --git a/src/deco-layout.cpp b/src/deco-layout.cpp
index 394ba37..47b2c7b 100644
--- a/src/deco-layout.cpp
+++ b/src/deco-layout.cpp
@@ -242,7 +242,7 @@ void pixdecor_layout_t::resize(int width, int height)
wf::option_wrapper_t shadow_radius{"pixdecor/shadow_radius"};
wf::option_wrapper_t overlay_engine{"pixdecor/overlay_engine"};
wf::option_wrapper_t maximized_borders{"pixdecor/maximized_borders"};
- bool rounded_corners = std::string(overlay_engine) == "rounded_corners";
+ bool rounded_corners = uses_shadow_overlay(std::string(overlay_engine));
double border = theme.get_border_size();
double radius = (rounded_corners && !maximized) ? int(shadow_radius) : 0;
@@ -627,7 +627,7 @@ uint32_t pixdecor_layout_t::calculate_resize_edges() const
{
wf::option_wrapper_t shadow_radius{"pixdecor/shadow_radius"};
wf::option_wrapper_t overlay_engine{"pixdecor/overlay_engine"};
- int radius = (std::string(overlay_engine) == "rounded_corners") ? int(shadow_radius) : 0;
+ int radius = (uses_shadow_overlay(std::string(overlay_engine))) ? int(shadow_radius) : 0;
uint32_t edges = 0;
for (auto& area : layout_areas)
{
diff --git a/src/deco-subsurface.cpp b/src/deco-subsurface.cpp
index 5a51dee..f626204 100644
--- a/src/deco-subsurface.cpp
+++ b/src/deco-subsurface.cpp
@@ -6,6 +6,8 @@
#include "wayfire/signal-provider.hpp"
#include "wayfire/toplevel.hpp"
#include
+#include
+#include
#define GLM_FORCE_RADIANS
#include
@@ -35,6 +37,7 @@ namespace pixdecor
{
wf::option_wrapper_t effect_color{"pixdecor/effect_color"};
wf::option_wrapper_t shadow_radius{"pixdecor/shadow_radius"};
+wf::option_wrapper_t rounded_corner_radius{"pixdecor/rounded_corner_radius"};
wf::option_wrapper_t titlebar_opt{"pixdecor/titlebar"};
wf::option_wrapper_t csd_titlebar_height{"pixdecor/csd_titlebar_height"};
wf::option_wrapper_t enable_shade{"pixdecor/enable_shade"};
@@ -208,7 +211,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in
}
/* Draw title & buttons */
- auto title_border = border + ((std::string(overlay_engine) == "rounded_corners" &&
+ auto title_border = border + ((uses_shadow_overlay(std::string(overlay_engine)) &&
(!maximized || maximized_shadows)) ? int(shadow_radius) * 2 : 0);
for (auto item : renderables)
{
@@ -236,7 +239,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in
double border = theme.get_border_size();
double r =
- (std::string(overlay_engine) == "rounded_corners" &&
+ (uses_shadow_overlay(std::string(overlay_engine)) &&
(!maximized || (maximized && maximized_shadows))) ? int(shadow_radius) * 2 : 0;
r -= MIN_RESIZE_HANDLE_SIZE - std::min((int)border, MIN_RESIZE_HANDLE_SIZE);
wf::pointf_t local = at - wf::pointf_t{get_offset()};
@@ -249,6 +252,22 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in
if (deco_region.contains_pointf(local))
{
+ if (std::string(overlay_engine) == "sculpted")
+ {
+ const double shadow = uses_shadow_overlay(std::string(overlay_engine)) &&
+ (!maximized || maximized_shadows) ? int(shadow_radius) * 2 : 0;
+ const double w = g.width + 2 * r - 2 * shadow;
+ const double h = g.height + 2 * r - 2 * shadow;
+ const double radius = std::max(0.0, std::min({double(rounded_corner_radius),
+ w / 2, h / 2, std::floor(border / (1.0 - 1.0 / std::sqrt(2.0)))}));
+ const double x = local.x - shadow, y = local.y - shadow;
+ const double dx = std::max({radius - x, 0.0, x - (w - radius)});
+ const double dy = std::max({radius - y, 0.0, y - (h - radius)});
+ if (dx > 0 && dy > 0 && dx * dx + dy * dy > radius * radius)
+ {
+ return {};
+ }
+ }
return wf::scene::input_node_t{
.node = this,
.local_coords = local,
@@ -321,7 +340,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in
self->current_cursor_position, self->theme.get_decor_color(activated),
self->theme.get_shadow_color(activated), effect_color,
self->theme.get_title_height(), self->theme.get_border_size(),
- (std::string(overlay_engine) == "rounded_corners" && !maximized) ? shadow_radius : 0);
+ (uses_shadow_overlay(std::string(overlay_engine)) && !maximized) ? shadow_radius : 0);
}
self->render_region(data, offset);
@@ -537,7 +556,7 @@ class simple_decoration_node_t : public wf::scene::node_t, public wf::pointer_in
this->cached_region.clear();
} else
{
- int shadow_thickness = std::string(overlay_engine) == "rounded_corners" &&
+ int shadow_thickness = uses_shadow_overlay(std::string(overlay_engine)) &&
(!maximized || (maximized && maximized_shadows)) ? int(shadow_radius) * 2 : 0;
current_thickness = theme.get_border_size() + shadow_thickness;
@@ -649,7 +668,7 @@ wf::decoration_margins_t simple_decorator_t::get_margins(const wf::toplevel_stat
bool maximized = state.tiled_edges;
deco->theme.set_maximize(maximized);
- this->shadow_thickness = std::string(overlay_engine) == "rounded_corners" &&
+ this->shadow_thickness = uses_shadow_overlay(std::string(overlay_engine)) &&
(!state.tiled_edges || (state.tiled_edges && maximized_shadows)) ? int(shadow_radius) * 2 : 0;
double thickness = deco->theme.get_border_size() + this->shadow_thickness;
diff --git a/src/decoration.cpp b/src/decoration.cpp
index e5f3ec2..0ceb16f 100644
--- a/src/decoration.cpp
+++ b/src/decoration.cpp
@@ -19,6 +19,8 @@
#include
#include
#include "shade.hpp"
+#include "deco-effects.hpp"
+#include
namespace wf
{
@@ -57,6 +59,8 @@ class wayfire_pixdecor : public wf::plugin_interface_t
wf::option_wrapper_t effect_type{"pixdecor/effect_type"};
wf::option_wrapper_t overlay_engine{"pixdecor/overlay_engine"};
wf::option_wrapper_t effect_animate{"pixdecor/animate"};
+ wf::option_wrapper_t border_depth;
+ wf::option_wrapper_t pinstripe_color;
wf::option_wrapper_t rounded_corner_radius{"pixdecor/rounded_corner_radius"};
wf::option_wrapper_t shadow_radius{"pixdecor/shadow_radius"};
wf::option_wrapper_t shadow_color{"pixdecor/shadow_color"};
@@ -222,6 +226,23 @@ class wayfire_pixdecor : public wf::plugin_interface_t
void init() override
{
+ // Promote new options when replacing a module in an older resident core.
+ auto section = wf::get_core().config->get_section("pixdecor");
+ auto promote = [&] (const std::string& name, auto value)
+ {
+ using T = decltype(value);
+ auto previous = section->get_option_or(name);
+ if (!std::dynamic_pointer_cast>(previous))
+ {
+ auto option = std::make_shared>(name, value);
+ if (previous) option->set_value_str(previous->get_value_str());
+ section->register_new_option(option);
+ }
+ };
+ promote("border_depth", 0.65);
+ promote("pinstripe_color", wf::color_t{90.0 / 255, 113.0 / 255, 129.0 / 255, 36.0 / 255});
+ border_depth.load_option("pixdecor/border_depth");
+ pinstripe_color.load_option("pixdecor/pinstripe_color");
auto& ol = wf::get_core().output_layout;
ol->connect(&on_output_added);
ol->connect(&on_output_removed);
@@ -427,7 +448,7 @@ class wayfire_pixdecor : public wf::plugin_interface_t
title_font.set_callback([=] {recreate_frames();});
shadow_radius.set_callback([=]
{
- option_changed_cb(false, (std::string(overlay_engine) == "rounded_corners"));
+ option_changed_cb(false, (uses_shadow_overlay(std::string(overlay_engine))));
});
shadow_color.set_callback([=]
{
@@ -455,6 +476,21 @@ class wayfire_pixdecor : public wf::plugin_interface_t
view->damage();
}
});
+ auto repaint_frames = [=] ()
+ {
+ for (auto& view : wf::get_core().get_all_views())
+ {
+ if (auto toplevel = wf::toplevel_cast(view))
+ {
+ if (toplevel->toplevel()->get_data())
+ {
+ view->damage();
+ }
+ }
+ }
+ };
+ border_depth.set_callback(repaint_frames);
+ pinstripe_color.set_callback(repaint_frames);
rounded_corner_radius.set_callback([=]
{
for (auto& view : wf::get_core().get_all_views())
@@ -629,7 +665,7 @@ class wayfire_pixdecor : public wf::plugin_interface_t
continue;
}
- if (std::string(overlay_engine) == "rounded_corners")
+ if (uses_shadow_overlay(std::string(overlay_engine)))
{
pending.margins =
{double(shadow_radius * 2), double(shadow_radius * 2),
diff --git a/src/meson.build b/src/meson.build
index 000ce95..66f1573 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,5 +1,6 @@
pixdecor = shared_module('pixdecor',
['decoration.cpp', 'deco-subsurface.cpp', 'deco-button.cpp', 'deco-layout.cpp', 'deco-theme.cpp', 'deco-effects.cpp'],
dependencies: [wayfire],
+ link_args: ['-Wl,-Bsymbolic'],
install: true,
install_dir: join_paths(get_option('libdir'), 'wayfire'))
diff --git a/src/sculpted-overlay.hpp b/src/sculpted-overlay.hpp
new file mode 100644
index 0000000..99f0568
--- /dev/null
+++ b/src/sculpted-overlay.hpp
@@ -0,0 +1,84 @@
+#pragma once
+
+namespace wf::pixdecor
+{
+// Static shell material, composed over the selected border effect. Coordinates
+// and shadow margins follow Pixdecor's existing rounded-corner overlay.
+inline constexpr const char *sculpted_overlay = R"(
+#version 320 es
+precision highp float;
+layout(binding = 0, rgba32f) readonly uniform highp image2D in_frame;
+layout(binding = 0, rgba32f) writeonly uniform highp image2D out_frame;
+layout(local_size_x = 16, local_size_y = 16) in;
+layout(location = 1) uniform int title_height;
+layout(location = 2) uniform int border_size;
+layout(location = 5) uniform int width;
+layout(location = 6) uniform int height;
+layout(location = 7) uniform int corner_radius;
+layout(location = 8) uniform int shadow_radius;
+layout(location = 9) uniform vec4 shadow_color;
+layout(location = 10) uniform float border_depth;
+layout(location = 11) uniform vec4 pinstripe_color;
+
+vec4 over(vec4 base, vec4 tint)
+{
+ return vec4(tint.rgb * tint.a + base.rgb * (1.0 - tint.a),
+ tint.a + base.a * (1.0 - tint.a));
+}
+
+void main()
+{
+ ivec2 pos = ivec2(gl_GlobalInvocationID.xy);
+ if (pos.x >= width || pos.y >= height) return;
+ // Application pixels are owned by the child surface, never this overlay.
+ if (pos.x >= border_size && pos.x < width - border_size &&
+ pos.y >= title_height && pos.y < height - border_size) return;
+
+ float margin = 2.0 * float(shadow_radius);
+ float border = max(0.0, float(border_size) - margin);
+ vec2 lo = vec2(margin), hi = vec2(width, height) - lo;
+ vec2 halfsize = max(vec2(0.0), (hi - lo) * 0.5);
+ // Keep a square application's inner corners inside the rounded frame.
+ float radius = clamp(float(corner_radius), 0.0,
+ min(min(halfsize.x, halfsize.y), floor(border / (1.0 - inversesqrt(2.0)))));
+ vec2 p = vec2(pos) + 0.5;
+ vec2 center = (lo + hi) * 0.5;
+ vec2 q = abs(p - center) - (halfsize - radius);
+ float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - radius;
+ float coverage = clamp(0.5 - d, 0.0, 1.0);
+ vec4 shadow = vec4(0.0);
+ if (shadow_radius > 0)
+ {
+ float a = shadow_color.a * exp(-pow(max(d, 0.0) / float(shadow_radius), 2.0));
+ shadow = vec4(shadow_color.rgb * a, a);
+ }
+ if (coverage == 0.0)
+ {
+ imageStore(out_frame, pos, shadow);
+ return;
+ }
+
+ vec4 body = imageLoad(in_frame, pos);
+ body.rgb *= body.a;
+ float inset = max(0.0, -d);
+ float strength = clamp(border_depth, 0.0, 1.0) / 0.65;
+ float falloff = border > 0.0 ? clamp(1.0 - inset / border, 0.0, 1.0) : 0.0;
+ vec2 outward = p - clamp(p, lo + radius, hi - radius);
+ if (dot(outward, outward) < 0.0001)
+ {
+ vec2 edges = min(p - lo, hi - p);
+ outward = edges.x < edges.y ? vec2(p.x < center.x ? -1.0 : 1.0, 0.0) :
+ vec2(0.0, p.y < center.y ? -1.0 : 1.0);
+ }
+ outward = normalize(outward);
+ float light = max(-outward.y, 0.0) * 0.170 + max(-outward.x, 0.0) * 0.045;
+ float shade = max(outward.y, 0.0) * 0.500 + max(outward.x, 0.0) * 0.160;
+ body = over(body, vec4(1.0, 1.0, 1.0, clamp(light * strength * falloff, 0.0, 1.0)));
+ body = over(body, vec4(0.0, 0.0, 0.0, clamp(shade * strength * falloff, 0.0, 1.0)));
+ vec4 rim = pinstripe_color;
+ rim.a *= 1.0 - smoothstep(0.5, 1.5, inset);
+ body = over(body, rim);
+ imageStore(out_frame, pos, body * coverage + shadow * (1.0 - coverage));
+}
+)";
+}