1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121
|
#include <wayfire/per-output-plugin.hpp>
#include <wayfire/output.hpp>
#include <wayfire/render.hpp>
#include <wayfire/render-manager.hpp>
#include <wayfire/util/duration.hpp>
class wayfire_zoom_screen : public wf::per_output_plugin_instance_t
{
enum class interpolation_method_t
{
LINEAR = 0,
NEAREST = 1,
};
wf::option_wrapper_t<wf::keybinding_t> modifier{"zoom/modifier"};
wf::option_wrapper_t<double> speed{"zoom/speed"};
wf::option_wrapper_t<wf::animation_description_t> smoothing_duration{"zoom/smoothing_duration"};
wf::option_wrapper_t<int> interpolation_method{"zoom/interpolation_method"};
wf::animation::simple_animation_t progression{smoothing_duration};
bool hook_set = false;
wf::plugin_activation_data_t grab_interface = {
.name = "zoom",
.capabilities = 0,
};
public:
void init() override
{
progression.set(1, 1);
output->add_axis(modifier, &axis);
}
void update_zoom_target(float delta)
{
float target = progression.end;
target -= target * delta * speed;
target = wf::clamp(target, 1.0f, 50.0f);
if (target != progression.end)
{
progression.animate(target);
if (!hook_set)
{
hook_set = true;
output->render->add_post(&render_hook);
output->render->set_redraw_always();
}
}
}
wf::axis_callback axis = [=] (wlr_pointer_axis_event *ev)
{
if (!output->can_activate_plugin(&grab_interface))
{
return false;
}
if (ev->orientation != WL_POINTER_AXIS_VERTICAL_SCROLL)
{
return false;
}
update_zoom_target(ev->delta);
return true;
};
wf::post_hook_t render_hook = [=] (wf::auxilliary_buffer_t& source,
const wf::render_buffer_t& destination)
{
auto w = destination.get_size().width;
auto h = destination.get_size().height;
auto oc = output->get_cursor_position();
double x, y;
wlr_box b = output->get_relative_geometry();
wlr_box_closest_point(&b, oc.x, oc.y, &x, &y);
/* get rotation & scale */
wlr_box box = {int(x), int(y), 1, 1};
box = output->render->get_target_framebuffer().framebuffer_box_from_geometry_box(box);
x = box.x;
y = box.y;
// Store progression once to avoid its value changing in subsequent calls, could be very tricky due to
// timing. And if we use slightly different progressions, we can get an invalid rect.
const float factor = (float)progression;
const float scale = (factor - 1) / factor;
const float x1 = x * scale;
const float y1 = y * scale;
const float tw = std::clamp(w / factor, 0.0f, w - x1);
const float th = std::clamp(h / factor, 0.0f, h - y1);
auto filter_mode = (interpolation_method == (int)interpolation_method_t::NEAREST) ?
WLR_SCALE_FILTER_NEAREST : WLR_SCALE_FILTER_BILINEAR;
destination.blit(source, {x1, y1, tw, th}, {0, 0, w, h}, filter_mode);
if (!progression.running() && (progression - 1 <= 0.01))
{
unset_hook();
}
};
void unset_hook()
{
output->render->set_redraw_always(false);
output->render->rem_post(&render_hook);
hook_set = false;
}
void fini() override
{
if (hook_set)
{
output->render->rem_post(&render_hook);
}
output->rem_binding(&axis);
}
};
DECLARE_WAYFIRE_PLUGIN(wf::per_output_plugin_t<wayfire_zoom_screen>);
|