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