--- 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 warp_timer; wf::option_wrapper_t background_mode{"cube/background_mode"}; + wf::option_wrapper_t backdrop_image_opacity; + wf::option_wrapper_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("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("warp_speed", 0.8); ensure_warp_option("warp_density", 1.0); ensure_warp_option("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 speed{"cube/warp_speed"}; wf::option_wrapper_t 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. 0.1 0.1 0.1 1.0 + <_short>Warp tunnel - <_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>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.