diff --git a/plugins/cube/cube.cpp b/plugins/cube/cube.cpp index 3b4b857..813a459 100644 --- a/plugins/cube/cube.cpp +++ b/plugins/cube/cube.cpp @@ -907,13 +907,13 @@ class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer output->render->schedule_redraw(); } - void animate_workspace_delta(wf::point_t delta, + bool animate_workspace_delta(wf::point_t delta, wf::point_t anchor_workspace, wayfire_toplevel_view fixed_view) { if (!activate()) { - return; + return false; } end_mouse_overview(); @@ -957,14 +957,15 @@ class wayfire_cube : public wf::per_output_plugin_instance_t, public wf::pointer start_animation(); update_view_matrix(); output->render->schedule_redraw(); + return true; } wf::signal::connection_t on_cube_workspace_animation = [=] (cube_workspace_animation_signal *ev) { - animate_workspace_delta(ev->delta, ev->anchor_workspace, - ev->fixed_view); - ev->carried_out = true; + // Report failure so vswitch can fall back to its own animation. + ev->carried_out = animate_workspace_delta(ev->delta, + ev->anchor_workspace, ev->fixed_view); }; wf::signal::connection_t diff --git a/plugins/cube/meson.build b/plugins/cube/meson.build index 48ea276..af57b9a 100644 --- a/plugins/cube/meson.build +++ b/plugins/cube/meson.build @@ -2,5 +2,7 @@ animiate = shared_module('cube', ['cube.cpp', 'cubemap.cpp', 'skydome.cpp', 'simple-background.cpp', 'warp-background.cpp', 'hyperspace-background.cpp'], include_directories: [wayfire_api_inc, wayfire_conf_inc, plugins_common_inc, ipc_include_dirs], dependencies: [wlroots, pixman, wfconfig, json, plugin_pch_dep], + link_args: ['-Wl,--version-script=' + meson.project_source_root() / 'plugins/common/plugin-exports.map'], + link_depends: '../../plugins/common/plugin-exports.map', install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) diff --git a/plugins/grid/grid.cpp b/plugins/grid/grid.cpp index ed24b42..46bf79e 100644 --- a/plugins/grid/grid.cpp +++ b/plugins/grid/grid.cpp @@ -294,6 +294,8 @@ class wayfire_grid : public wf::plugin_interface_t, public wf::per_output_tracke public: void init() override { + wf::get_core().store_data(std::make_unique(), + wf::grid::grid_loaded_marker); init_output_tracking(); // Older metadata leaves new ini options untyped, or absent. Promote // them before loading activators, preserving any configured binding. @@ -465,6 +467,7 @@ class wayfire_grid : public wf::plugin_interface_t, public wf::per_output_tracke void fini() override { fini_output_tracking(); + wf::get_core().erase_data(wf::grid::grid_loaded_marker); } bool can_adjust_view(wayfire_toplevel_view view) diff --git a/plugins/grid/meson.build b/plugins/grid/meson.build index e107495..71c5746 100644 --- a/plugins/grid/meson.build +++ b/plugins/grid/meson.build @@ -5,6 +5,8 @@ all_deps = [wlroots, pixman, wfconfig, wftouch, cairo, json, plugin_pch_dep] shared_module('grid', ['grid.cpp'], include_directories: all_include_dirs, dependencies: all_deps, + link_args: ['-Wl,--version-script=' + meson.project_source_root() / 'plugins/common/plugin-exports.map'], + link_depends: '../../plugins/common/plugin-exports.map', install: true, install_dir: conf_data.get('PLUGIN_PATH')) diff --git a/plugins/grid/wayfire/plugins/grid.hpp b/plugins/grid/wayfire/plugins/grid.hpp index d981735..1da956e 100644 --- a/plugins/grid/wayfire/plugins/grid.hpp +++ b/plugins/grid/wayfire/plugins/grid.hpp @@ -23,6 +23,13 @@ enum move_op_t MOVE_OP_FINISH_PREVIEW = 3, }; +/** + * Core custom data name present while the Grid plugin is loaded, so that + * move-snap leaves view_tile_request_signal to Grid regardless of load order. + * Checked by name only: custom data types do not compare across plugin builds. + */ +static constexpr const char *grid_loaded_marker = "wf-grid-plugin-loaded"; + /** * name: request * on: core diff --git a/plugins/single_plugins/meson.build b/plugins/single_plugins/meson.build index f2acc10..716b824 100644 --- a/plugins/single_plugins/meson.build +++ b/plugins/single_plugins/meson.build @@ -18,11 +18,19 @@ extra_libs = { 'vswipe': [workspace_wall], } +# Export only the plugin entry points, so that live-swapped revisions do not +# bind to each other's inline/template symbols. +export_map = 'plugins/common/plugin-exports.map' +export_map_plugins = ['move'] + foreach plugin : plugins plugin_module = shared_module(plugin, plugin + '.cpp', include_directories: all_include_dirs, dependencies: all_deps, link_with: extra_libs.get(plugin, []), + link_args: plugin in export_map_plugins ? + ['-Wl,--version-script=' + meson.project_source_root() / export_map] : [], + link_depends: plugin in export_map_plugins ? '../../' + export_map : [], install: true, install_dir: conf_data.get('PLUGIN_PATH')) diff --git a/plugins/single_plugins/move-snap.hpp b/plugins/single_plugins/move-snap.hpp index ddcb70f..2c2fce5 100644 --- a/plugins/single_plugins/move-snap.hpp +++ b/plugins/single_plugins/move-snap.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include "move-snap-map.hpp" @@ -300,7 +301,8 @@ class move_snap_t : public wf::per_output_tracker_mixin_t<> // Without Grid, ordinary tiling reflows still preserve half/quarter sizes. wf::signal::connection_t on_tile = [this] (auto *ev) { - if (ev->carried_out || !eligible(ev->view) || !ev->edges) return; + if (ev->carried_out || wf::get_core().has_data(wf::grid::grid_loaded_marker)) return; + if (!eligible(ev->view) || !ev->edges) return; int slot = wf::grid::get_slot_from_tiled_edges(ev->edges); if (!slot) return; auto area = wf::window_scope_workarea(ev->view); diff --git a/plugins/tile/tile-plugin.cpp b/plugins/tile/tile-plugin.cpp index 5d66c4d..ab03b2f 100644 --- a/plugins/tile/tile-plugin.cpp +++ b/plugins/tile/tile-plugin.cpp @@ -183,10 +183,26 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom ev->carried_out = true; stop_controller(true); auto& data = tile_workspace_set_data_t::get(ev->view->get_wset()); - data.reconcile_view(ev->view, ev->workspace); + reconcile_requested_view(data, ev->view, ev->workspace); data.update_root_size(); }; + /** + * Most tile/fullscreen requests carry the output's current workspace, not + * the view's. Only an explicit window-scope reflow may move the node to + * another workspace; otherwise just reconcile its region on its own one. + */ + void reconcile_requested_view(tile_workspace_set_data_t& data, + wayfire_toplevel_view view, wf::point_t requested) + { + auto vp = view->has_data() ? + std::optional{requested} : data.workspace_of(view); + if (vp) + { + data.reconcile_view(view, *vp); + } + } + wf::signal::connection_t on_fullscreen_request = [=] (view_fullscreen_request_signal *ev) { @@ -196,8 +212,9 @@ class tile_output_plugin_t : public wf::pointer_interaction_t, public wf::custom } ev->carried_out = true; - tile_workspace_set_data_t::get(ev->view->get_wset()).reconcile_view(ev->view, ev->workspace); - tile_workspace_set_data_t::get(ev->view->get_wset()).set_view_fullscreen(ev->view, ev->state); + auto& data = tile_workspace_set_data_t::get(ev->view->get_wset()); + reconcile_requested_view(data, ev->view, ev->workspace); + data.set_view_fullscreen(ev->view, ev->state); }; void change_view_workspace(wayfire_toplevel_view view, std::optional vp = {}) diff --git a/plugins/tile/tile-wset.hpp b/plugins/tile/tile-wset.hpp index a695bc3..6a6f17a 100644 --- a/plugins/tile/tile-wset.hpp +++ b/plugins/tile/tile-wset.hpp @@ -117,8 +117,27 @@ class tile_workspace_set_data_t : public wf::custom_data_t return output->workarea->get_workarea(); } + wf::point_t clamp_workspace(wf::point_t vp) + { + auto size = wset.lock()->get_workspace_grid_size(); + return {std::clamp(vp.x, 0, size.width - 1), std::clamp(vp.y, 0, size.height - 1)}; + } + + /** The workspace whose (virtual or physical) root holds the view's node. */ + std::optional workspace_of(wayfire_toplevel_view view) + { + auto node = tile::view_node_t::get_node(view); + if (!node) return {}; + auto root = tile::get_root(node); + std::optional result; + for_each_root([&] (auto& candidate, auto vp, const auto&) + { if (candidate.get() == root.get()) result = vp; }); + return result; + } + std::unique_ptr& root_at(wf::point_t vp, const std::string& monitor = "") { + vp = clamp_workspace(vp); if (monitor.empty()) return roots[vp.x][vp.y]; auto& grid = physical_roots[monitor]; auto size = wset.lock()->get_workspace_grid_size(); @@ -166,6 +185,7 @@ class tile_workspace_set_data_t : public wf::custom_data_t { auto node = tile::view_node_t::get_node(view); if (!node) return; + vp = clamp_workspace(vp); auto& destination = root_at(vp, region_for(view)); if (tile::get_root(node).get() == destination.get()) return; autocommit_transaction_t tx; diff --git a/plugins/vswitch/vswitch.cpp b/plugins/vswitch/vswitch.cpp index 34b63be..3ad44e8 100644 --- a/plugins/vswitch/vswitch.cpp +++ b/plugins/vswitch/vswitch.cpp @@ -387,6 +387,15 @@ class vswitch : public wf::per_output_plugin_instance_t on_done(); } + /** The workspace has already changed: slide in from @old_ws. */ + void slide_from(wf::point_t old_ws) + { + auto cws = output->wset()->get_current_workspace(); + animation.dx.set(old_ws.x - cws.x, 0); + animation.dy.set(old_ws.y - cws.y, 0); + animation.start(); + } + private: std::function on_done; }; @@ -665,27 +674,36 @@ class vswitch : public wf::per_output_plugin_instance_t if (!is_active()) { + // Respect plugin exclusivity (expo, scale, ...) like upstream's + // start_switch() did. + if (!output->can_activate_plugin(&grab_interface)) + { + return false; + } + if (fixed_view) { clear_held_window_dim(); wf::vswitch::move_view(fixed_view, target_ws); output->wset()->set_workspace(target_ws); wf::get_core().seat->focus_view(view); - - cube_workspace_animation_signal ev; - ev.delta = animation_delta; - ev.anchor_workspace = cws; - ev.fixed_view = fixed_view; - output->emit(&ev); - return true; + } else + { + output->wset()->set_workspace(target_ws); } - output->wset()->set_workspace(target_ws); cube_workspace_animation_signal ev; ev.delta = animation_delta; ev.anchor_workspace = cws; - ev.fixed_view = nullptr; + ev.fixed_view = fixed_view; output->emit(&ev); + + // Without a cube to animate it, use vswitch's own slide. + if (!ev.carried_out && start_switch()) + { + algorithm->slide_from(cws); + } + return true; } @@ -695,8 +713,7 @@ class vswitch : public wf::per_output_plugin_instance_t } algorithm->set_overlay_view(toplevel_cast(view)); - algorithm->set_target_workspace( - output->wset()->get_current_workspace() + delta); + algorithm->set_target_workspace(target_ws); return true; } @@ -781,15 +798,6 @@ class vswitch : public wf::per_output_plugin_instance_t return; } - if (!ev->fixed_views.empty()) - { - cube_workspace_animation_signal cube_ev; - cube_ev.delta = ev->new_viewport - ev->old_viewport; - cube_ev.anchor_workspace = ev->old_viewport; - cube_ev.fixed_view = ev->fixed_views[0]; - output->emit(&cube_ev); - } - if (is_active()) { ev->carried_out = add_direction(ev->new_viewport - ev->old_viewport);