--- a/src/force-fullscreen.cpp +++ b/src/force-fullscreen.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,9 @@ namespace scene { namespace force_fullscreen +{ +// Keep reloads independent of older libraries retained by the plugin loader. +namespace { class simple_node_render_instance_t : public render_instance_t { @@ -102,7 +106,9 @@ bbox.y += 1; bbox.width -= 2; bbox.height -= 2; - scissor_region ^= wf::regionf_t{bbox}; + // Subtract only the damaged intersection; XOR with the entire + // hole would introduce damage outside the scoped backdrop. + scissor_region ^= (scissor_region & bbox); } data.pass->clear(scissor_region, {0, 0, 0, 1}); @@ -149,7 +155,6 @@ class fullscreen_background { public: - wf::geometry_t saved_geometry; wf::geometry_t undecorated_geometry; std::shared_ptr transformer; std::shared_ptr black_border_node; @@ -199,31 +204,25 @@ output->connect(&viewport_changed); } - wf::signal::connection_t viewport_changed{[this] (wf:: - workspace_changed_signal* - ev) - { - auto og = output->get_relative_geometry(); - auto nvp = ev->new_viewport; - - for (auto& b : backgrounds) - { - int w = (og.width - b.second->transformed_view_box.width) / 2.0f; - auto ws = output->wset()->get_view_main_workspace(b.first); - auto offset = ws - nvp; - int x = offset.x * og.width; - int y = offset.y * og.height; - b.second->transformed_view_box.x = x + w; - b.second->black_border_node->geometry.x = x; - b.second->black_border_node->geometry.y = y; - b.second->black_border_node->geometry.width = og.width; - b.second->black_border_node->geometry.height = og.height; - b.second->transformed_view_box.y = b.second->black_border_node->geometry.y = - y; - } - - output->render->damage_whole(); - } + // Window geometry and these regions are output-local. Preserve the view's + // real position so monitor selection and restoration do not depend on an + // artificial move to the logical output's origin. + wf::geometry_t scope_geometry(wayfire_toplevel_view view) + { + auto box = wf::window_scope_fullscreen(view); + auto size = output->get_relative_geometry(); + auto offset = output->wset()->get_view_main_workspace(view) - + output->wset()->get_current_workspace(); + box.x += offset.x * size.width; + box.y += offset.y * size.height; + return box; + } + + wf::signal::connection_t viewport_changed = + [this] (wf::workspace_changed_signal*) + { + update_backgrounds(); + output->render->damage_whole(); }; void ensure_subsurface(wayfire_toplevel_view view, wf::geometry_t transformed_view_box) @@ -245,9 +244,9 @@ return; } - auto og = output->get_relative_geometry(); + auto og = scope_geometry(view); background->black_border_node = std::make_shared( - view, 0, 0, og.width, og.height, transformed_view_box); + view, og.x, og.y, og.width, og.height, transformed_view_box); wf::scene::add_back(view->get_root_node(), background->black_border_node); background->black_border = true; @@ -274,13 +273,17 @@ void setup_transform(wayfire_toplevel_view view) { - auto og = output->get_relative_geometry(); + auto og = scope_geometry(view); auto vg = view->get_geometry(); + if (vg.width <= 0 || vg.height <= 0 || og.width <= 0 || og.height <= 0) + { + return; + } double scale_x = (double)og.width / vg.width; double scale_y = (double)og.height / vg.height; - double translation_x = (og.width - vg.width) / 2.0; - double translation_y = (og.height - vg.height) / 2.0; + double translation_x = og.x + (og.width - vg.width) / 2.0 - vg.x; + double translation_y = og.y + (og.height - vg.height) / 2.0 - vg.y; if (preserve_aspect) { @@ -288,21 +291,15 @@ } wf::geometry_t box; - box.width = std::floor(vg.width * scale_x); - box.height = std::floor(vg.height * scale_y); - box.x = std::ceil((og.width - box.width) / 2.0); - box.y = std::ceil((og.height - box.height) / 2.0); + box.width = vg.width * scale_x; + box.height = vg.height * scale_y; + box.x = og.x + (og.width - box.width) / 2.0; + box.y = og.y + (og.height - box.height) / 2.0; destroy_subsurface(view); if (!transparent_behind_views || preserve_aspect) { ensure_subsurface(view, box); - } - - if (preserve_aspect) - { - scale_x += 1.0 / vg.width; - translation_x -= 1.0; } backgrounds[view]->transformed_view_box = box; @@ -318,7 +315,6 @@ { for (auto& b : backgrounds) { - destroy_subsurface(b.first); setup_transform(b.first); } } @@ -329,8 +325,6 @@ { return false; } - - wf::geometry_t saved_geometry = view->get_geometry(); auto background = backgrounds.find(view); bool fullscreen = background == backgrounds.end() ? true : false; @@ -359,7 +353,6 @@ } background->second->undecorated_geometry = undecorated_geometry; - background->second->saved_geometry = saved_geometry; setup_transform(view); @@ -380,7 +373,6 @@ void activate(wayfire_toplevel_view view) { - view->move(0, 0); backgrounds[view] = std::make_unique(view); backgrounds[view]->transformer = std::make_shared(view); @@ -419,10 +411,6 @@ view_focused.disconnect(); } - view->move( - background->second->saved_geometry.x, - background->second->saved_geometry.y); - if (view->get_transformed_node()->get_transformer(background_name)) { view->get_transformed_node()->rem_transformer( @@ -511,7 +499,9 @@ if (std::string(constraint_area) == "output") { - box = og; + box = scope_geometry(b.first); + box.x += og.x; + box.y += og.y; } if ((b.first == view) && @@ -548,8 +538,8 @@ } }; - wf::signal::connection_t view_output_changed{[this] (wf:: - view_pre_moved_to_wset_signal + wf::signal::connection_t view_output_changed{[this] (wf:: + view_moved_to_wset_signal *ev) { auto view = ev->view; @@ -560,15 +550,18 @@ return; } - if (!ev->new_wset->get_attached_output()) - { - return; - } - - toggle_fullscreen(view); - - auto instance = wayfire_force_fullscreen_instances[ev->new_wset->get_attached_output()]; - instance->toggle_fullscreen(view); + auto target = ev->new_wset ? ev->new_wset->get_attached_output() : nullptr; + + // The move has completed, so scope resolution sees the new output. + view->toplevel()->pending().fullscreen = false; + wf::get_core().tx_manager->schedule_object(view->toplevel()); + deactivate(view); + + auto instance = wayfire_force_fullscreen_instances.find(target); + if (instance != wayfire_force_fullscreen_instances.end()) + { + instance->second->toggle_fullscreen(view); + } } }; @@ -590,7 +583,11 @@ return; } - toggle_fullscreen(view); + // A disappearing client must be cleaned up even while actions + // are inhibited by a lock screen or another plugin. + view->toplevel()->pending().fullscreen = false; + wf::get_core().tx_manager->schedule_object(view->toplevel()); + deactivate(view); } }; @@ -606,8 +603,17 @@ return; } - if (ev->state || ev->carried_out) - { + if (ev->carried_out) + { + return; + } + + if (ev->state) + { + // Scope/configuration reflow must not turn this transformed + // window into native fullscreen and overwrite its real size. + setup_transform(view); + ev->carried_out = true; return; } @@ -661,6 +667,8 @@ } }; +} + DECLARE_WAYFIRE_PLUGIN(wf::per_output_plugin_t); } }