Skip to content

Commit

Permalink
vswitch: move most of the implementation to the cpp file
Browse files Browse the repository at this point in the history
We don't need most of vswitch in the places where it is included.
  • Loading branch information
ammen99 committed Feb 27, 2025
1 parent 7834b75 commit acea821
Show file tree
Hide file tree
Showing 2 changed files with 310 additions and 304 deletions.
310 changes: 310 additions & 0 deletions plugins/vswitch/vswitch.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,316 @@
#include <wayfire/seat.hpp>
#include "plugins/ipc/ipc-method-repository.hpp"
#include "wayfire/plugins/common/shared-core-data.hpp"
#include "wayfire/unstable/translation-node.hpp"
#include "wayfire/scene-operations.hpp"
#include "wayfire/render-manager.hpp"

namespace wf
{
namespace vswitch
{
using namespace animation;
class workspace_animation_t : public duration_t
{
public:
using duration_t::duration_t;
timed_transition_t dx{*this};
timed_transition_t dy{*this};
};

/**
* A simple scenegraph node which draws a view at a fixed position and as an overlay over the workspace wall.
*/
class vswitch_overlay_node_t : public wf::scene::node_t
{
std::weak_ptr<wf::toplevel_view_interface_t> _view;

public:
vswitch_overlay_node_t(wayfire_toplevel_view view) : node_t(true)
{
_view = view->weak_from_this();
}

// Since we do not grab focus via a grab node, route focus to the view being rendered as an overlay
wf::keyboard_focus_node_t keyboard_refocus(wf::output_t *output)
{
if (auto view = _view.lock())
{
return view->get_transformed_node()->keyboard_refocus(output);
}

return wf::keyboard_focus_node_t{};
}

virtual void gen_render_instances(std::vector<scene::render_instance_uptr>& instances,
scene::damage_callback push_damage, wf::output_t *output = nullptr)
{
if (auto view = _view.lock())
{
view->get_transformed_node()->gen_render_instances(instances, push_damage, output);
}
}

virtual wf::geometry_t get_bounding_box()
{
if (auto view = _view.lock())
{
return view->get_transformed_node()->get_bounding_box();
}

return {0, 0, 0, 0};
}
};

/**
* Represents the action of switching workspaces with the vswitch algorithm.
*
* The workspace is actually switched at the end of the animation
*/
class workspace_switch_t
{
public:
/**
* Initialize the workspace switch process.
*
* @param output The output the workspace switch happens on.
*/
workspace_switch_t(output_t *output)
{
this->output = output;
wall = std::make_unique<workspace_wall_t>(output);
animation = workspace_animation_t{
wf::option_wrapper_t<wf::animation_description_t>{"vswitch/duration"}
};
}

/**
* Initialize switching animation.
* At this point, the calling plugin needs to have the custom renderer
* ability set.
*/
virtual void start_switch()
{
/* Setup wall */
wall->set_gap_size(gap);
wall->set_viewport(wall->get_workspace_rectangle(
output->wset()->get_current_workspace()));
wall->set_background_color(background_color);
wall->start_output_renderer();

if (overlay_view_node)
{
wf::scene::readd_front(wf::get_core().scene(), overlay_view_node);
}

output->render->add_effect(&post_render, OUTPUT_EFFECT_POST);

running = true;

/* Setup animation */
animation.dx.set(0, 0);
animation.dy.set(0, 0);
animation.start();
}

/**
* Start workspace switch animation towards the given workspace,
* and set that workspace as current.
*
* @param workspace The new target workspace.
*/
virtual void set_target_workspace(point_t workspace)
{
point_t cws = output->wset()->get_current_workspace();

animation.dx.set(animation.dx + cws.x - workspace.x, 0);
animation.dy.set(animation.dy + cws.y - workspace.y, 0);
animation.start();

std::vector<wayfire_toplevel_view> fixed_views;
if (overlay_view)
{
fixed_views.push_back(overlay_view);
}

output->wset()->set_workspace(workspace, fixed_views);
}

/**
* Set the overlay view. It will be hidden from the normal workspace layers
* and shown on top of the workspace wall. The overlay view's position is
* not animated together with the workspace transition, but its alpha is.
*
* Note: if the view disappears, the caller is responsible for resetting the
* overlay view.
*
* @param view The desired overlay view, or NULL if the overlay view needs
* to be unset.
*/
virtual void set_overlay_view(wayfire_toplevel_view view)
{
if (this->overlay_view == view)
{
/* Nothing to do */
return;
}

/* Reset old view */
if (this->overlay_view)
{
wf::scene::set_node_enabled(overlay_view->get_transformed_node(), true);
overlay_view->get_transformed_node()->rem_transformer(
vswitch_view_transformer_name);

wf::scene::remove_child(overlay_view_node);
overlay_view_node.reset();
}

/* Set new view */
this->overlay_view = view;
if (view)
{
view->get_transformed_node()->add_transformer(
std::make_shared<wf::scene::view_2d_transformer_t>(view),
wf::TRANSFORMER_2D, vswitch_view_transformer_name);
wf::scene::set_node_enabled(view->get_transformed_node(), false);

// Render as an overlay, but make sure it is translated to the local output
auto vswitch_overlay = std::make_shared<vswitch_overlay_node_t>(view);

overlay_view_node = std::make_shared<scene::translation_node_t>();
overlay_view_node->set_children_list({vswitch_overlay});
overlay_view_node->set_offset(origin(output->get_layout_geometry()));

wf::scene::add_front(wf::get_core().scene(), overlay_view_node);
}
}

/** @return the current overlay view, might be NULL. */
virtual wayfire_view get_overlay_view()
{
return this->overlay_view;
}

/**
* Called automatically when the workspace switch animation is done.
* By default, this stops the animation.
*
* @param normal_exit Whether the operation has ended because of animation
* running out, in which case the workspace and the overlay view are
* adjusted, and otherwise not.
*/
virtual void stop_switch(bool normal_exit)
{
if (normal_exit)
{
auto old_ws = output->wset()->get_current_workspace();
adjust_overlay_view_switch_done(old_ws);
}

wall->stop_output_renderer(true);
output->render->rem_effect(&post_render);
running = false;
}

virtual bool is_running() const
{
return running;
}

virtual ~workspace_switch_t()
{}

protected:
option_wrapper_t<int> gap{"vswitch/gap"};
option_wrapper_t<color_t> background_color{"vswitch/background"};
workspace_animation_t animation;

output_t *output;
std::unique_ptr<workspace_wall_t> wall;

const std::string vswitch_view_transformer_name = "vswitch-transformer";
wayfire_toplevel_view overlay_view;
std::shared_ptr<scene::translation_node_t> overlay_view_node;

bool running = false;
void update_overlay_fb()
{
if (!overlay_view)
{
return;
}

double progress = animation.progress();

auto tmanager = overlay_view->get_transformed_node();
auto tr = tmanager->get_transformer<wf::scene::view_2d_transformer_t>(
vswitch_view_transformer_name);

static constexpr double smoothing_in = 0.4;
static constexpr double smoothing_out = 0.2;
static constexpr double smoothing_amount = 0.5;

tmanager->begin_transform_update();
if (progress <= smoothing_in)
{
tr->alpha = 1.0 - (smoothing_amount / smoothing_in) * progress;
} else if (progress >= 1.0 - smoothing_out)
{
tr->alpha = 1.0 - (smoothing_amount / smoothing_out) * (1.0 - progress);
} else
{
tr->alpha = smoothing_amount;
}

tmanager->end_transform_update();
}

wf::effect_hook_t post_render = [=] ()
{
auto start = wall->get_workspace_rectangle(
output->wset()->get_current_workspace());
auto size = output->get_screen_size();
geometry_t viewport = {
(int)std::round(animation.dx * (size.width + gap) + start.x),
(int)std::round(animation.dy * (size.height + gap) + start.y),
start.width,
start.height,
};
wall->set_viewport(viewport);
update_overlay_fb();

output->render->damage_whole();
output->render->schedule_redraw();
if (!animation.running())
{
stop_switch(true);
}
};

/**
* Emit the view-change-workspace signal from the old workspace to the current
* workspace and unset the view.
*/
virtual void adjust_overlay_view_switch_done(wf::point_t old_workspace)
{
if (!overlay_view)
{
return;
}

wf::view_change_workspace_signal data;
data.view = overlay_view;
data.from = old_workspace;
data.to = output->wset()->get_current_workspace();
output->emit(&data);

set_overlay_view(nullptr);
wf::get_core().seat->refocus();
}
};
}
}

class vswitch : public wf::per_output_plugin_instance_t
{
Expand Down
Loading

0 comments on commit acea821

Please sign in to comment.