1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
|
--- a/plugins/cube/cube.cpp
+++ b/plugins/cube/cube.cpp
@@ -219,7 +219,9 @@
output_background->exclude_desktop_environment = self->cube->rotate_layer_shell;
auto push_background_damage = [=] (const wf::regionf_t& damage)
{
- push_damage(self->get_bounding_box());
+ // An opaque effect or hidden image owns these pixels.
+ if (double(self->cube->backdrop_image_opacity) > 0 && !self->cube->warp_visible())
+ push_damage(self->get_bounding_box());
};
output_background->gen_render_instances(output_background_instances,
push_background_damage, self->cube->output, false);
@@ -270,10 +272,13 @@
wf::regionf_t background_damage = our_damage;
auto background_target =
target.translated(-wf::origin(self->get_bounding_box()));
- for (auto& instance : output_background_instances)
+ if (double(self->cube->backdrop_image_opacity) > 0 && !self->cube->warp_visible())
{
- instance->schedule_instructions(
- instructions, background_target, background_damage);
+ for (auto& instance : output_background_instances)
+ {
+ instance->schedule_instructions(
+ instructions, background_target, background_damage);
+ }
}
damage ^= bbox;
@@ -448,6 +453,8 @@
wf::wl_timer<true> warp_timer;
wf::option_wrapper_t<std::string> background_mode{"cube/background_mode"};
+ wf::option_wrapper_t<double> backdrop_image_opacity;
+ wf::option_wrapper_t<wf::color_t> backdrop_color{"cube/background"};
bool warp_visible() const
{
@@ -637,6 +644,10 @@
}
mouse_overview.load_option("cube/mouse_overview");
render_at_rest.load_option("cube/render_at_rest");
+ ensure_warp_option<double>("backdrop_image_opacity", 1.0);
+ backdrop_image_opacity.load_option("cube/backdrop_image_opacity");
+ backdrop_image_opacity.set_callback([this] { output->render->damage_whole(); });
+ backdrop_color.set_callback([this] { output->render->damage_whole(); });
ensure_warp_option<double>("warp_speed", 0.8);
ensure_warp_option<double>("warp_density", 1.0);
ensure_warp_option<double>("warp_streak_length", 0.6);
@@ -1591,9 +1602,28 @@
load_program();
}
- /* The fixed output background is scheduled behind the cube. */
- if (warp_visible()) warp_background->render(data.target, data.damage,
- wf::dimensions(output->get_relative_geometry()));
+ // Fill the pixels withheld from the wallpaper pass before drawing
+ // the faces. Never fade windows or foreground desktop surfaces.
+ const float opacity = warp_visible() ? 1.0f :
+ 1.0f - std::clamp(double(backdrop_image_opacity), 0.0, 1.0);
+ if (opacity > 0 && std::string(background_mode) == "warp")
+ {
+ warp_background->render(data.target, data.damage,
+ wf::dimensions(output->get_relative_geometry()), opacity);
+ } else if (opacity > 0)
+ {
+ auto color = wf::color_t(backdrop_color);
+ color.a = opacity;
+ color.r *= opacity; color.g *= opacity; color.b *= opacity;
+ GL_CALL(glDisable(GL_DEPTH_TEST));
+ GL_CALL(glDisable(GL_CULL_FACE));
+ wf::gles::for_each_scissor_rect(data.target, data.damage, [&]
+ {
+ OpenGL::render_rectangle(output->get_relative_geometry(), color,
+ wf::gles::render_target_orthographic_projection(data.target));
+ });
+ GL_CALL(glDisable(GL_BLEND));
+ }
program.use(wf::TEXTURE_TYPE_RGBA);
GL_CALL(glEnable(GL_DEPTH_TEST));
--- a/plugins/cube/warp-background.hpp
+++ b/plugins/cube/warp-background.hpp
@@ -7,7 +7,7 @@
// A fixed camera shared by the whole logical output, including stitched
// monitors. Particle seeds are stable; only their depth advances each frame.
-class wf_cube_warp_background
+class __attribute__((visibility("hidden"))) wf_cube_warp_background
{
wf::option_wrapper_t<double> speed{"cube/warp_speed"};
wf::option_wrapper_t<double> density{"cube/warp_density"};
@@ -36,5 +36,5 @@
bool animated() const;
unsigned frame_interval() const;
void render(const wf::render_target_t& target, const wf::regionf_t& damage,
- wf::dimensions_t resolution);
+ wf::dimensions_t resolution, float opacity = 1.0f);
};
--- a/plugins/cube/warp-background.cpp
+++ b/plugins/cube/warp-background.cpp
@@ -98,7 +98,7 @@
}
void wf_cube_warp_background::render(const wf::render_target_t& target, const wf::regionf_t& damage,
- wf::dimensions_t resolution)
+ wf::dimensions_t resolution, float opacity)
{
const auto now = std::chrono::steady_clock::now();
if (active)
@@ -126,6 +126,7 @@
program.uniform1f("orientation", orientation);
program.uniform1f("twinkle", twinkle);
program.uniform1f("ambience", ambience);
+ program.uniform1f("backdrop_opacity", opacity);
program.uniform1f("brightness", std::clamp(double(brightness), 0.0, 2.0));
program.uniform1f("glow", std::clamp(double(glow), 0.0, 2.0));
program.uniform1f("twist", std::clamp(double(twist), -2.0, 2.0));
@@ -133,13 +134,21 @@
};
GL_CALL(glDisable(GL_DEPTH_TEST));
GL_CALL(glDisable(GL_CULL_FACE));
- GL_CALL(glDisable(GL_BLEND));
+ if (opacity < 1.0f)
+ {
+ GL_CALL(glEnable(GL_BLEND));
+ GL_CALL(glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA));
+ } else
+ {
+ GL_CALL(glDisable(GL_BLEND));
+ }
tunnel_program.use(wf::TEXTURE_TYPE_RGBA);
uniforms(tunnel_program);
tunnel_program.uniform1f("ring_strength", std::clamp(double(rings), 0.0, 1.0));
tunnel_program.attrib_pointer("position", 2, 0, quad);
wf::gles::for_each_scissor_rect(target, damage, [&] { GL_CALL(glDrawArrays(GL_TRIANGLES, 0, 6)); });
tunnel_program.deactivate();
+ GL_CALL(glDisable(GL_BLEND));
if (!star_count) return;
star_program.use(wf::TEXTURE_TYPE_RGBA);
uniforms(star_program);
--- a/plugins/cube/warp-shaders.tpp
+++ b/plugins/cube/warp-shaders.tpp
@@ -18,6 +18,7 @@
uniform vec2 resolution;
uniform float travel, brightness, glow, twist, ring_strength, orientation, ambience;
uniform vec3 tint;
+uniform float backdrop_opacity;
varying vec2 uv;
float hash(vec2 p)
@@ -51,7 +52,7 @@
vec3 color = vec3(0.0015, 0.003, 0.009);
color += tint * haze * 0.021 * glow;
color += tint * ring_strength * (ring * 0.20 + filaments * centre_fade * 0.013);
- gl_FragColor = vec4(color * brightness, 1.0);
+ gl_FragColor = vec4(color * brightness * backdrop_opacity, backdrop_opacity);
}
)";
@@ -98,7 +99,7 @@
static const char *warp_star_fragment = R"(
#version 100
precision highp float;
-uniform float glow;
+uniform float glow, backdrop_opacity;
varying vec2 local;
varying float trail_length, radius, luminosity;
varying vec3 star_color;
@@ -114,6 +115,6 @@
float edge = 1.0 - smoothstep(0.75, 1.0, distance2);
vec3 color = star_color * luminosity * (core + trail + halo) * edge;
// Additive RGB preserves the opaque backdrop's alpha.
- gl_FragColor = vec4(color, 0.0);
+ gl_FragColor = vec4(color * backdrop_opacity, 0.0);
}
)";
--- a/metadata/cube.xml
+++ b/metadata/cube.xml
@@ -177,9 +177,17 @@
<_long>Sets the background color for the simple background mode.</_long>
<default>0.1 0.1 0.1 1.0</default>
</option>
+ <option name="backdrop_image_opacity" type="double">
+ <_short>Backdrop image opacity</_short>
+ <_long>Opacity of the fixed desktop image behind Cube. Set to zero to remove the image, or use a value between zero and one to fade it into Cube's background. Windows and panels keep their opacity. Warp replaces the image during movement and remains still behind the resting Cube when the image is hidden.</_long>
+ <default>1.0</default>
+ <min>0</min>
+ <max>1</max>
+ <precision>0.05</precision>
+ </option>
<group>
<_short>Warp tunnel</_short>
- <_long>Choose Warp tunnel in Background mode to fly through a starfield behind Cube. The viewpoint spans joined monitors. It animates during mouse and keyboard Cube movement and returns to the normal desktop background at rest.</_long>
+ <_long>Choose Warp tunnel in Background mode to fly through a starfield behind Cube. The viewpoint spans joined monitors. It animates during mouse and keyboard Cube movement. Backdrop image opacity controls whether the resting Cube shows the desktop image, a still tunnel, or a blend of both.</_long>
<option name="warp_speed" type="double">
<_short>Travel speed</_short>
<_long>Forward travel speed. Set to zero to pause the stars and tunnel.</_long>
|