1 /*
2  * The MIT License (MIT)
3  *
4  * Copyright (c) 2018 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/plugin.hpp>
26 #include <wayfire/output.hpp>
27 #include <wayfire/opengl.hpp>
28 #include <wayfire/util/duration.hpp>
29 #include <wayfire/render-manager.hpp>
30 
31 static const char *vertex_shader =
32     R"(
33 #version 100
34 
35 attribute mediump vec2 position;
36 
37 void main() {
38 
39     gl_Position = vec4(position.xy, 0.0, 1.0);
40 }
41 )";
42 
43 static const char *fragment_shader =
44     R"(
45 #version 100
46 precision mediump float;
47 
48 uniform vec2 u_resolution;
49 uniform vec2 u_mouse;
50 uniform float u_radius;
51 uniform float u_zoom;
52 uniform sampler2D u_texture;
53 
54 const float PI = 3.1415926535;
55 
56 void main()
57 {
58         float radius = u_radius;
59 
60         float zoom = u_zoom;
61         float pw = 1.0 / u_resolution.x;
62         float ph = 1.0 / u_resolution.y;
63 
64         vec4 p0 = vec4(u_mouse.x, u_resolution.y - u_mouse.y, 1.0 / radius, 0.0);
65         vec4 p1 = vec4(pw, ph, PI / radius, (zoom - 1.0) * zoom);
66         vec4 p2 = vec4(0, 0, -PI / 2.0, 0.0);
67 
68         vec4 t0, t1, t2, t3;
69 
70         vec3 tc = vec3(1.0, 0.0, 0.0);
71         vec2 uv = vec2(gl_FragCoord.x, gl_FragCoord.y);
72 
73         t1 = p0.xyww - vec4(uv, 0.0, 0.0);
74         t2.x = t2.y = t2.z = t2.w = 1.0 / sqrt(dot(t1.xyz, t1.xyz));
75         t0 = t2 - p0;
76 
77         t3.x = t3.y = t3.z = t3.w = 1.0 / t2.x;
78         t3 = t3 * p1.z + p2.z;
79         t3.x = t3.y = t3.z = t3.w = cos(t3.x);
80 
81         t3 = t3 * p1.w;
82 
83         t1 = t2 * t1;
84         t1 = t1 * t3 + vec4(uv, 0.0, 0.0);
85 
86         if (t0.z < 0.0) {
87                 t1.x = uv.x;
88                 t1.y = uv.y;
89         }
90 
91         t1 = t1 * p1 + p2;
92 
93         tc = texture2D(u_texture, t1.xy).rgb;
94 
95         gl_FragColor = vec4(tc, 1.0);
96 }
97 )";
98 
99 class wayfire_fisheye : public wf::plugin_interface_t
100 {
101     wf::animation::simple_animation_t progression{wf::create_option<int>(300)};
102 
103     float target_zoom;
104     bool active, hook_set;
105 
106     wf::option_wrapper_t<double> radius{"fisheye/radius"};
107     wf::option_wrapper_t<double> zoom{"fisheye/zoom"};
108 
109     OpenGL::program_t program;
110 
111   public:
init()112     void init() override
113     {
114         grab_interface->name = "fisheye";
115         grab_interface->capabilities = 0;
116 
117         hook_set = active = false;
118         output->add_activator(
119             wf::option_wrapper_t<wf::activatorbinding_t>{"fisheye/toggle"},
120             &toggle_cb);
121 
122         target_zoom = zoom;
123         zoom.set_callback([=] ()
124         {
125             if (active)
126             {
127                 this->progression.animate(zoom);
128             }
129         });
130 
131         OpenGL::render_begin();
132         program.set_simple(
133             OpenGL::compile_program(vertex_shader, fragment_shader));
134         OpenGL::render_end();
135     }
136 
137     wf::activator_callback toggle_cb = [=] (auto)
__anon9dbfed110202(auto) 138     {
139         if (!output->can_activate_plugin(grab_interface))
140         {
141             return false;
142         }
143 
144         if (active)
145         {
146             active = false;
147             progression.animate(0);
148         } else
149         {
150             active = true;
151             progression.animate(zoom);
152             if (!hook_set)
153             {
154                 hook_set = true;
155                 output->render->add_post(&render_hook);
156                 output->render->set_redraw_always();
157             }
158         }
159 
160         return true;
161     };
162 
163     wf::post_hook_t render_hook = [=] (const wf::framebuffer_base_t& source,
164                                        const wf::framebuffer_base_t& dest)
__anon9dbfed110302(const wf::framebuffer_base_t& source, const wf::framebuffer_base_t& dest) 165     {
166         auto oc     = output->get_cursor_position();
167         wlr_box box = {(int)oc.x, (int)oc.y, 1, 1};
168         box = output->render->get_target_framebuffer().
169             framebuffer_box_from_geometry_box(box);
170         oc.x = box.x;
171         oc.y = box.y;
172 
173         static const float vertexData[] = {
174             -1.0f, -1.0f,
175             1.0f, -1.0f,
176             1.0f, 1.0f,
177             -1.0f, 1.0f
178         };
179 
180         OpenGL::render_begin(dest);
181         program.use(wf::TEXTURE_TYPE_RGBA);
182         GL_CALL(glBindTexture(GL_TEXTURE_2D, source.tex));
183         GL_CALL(glActiveTexture(GL_TEXTURE0));
184 
185         program.uniform2f("u_mouse", oc.x, oc.y);
186         program.uniform2f("u_resolution", dest.viewport_width, dest.viewport_height);
187         program.uniform1f("u_radius", radius);
188         program.uniform1f("u_zoom", progression);
189 
190         program.attrib_pointer("position", 2, 0, vertexData);
191 
192         GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
193         GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
194 
195         program.deactivate();
196         OpenGL::render_end();
197 
198         if (!active && !progression.running())
199         {
200             finalize();
201         }
202     };
203 
finalize()204     void finalize()
205     {
206         output->render->rem_post(&render_hook);
207         output->render->set_redraw_always(false);
208         hook_set = false;
209     }
210 
fini()211     void fini() override
212     {
213         if (hook_set)
214         {
215             finalize();
216         }
217 
218         OpenGL::render_begin();
219         program.free_resources();
220         OpenGL::render_end();
221 
222         output->rem_binding(&toggle_cb);
223     }
224 };
225 
226 DECLARE_WAYFIRE_PLUGIN(wayfire_fisheye);
227