commit b5e7b442bdb5ed8a8a2525a2687c9fc03d665c1f Author: Kuoi Date: Sun Aug 21 20:37:30 2022 +0100 first commit diff --git a/meson.build b/meson.build new file mode 100644 index 0000000..b257942 --- /dev/null +++ b/meson.build @@ -0,0 +1,34 @@ +project( + 'wayfire-plugins-extra', + 'c', + 'cpp', + version: '0.8.0', + license: 'MIT', + meson_version: '>=0.51.0', + default_options: [ + 'cpp_std=c++17', + 'c_std=c11', + 'warning_level=2', + 'werror=false', + ], +) + +wayfire = dependency('wayfire') +giomm = dependency('giomm-2.4') + +add_project_arguments(['-DWLR_USE_UNSTABLE'], language: ['cpp', 'c']) +add_project_arguments(['-DWAYFIRE_PLUGIN'], language: ['cpp', 'c']) +add_project_link_arguments(['-rdynamic'], language:'cpp') + +add_project_arguments(['-Wno-unused-parameter'], language: 'cpp') +subdir('src') +subdir('metadata') + +summary = [ + '', + '----------------', + 'wf-water @0@'.format(meson.project_version()), + '----------------', + '' +] +message('\n'.join(summary)) diff --git a/metadata/meson.build b/metadata/meson.build new file mode 100644 index 0000000..69ff308 --- /dev/null +++ b/metadata/meson.build @@ -0,0 +1 @@ +install_data('water.xml', install_dir: wayfire.get_variable(pkgconfig: 'metadatadir')) diff --git a/metadata/water.xml b/metadata/water.xml new file mode 100644 index 0000000..f1c91b1 --- /dev/null +++ b/metadata/water.xml @@ -0,0 +1,13 @@ + + + + <_short>Water + <_long>Water effect. + Effects + + + diff --git a/src/meson.build b/src/meson.build new file mode 100644 index 0000000..23ab4ae --- /dev/null +++ b/src/meson.build @@ -0,0 +1,3 @@ +water = shared_module('water', 'water.cpp', + dependencies: [wayfire], + install: true, install_dir: join_paths(get_option('libdir'), 'wayfire')) diff --git a/src/water.cpp b/src/water.cpp new file mode 100644 index 0000000..bc26fa8 --- /dev/null +++ b/src/water.cpp @@ -0,0 +1,424 @@ +/* + * The MIT License (MIT) + * + * Copyright (c) 2020 Scott Moreau + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Shaders ported from: + * https://www.shadertoy.com/view/4sd3WB (Buffer A and B) + * https://www.shadertoy.com/view/Xsd3DB (Image) + * + */ + +#include +#include +#include +#include +#include + +static const char *vertex_shader = + R"( +#version 100 + +attribute mediump vec2 position; +attribute highp vec2 uvPosition; + +varying highp vec2 uvpos; + +void main() +{ + gl_Position = vec4(position.xy, 0.0, 1.0); + uvpos = uvPosition; +} +)"; + +static const char *fragment_shader_a = + R"( +#version 100 +precision mediump float; + +uniform int num_points; +uniform vec2 points[64]; +uniform int button_down; +varying highp vec2 uvpos; +uniform sampler2D u_texture; + +void main() +{ + int i; + for (i = 0; i < num_points; i++) + { + vec2 r = gl_FragCoord.xy - points[i]; + float d = 0.005 * dot(r, r); + if (button_down == 1 && d < 0.05) + { + gl_FragColor = vec4(0.0, 1.0, 0.0, 0.0); + return; + } + } + + gl_FragColor = texture2D(u_texture, uvpos); +} +)"; + +static const char *fragment_shader_b = + R"( +#version 100 +precision mediump float; + +uniform vec2 resolution; +varying highp vec2 uvpos; +uniform sampler2D u_texture; + +void main() +{ + float dx = resolution.x; + float dy = resolution.y; + vec2 uv = uvpos; + + vec2 udu = texture2D(u_texture, uv).xy; + // old elevation + float u = udu.x; + // old velocity + float du = udu.y; + + // Finite differences + float ux = texture2D(u_texture, vec2(uv.x + dx, uv.y)).x; + float umx = texture2D(u_texture, vec2(uv.x - dx, uv.y)).x; + float uy = texture2D(u_texture, vec2(uv.x, uv.y + dy)).x; + float umy = texture2D(u_texture, vec2(uv.x, uv.y - dy)).x; + + // new elevation + float nu = u + du + 0.28 * (umx + ux + umy + uy - 4.0 * u); + nu *= 0.99; + + // evaporation + if (nu < 0.025) + { + nu *= 0.2; + } + + // store elevation and velocity + gl_FragColor = vec4(nu, nu - u, 0.0, 0.0); +} +)"; + +static const char *fragment_shader_c = + R"( +#version 100 +precision mediump float; + +#define DEBUG 0 + +uniform float fade; +uniform vec2 resolution; +varying highp vec2 uvpos; +uniform sampler2D u_texture; +uniform sampler2D water_texture; + +void main() +{ + vec2 uv = uvpos; +#if DEBUG == 1 + float h = texture2D(water_texture, uv).x; + float sh = 1.35 - h * 2.; + vec4 effect = + vec4(exp(pow(sh - .75, 2.) * -10.), + exp(pow(sh - .50, 2.) * -20.), + exp(pow(sh - .25, 2.) * -10.), + 1.); + vec4 fb_pixel = vec4(0.); + vec4 color = effect; + if (fade < 1.) + { + fb_pixel = texture2D(u_texture, uv) * (1. - fade); + color *= fade; + color += fb_pixel; + } + gl_FragColor = color; +#else + vec3 e = vec3(resolution, 0.); + float p10 = texture2D(water_texture, uv - e.zy).x; + float p01 = texture2D(water_texture, uv - e.xz).x; + float p21 = texture2D(water_texture, uv + e.xz).x; + float p12 = texture2D(water_texture, uv + e.zy).x; + + vec3 grad = normalize(vec3(p21 - p01, p12 - p10, 1.)); + vec4 c = texture2D(u_texture, uv + grad.xy * .35); + vec3 light = normalize(vec3(.2, -.5, .7)); + float diffuse = dot(grad, light); + if (diffuse > 0.75) + { + diffuse = 1.0; + } + float spec = pow(max(0., -reflect(light, grad).z), 32.); + c = c * diffuse + spec; + + if (fade < 1.) + { + vec4 fb_pixel = texture2D(u_texture, uv) * (1. - fade); + c = c * fade + fb_pixel; + } + + gl_FragColor = c; +#endif +} +)"; + +class wayfire_water_screen : public wf::plugin_interface_t +{ + wf::option_wrapper_t button{"water/activate"}; + wf::animation::simple_animation_t animation = + wf::animation::simple_animation_t(wf::create_option(5000)); + OpenGL::program_t program[3]; + wf::framebuffer_t buffer[2]; + wf::pointf_t last_cursor; + bool button_down = false; + bool hook_set = false; + wf::wl_timer timer; + int points_loc; + + public: + void init() override + { + grab_interface->name = "water"; + grab_interface->capabilities = wf::CAPABILITY_MANAGE_COMPOSITOR; + + OpenGL::render_begin(); + program[0].set_simple( + OpenGL::compile_program(vertex_shader, fragment_shader_a)); + program[1].set_simple( + OpenGL::compile_program(vertex_shader, fragment_shader_b)); + program[2].set_simple( + OpenGL::compile_program(vertex_shader, fragment_shader_c)); + points_loc = GL_CALL(glGetUniformLocation( + program[0].get_program_id(wf::TEXTURE_TYPE_RGBA), "points")); + OpenGL::render_end(); + + output->add_button(button, &activate_binding); + + grab_interface->callbacks.pointer.button = [=] (uint32_t b, uint32_t s) + { + if (s == WL_POINTER_BUTTON_STATE_RELEASED) + { + output->deactivate_plugin(grab_interface); + timer.set_timeout(5000, timeout); + grab_interface->ungrab(); + button_down = false; + } + }; + animation.set(0, 0); + } + + wf::button_callback activate_binding = [=] (auto) + { + if (!output->is_plugin_active(grab_interface->name)) + { + if (!output->activate_plugin(grab_interface)) + { + return false; + } + } + + if (!hook_set) + { + output->render->add_post(&render); + hook_set = true; + } + + last_cursor = output->get_cursor_position(); + animation.animate(animation, 1); + grab_interface->grab(); + timer.disconnect(); + button_down = true; + + return true; + }; + + wf::wl_timer::callback_t timeout = [=] () + { + animation.animate(animation, 0); + return false; // disconnect + }; + + wf::post_hook_t render = [=] (const wf::framebuffer_base_t& source, + const wf::framebuffer_base_t& destination) + { + auto transform = output->render->get_target_framebuffer().transform; + auto cursor_position = output->get_cursor_position(); + auto og = output->get_relative_geometry(); + auto fbg = output->render->get_target_framebuffer(). + framebuffer_box_from_geometry_box(og); + transform = glm::inverse(transform); + + static const float vertexData[] = { + -1.0f, -1.0f, + 1.0f, -1.0f, + 1.0f, 1.0f, + -1.0f, 1.0f + }; + + static const float coordData[] = { + 0.0f, 0.0f, + 1.0f, 0.0f, + 1.0f, 1.0f, + 0.0f, 1.0f + }; + + wf::pointf_t step; + std::vector points; + wf::pointf_t center{0.5, 0.5}; + auto d = glm::distance(glm::vec2(last_cursor.x, last_cursor.y), + glm::vec2(cursor_position.x, cursor_position.y)); + + /* Interpolate between last and current cursor */ + int num_points = std::min(int(d / 5 + 1), 64); + step.x = (cursor_position.x - last_cursor.x) / num_points; + step.y = (cursor_position.y - last_cursor.y) / num_points; + for (int i = 0; i < num_points; i++) + { + wf::pointf_t p = wf::pointf_t{ + cursor_position.x - step.x * i, + cursor_position.y - step.y * i}; + + float x = p.x / og.width; + float y = p.y / og.height; + + /* Apply transform to cursor position */ + glm::vec4 point{x - center.x, y - center.y, 1.0, 1.0}; + glm::vec4 result = transform * point; + x = (result.x + center.x) * fbg.width; + y = (result.y + center.y) * fbg.height; + y = fbg.height - y; + points.push_back(x); + points.push_back(y); + } + + last_cursor = cursor_position; + + /* First pass */ + OpenGL::render_begin(); + if (buffer[0].allocate(fbg.width, fbg.height)) + { + buffer[0].bind(); + buffer[0].geometry = fbg; + OpenGL::clear({0, 0, 0, 1}); + } + + if (buffer[1].allocate(fbg.width, fbg.height)) + { + buffer[1].bind(); + buffer[1].geometry = fbg; + OpenGL::clear({0, 0, 0, 1}); + } + + buffer[0].bind(); + program[0].use(wf::TEXTURE_TYPE_RGBA); + program[0].attrib_pointer("position", 2, 0, vertexData); + program[0].attrib_pointer("uvPosition", 2, 0, coordData); + GL_CALL(glUniform2fv(points_loc, num_points, points.data())); + program[0].uniform1i("num_points", num_points); + program[0].uniform1i("button_down", button_down ? 1 : 0); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, buffer[1].tex)); + + GL_CALL(glDisable(GL_BLEND)); + GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4)); + GL_CALL(glEnable(GL_BLEND)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + program[0].deactivate(); + OpenGL::render_end(); + + /* Second pass */ + OpenGL::render_begin(buffer[1]); + program[1].use(wf::TEXTURE_TYPE_RGBA); + program[1].attrib_pointer("position", 2, 0, vertexData); + program[1].attrib_pointer("uvPosition", 2, 0, coordData); + program[1].uniform2f("resolution", 1.0 / fbg.width, 1.0 / fbg.height); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, buffer[0].tex)); + + GL_CALL(glDisable(GL_BLEND)); + GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4)); + GL_CALL(glEnable(GL_BLEND)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + program[1].deactivate(); + OpenGL::render_end(); + + /* Final pass */ + OpenGL::render_begin(destination); + program[2].use(wf::TEXTURE_TYPE_RGBA); + program[2].attrib_pointer("position", 2, 0, vertexData); + program[2].attrib_pointer("uvPosition", 2, 0, coordData); + program[2].uniform2f("resolution", 1.0 / fbg.width, 1.0 / fbg.height); + program[2].uniform1f("fade", animation); + program[2].uniform1i("water_texture", 1); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, source.tex)); + GL_CALL(glActiveTexture(GL_TEXTURE0 + 1)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, buffer[1].tex)); + + GL_CALL(glDisable(GL_BLEND)); + GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4)); + GL_CALL(glEnable(GL_BLEND)); + + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + GL_CALL(glActiveTexture(GL_TEXTURE0)); + GL_CALL(glBindTexture(GL_TEXTURE_2D, 0)); + program[2].deactivate(); + OpenGL::render_end(); + + if (!button_down && !timer.is_connected() && !animation.running()) + { + hook_set = false; + output->render->rem_post(&render); + OpenGL::render_begin(); + buffer[0].release(); + buffer[1].release(); + OpenGL::render_end(); + } + + output->render->schedule_redraw(); + }; + + void fini() override + { + output->deactivate_plugin(grab_interface); + output->rem_binding(&activate_binding); + grab_interface->ungrab(); + timer.disconnect(); + if (hook_set) + { + output->render->rem_post(&render); + } + + OpenGL::render_begin(); + buffer[0].release(); + buffer[1].release(); + program[0].free_resources(); + program[1].free_resources(); + program[2].free_resources(); + OpenGL::render_end(); + } +}; + +DECLARE_WAYFIRE_PLUGIN(wayfire_water_screen);