--- a/plugins/cube/cube.cpp +++ b/plugins/cube/cube.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -50,6 +51,103 @@ namespace { +// Cube replaces the desktop through TOP, but only selected panels rotate. +// Fixed TOP surfaces (including the dock) must remain above both the cube +// textures and any held-window image. Do not reparent their real/input nodes. +class fixed_top_layer_node_t : public wf::scene::node_t +{ + wf::output_t *output; + std::string rotating_namespace; + + class instance_t : public wf::scene::render_instance_t + { + std::shared_ptr self; + std::vector children; + + wf::regionf_t fullscreen_mask() const + { + if (auto mask = self->output->get_data()) + { + return mask->hidden; + } + + return {}; + } + + public: + instance_t(fixed_top_layer_node_t *node, wf::scene::damage_callback push_damage) : + self(std::static_pointer_cast(node->shared_from_this())) + { + auto damage = [self = this->self, push_damage] (const wf::regionf_t& region) + { + push_damage(region + wf::origin(self->output->get_layout_geometry())); + }; + for (const auto& child : self->output->node_for_layer(wf::scene::layer::TOP)->get_children()) + { + auto view = wf::node_to_view(child); + const bool rotates = view && view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT && + !self->rotating_namespace.empty() && + (self->rotating_namespace == "*" || view->get_app_id() == self->rotating_namespace); + if (child->is_enabled() && !rotates) + { + child->gen_render_instances(children, damage, self->output); + } + } + } + + void schedule_instructions(std::vector& instructions, + const wf::render_target_t& target, wf::regionf_t& damage) override + { + auto box = self->get_bounding_box(); + auto offset = wf::origin(box); + auto local_damage = (damage & box) - offset; + auto excluded = local_damage & fullscreen_mask(); + local_damage ^= excluded; + auto local_target = target.translated(-offset); + for (auto& child : children) + { + child->schedule_instructions(instructions, local_target, local_damage); + } + + local_damage |= excluded; + damage ^= box; + damage |= (local_damage + offset) & box; + } + + void compute_visibility(wf::output_t *output, wf::regionf_t& visible) override + { + auto offset = wf::origin(self->get_bounding_box()); + visible -= offset; + auto excluded = visible & fullscreen_mask(); + visible ^= excluded; + for (auto& child : children) child->compute_visibility(output, visible); + visible |= excluded; + visible += offset; + } + + void presentation_feedback(wf::output_t *output) override + { + for (auto& child : children) child->presentation_feedback(output); + } + }; + + public: + fixed_top_layer_node_t(wf::output_t *output, std::string rotating_namespace) : + node_t(false), output(output), rotating_namespace(std::move(rotating_namespace)) + {} + + wf::geometry_t get_bounding_box() override { return output->get_layout_geometry(); } + + void gen_render_instances(std::vector& instances, + wf::scene::damage_callback damage, wf::output_t *shown_on) override + { + if (shown_on == output) + { + instances.push_back(std::make_unique(this, damage)); + } + } +}; + class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer_interaction_t { class fixed_view_overlay_node_t : public wf::scene::node_t @@ -122,7 +220,7 @@ push_damage(self->get_bounding_box()); }; output_background->gen_render_instances(output_background_instances, - push_background_damage, self->cube->output); + push_background_damage, self->cube->output, false); self->connect(&on_cube_damage); @@ -269,6 +367,7 @@ std::unique_ptr input_grab; std::shared_ptr render_node; + std::shared_ptr fixed_top_node; std::shared_ptr fixed_overlay_node; wayfire_toplevel_view fixed_overlay_view = nullptr; wayfire_toplevel_view post_move_dim_view = nullptr; @@ -630,6 +729,11 @@ void add_cube_render_node() { add_desktop_render_node(render_node); + if (fixed_top_node) wf::scene::remove_child(fixed_top_node); + fixed_top_node = std::make_shared(output, rotate_layer_shell); + wf::scene::node_ptr anchor = fixed_overlay_node ? + wf::scene::node_ptr(fixed_overlay_node) : wf::scene::node_ptr(render_node); + add_desktop_render_node(fixed_top_node, anchor); } void rebuild_cube_render_node() @@ -644,6 +748,15 @@ add_cube_render_node(); } + void clear_unused_fixed_top_node() + { + if (!render_node && !fixed_overlay_node && fixed_top_node) + { + wf::scene::remove_child(fixed_top_node); + fixed_top_node = nullptr; + } + } + void pause_cube_render(bool pause) { if (pause) @@ -652,6 +765,7 @@ { wf::scene::remove_child(render_node); render_node = nullptr; + clear_unused_fixed_top_node(); cube_render_paused = true; output->render->damage_whole(); } @@ -727,6 +841,7 @@ wf::scene::remove_child(fixed_overlay_node); fixed_overlay_node = nullptr; + clear_unused_fixed_top_node(); } void clear_post_move_window_dim() @@ -745,6 +860,8 @@ fixed_overlay_node = nullptr; } + clear_unused_fixed_top_node(); + post_move_dim_view = nullptr; fixed_overlay_added_transformer = false; } @@ -838,6 +955,7 @@ fixed_workspace_view = nullptr; workspace_animation_anchor.reset(); clear_fixed_workspace_overlay(); + clear_unused_fixed_top_node(); release_cube_input(); output->deactivate_plugin(&grab_interface); --- a/plugins/vswitch/vswitch.cpp +++ b/plugins/vswitch/vswitch.cpp @@ -385,7 +385,6 @@ std::unique_ptr algorithm; std::unique_ptr bindings; wayfire_toplevel_view held_dim_view = nullptr; - std::shared_ptr held_dim_overlay_node; uint32_t held_dim_modifiers = 0; std::weak_ptr held_move_view; uint32_t held_move_modifiers = 0; @@ -504,12 +503,6 @@ wf::scene::set_node_enabled(held_dim_view->get_transformed_node(), true); } - if (held_dim_overlay_node) - { - wf::scene::remove_child(held_dim_overlay_node); - held_dim_overlay_node = nullptr; - } - held_dim_view = nullptr; held_dim_modifiers = 0; } @@ -586,19 +579,12 @@ remove_held_dim_transformers(view); view->get_transformed_node()->add_transformer(transformer, wf::TRANSFORMER_2D, held_dim_transformer_name); - wf::scene::set_node_enabled(view->get_transformed_node(), false); - - auto overlay = std::make_shared(view); - held_dim_overlay_node = - std::make_shared(); - held_dim_overlay_node->set_children_list({overlay}); - held_dim_overlay_node->set_offset(wf::origin(output->get_layout_geometry())); - wf::scene::add_front(wf::get_core().scene(), held_dim_overlay_node); - + // Modifier feedback changes opacity, not stacking. Cube owns the + // stationary carried-window image during an actual workspace move; + // a root-front copy here would paint above panels, docks and locks. held_dim_view = view; held_dim_modifiers = wf::get_core().seat->get_keyboard_modifiers(); - wf::scene::damage_node(held_dim_overlay_node, - held_dim_overlay_node->get_bounding_box()); + view->damage(); } /** --- a/src/api/wayfire/workspace-stream.hpp +++ b/src/api/wayfire/workspace-stream.hpp @@ -54,6 +54,11 @@ std::string stringify() const override; void gen_render_instances(std::vector& instances, scene::damage_callback push_damage, wf::output_t *output) override; + // Cube's fixed background omits TOP surfaces, which have their own + // foreground pass. A non-virtual overload preserves the existing node + // layout and plugin ABI. + void gen_render_instances(std::vector& instances, + scene::damage_callback push_damage, wf::output_t *output, bool include_top_layer); // The bounding box of a workspace stream is // (0, 0, output_width, output_height). wf::geometry_t get_bounding_box() override; --- a/src/output/workspace-stream.cpp +++ b/src/output/workspace-stream.cpp @@ -40,7 +40,7 @@ public: workspace_stream_instance_t(workspace_stream_node_t *self, - scene::damage_callback push_damage) + scene::damage_callback push_damage, bool include_top_layer) { this->self = self; auto translate_and_push_damage = [this, push_damage] (wf::regionf_t damage) @@ -55,6 +55,11 @@ for (auto& output_node : wf::collect_output_nodes(wf::get_core().scene(), self->output)) { + if (!include_top_layer && output_node == self->output->node_for_layer(scene::layer::TOP)) + { + continue; + } + // A workspace contains desktop content only. In particular, cube/expo // subpasses must never fold a launcher, screensaver or lock into the desktop. if (output_node == self->output->node_for_layer(scene::layer::OVERLAY) || @@ -231,8 +236,15 @@ std::vector& instances, scene::damage_callback push_damage, wf::output_t *output) { + gen_render_instances(instances, push_damage, output, true); +} + +void workspace_stream_node_t::gen_render_instances( + std::vector& instances, + scene::damage_callback push_damage, wf::output_t *output, bool include_top_layer) +{ instances.push_back(std::make_unique(this, - push_damage)); + push_damage, include_top_layer)); } std::string workspace_stream_node_t::stringify() const