1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2020 Scott Moreau
5  *
6  * Permission is hereby granted, free of charge, to any person obtaining a copy
7  * of this software and associated documentation files (the "Software"), to deal
8  * in the Software without restriction, including without limitation the rights
9  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10  * copies of the Software, and to permit persons to whom the Software is
11  * furnished to do so, subject to the following conditions:
12  *
13  * The above copyright notice and this permission notice shall be included in
14  * all copies or substantial portions of the Software.
15  *
16  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22  * SOFTWARE.
23  */
24 
25 #include <wayfire/core.hpp>
26 #include <wayfire/view.hpp>
27 #include <wayfire/plugin.hpp>
28 #include <wayfire/output.hpp>
29 #include <wayfire/render-manager.hpp>
30 #include "wayfire/view-transform.hpp"
31 #include "wayfire/workspace-manager.hpp"
32 
33 
34 class winzoom_t : public wf::view_2D
35 {
36     wf::option_wrapper_t<bool> nearest_filtering{"winzoom/nearest_filtering"};
37     wayfire_view view;
38 
39     wf::config::option_base_t::updated_callback_t filtering_changed = [=] ()
__anon5d343bc20102() 40     {
41         view->damage();
42     };
43 
44   public:
winzoom_t(wayfire_view view)45     winzoom_t(wayfire_view view) : wf::view_2D(view)
46     {
47         nearest_filtering.set_callback(filtering_changed);
48         this->view = view;
49     }
50 
~winzoom_t()51     ~winzoom_t()
52     {}
53 
render_with_damage(wf::texture_t src_tex,wlr_box src_box,const wf::region_t & damage,const wf::framebuffer_t & target_fb)54     void render_with_damage(wf::texture_t src_tex, wlr_box src_box,
55         const wf::region_t& damage, const wf::framebuffer_t& target_fb) override
56     {
57         GL_CALL(glBindTexture(GL_TEXTURE_2D, src_tex.tex_id));
58         GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
59             nearest_filtering ? GL_NEAREST : GL_LINEAR));
60         GL_CALL(glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
61             nearest_filtering ? GL_NEAREST : GL_LINEAR));
62         wf::view_transformer_t::render_with_damage(src_tex, src_box, damage,
63             target_fb);
64     }
65 };
66 
67 class wayfire_winzoom : public wf::plugin_interface_t
68 {
69     wf::option_wrapper_t<wf::activatorbinding_t> inc_x_binding{
70         "winzoom/inc_x_binding"};
71     wf::option_wrapper_t<wf::activatorbinding_t> dec_x_binding{
72         "winzoom/dec_x_binding"};
73     wf::option_wrapper_t<wf::activatorbinding_t> inc_y_binding{
74         "winzoom/inc_y_binding"};
75     wf::option_wrapper_t<wf::activatorbinding_t> dec_y_binding{
76         "winzoom/dec_y_binding"};
77     wf::option_wrapper_t<bool> preserve_aspect{"winzoom/preserve_aspect"};
78     wf::option_wrapper_t<wf::keybinding_t> modifier{"winzoom/modifier"};
79     wf::option_wrapper_t<double> zoom_step{"winzoom/zoom_step"};
80 
81   public:
init()82     void init() override
83     {
84         grab_interface->name = "winzoom";
85         grab_interface->capabilities = 0;
86 
87         output->add_axis(modifier, &axis_cb);
88         output->add_activator(inc_x_binding, &on_inc_x);
89         output->add_activator(dec_x_binding, &on_dec_x);
90         output->add_activator(inc_y_binding, &on_inc_y);
91         output->add_activator(dec_y_binding, &on_dec_y);
92     }
93 
update_winzoom(wayfire_view view,wf::point_t delta)94     bool update_winzoom(wayfire_view view, wf::point_t delta)
95     {
96         winzoom_t *transformer;
97         wf::pointf_t zoom;
98 
99         if (!view)
100         {
101             return false;
102         }
103 
104         if (!output->activate_plugin(grab_interface))
105         {
106             return false;
107         }
108 
109         output->deactivate_plugin(grab_interface);
110 
111         auto layer = output->workspace->get_view_layer(view);
112 
113         if (layer & (wf::LAYER_BACKGROUND | wf::LAYER_TOP))
114         {
115             return false;
116         }
117 
118         if (view->role == wf::VIEW_ROLE_DESKTOP_ENVIRONMENT)
119         {
120             return false;
121         }
122 
123         if (!view->get_transformer("winzoom"))
124         {
125             view->add_transformer(std::make_unique<winzoom_t>(view), "winzoom");
126         }
127 
128         transformer =
129             dynamic_cast<winzoom_t*>(view->get_transformer("winzoom").get());
130 
131         zoom.x = transformer->scale_x;
132         zoom.y = transformer->scale_y;
133 
134         if (preserve_aspect)
135         {
136             if ((delta.x <= 0) && (delta.y <= 0))
137             {
138                 delta.x = delta.y = std::min(delta.x, delta.y);
139             }
140 
141             if ((delta.x >= 0) && (delta.y >= 0))
142             {
143                 delta.x = delta.y = std::max(delta.x, delta.y);
144             }
145         }
146 
147         zoom.x += zoom_step * delta.x;
148         zoom.y += zoom_step * delta.y;
149 
150         zoom.x = std::max(1.0, zoom.x);
151         zoom.y = std::max(1.0, zoom.y);
152 
153         if ((zoom.x == 1.0) && (zoom.y == 1.0))
154         {
155             view->pop_transformer("winzoom");
156             return true;
157         }
158 
159         if (transformer->scale_x != zoom.x)
160         {
161             transformer->scale_x = zoom.x;
162         }
163 
164         if (transformer->scale_y != zoom.y)
165         {
166             transformer->scale_y = zoom.y;
167         }
168 
169         output->render->damage_whole();
170 
171         return true;
172     }
173 
174     wf::activator_callback on_inc_x = [=] (auto)
__anon5d343bc20202(auto) 175     {
176         auto view = output->get_active_view();
177         return update_winzoom(view, wf::point_t{1, 0});
178     };
179 
180     wf::activator_callback on_dec_x = [=] (auto)
__anon5d343bc20302(auto) 181     {
182         auto view = output->get_active_view();
183         return update_winzoom(view, wf::point_t{-1, 0});
184     };
185 
186     wf::activator_callback on_inc_y = [=] (auto)
__anon5d343bc20402(auto) 187     {
188         auto view = output->get_active_view();
189         return update_winzoom(view, wf::point_t{0, 1});
190     };
191 
192     wf::activator_callback on_dec_y = [=] (auto)
__anon5d343bc20502(auto) 193     {
194         auto view = output->get_active_view();
195         return update_winzoom(view, wf::point_t{0, -1});
196     };
197 
198     wf::axis_callback axis_cb = [=] (wlr_event_pointer_axis *ev)
__anon5d343bc20602(wlr_event_pointer_axis *ev) 199     {
200         auto view = wf::get_core().get_cursor_focus_view();
201         if (ev->orientation == WLR_AXIS_ORIENTATION_VERTICAL)
202         {
203             auto delta = (int)-std::clamp(ev->delta, -1.0, 1.0);
204             return update_winzoom(view, wf::point_t{delta, delta});
205         }
206 
207         return false;
208     };
209 
fini()210     void fini() override
211     {
212         for (auto& view : output->workspace->get_views_in_layer(wf::ALL_LAYERS))
213         {
214             if (view->get_transformer("winzoom"))
215             {
216                 view->pop_transformer("winzoom");
217             }
218         }
219 
220         output->rem_binding(&axis_cb);
221         output->rem_binding(&on_inc_x);
222         output->rem_binding(&on_dec_x);
223         output->rem_binding(&on_inc_y);
224         output->rem_binding(&on_dec_y);
225     }
226 };
227 
228 DECLARE_WAYFIRE_PLUGIN(wayfire_winzoom);
229