1 #include <wayfire/plugin.hpp>
2 #include <wayfire/output.hpp>
3 #include <wayfire/opengl.hpp>
4 #include <wayfire/render-manager.hpp>
5 
6 static const char *vertex_shader =
7     R"(
8 #version 100
9 
10 attribute mediump vec2 position;
11 attribute highp vec2 uvPosition;
12 
13 varying highp vec2 uvpos;
14 
15 void main() {
16 
17     gl_Position = vec4(position.xy, 0.0, 1.0);
18     uvpos = uvPosition;
19 }
20 )";
21 
22 static const char *fragment_shader =
23     R"(
24 #version 100
25 
26 varying highp vec2 uvpos;
27 uniform sampler2D smp;
28 uniform bool preserve_hue;
29 
30 void main()
31 {
32     mediump vec4 tex = texture2D(smp, uvpos);
33 
34     if (preserve_hue)
35     {
36         mediump float hue = tex.a - min(tex.r, min(tex.g, tex.b)) - max(tex.r, max(tex.g, tex.b));
37         gl_FragColor = hue + tex;
38     } else
39     {
40         gl_FragColor = vec4(1.0 - tex.r, 1.0 - tex.g, 1.0 - tex.b, 1.0);
41     }
42 }
43 )";
44 
45 class wayfire_invert_screen : public wf::plugin_interface_t
46 {
47     wf::post_hook_t hook;
48     wf::activator_callback toggle_cb;
49     wf::option_wrapper_t<bool> preserve_hue{"invert/preserve_hue"};
50 
51     bool active = false;
52     OpenGL::program_t program;
53 
54   public:
init()55     void init() override
56     {
57         wf::option_wrapper_t<wf::activatorbinding_t> toggle_key{"invert/toggle"};
58 
59         grab_interface->name = "invert";
60         grab_interface->capabilities = 0;
61 
62         hook = [=] (const wf::framebuffer_base_t& source,
63                     const wf::framebuffer_base_t& destination)
64         {
65             render(source, destination);
66         };
67 
68         toggle_cb = [=] (auto)
69         {
70             if (!output->can_activate_plugin(grab_interface))
71             {
72                 return false;
73             }
74 
75             if (active)
76             {
77                 output->render->rem_post(&hook);
78             } else
79             {
80                 output->render->add_post(&hook);
81             }
82 
83             active = !active;
84 
85             return true;
86         };
87 
88         OpenGL::render_begin();
89         program.set_simple(
90             OpenGL::compile_program(vertex_shader, fragment_shader));
91         OpenGL::render_end();
92 
93         output->add_activator(toggle_key, &toggle_cb);
94     }
95 
render(const wf::framebuffer_base_t & source,const wf::framebuffer_base_t & destination)96     void render(const wf::framebuffer_base_t& source,
97         const wf::framebuffer_base_t& destination)
98     {
99         static const float vertexData[] = {
100             -1.0f, -1.0f,
101             1.0f, -1.0f,
102             1.0f, 1.0f,
103             -1.0f, 1.0f
104         };
105 
106         static const float coordData[] = {
107             0.0f, 0.0f,
108             1.0f, 0.0f,
109             1.0f, 1.0f,
110             0.0f, 1.0f
111         };
112 
113         OpenGL::render_begin(destination);
114 
115         program.use(wf::TEXTURE_TYPE_RGBA);
116         GL_CALL(glBindTexture(GL_TEXTURE_2D, source.tex));
117         GL_CALL(glActiveTexture(GL_TEXTURE0));
118 
119         program.attrib_pointer("position", 2, 0, vertexData);
120         program.attrib_pointer("uvPosition", 2, 0, coordData);
121         program.uniform1i("preserve_hue", preserve_hue);
122 
123         GL_CALL(glDisable(GL_BLEND));
124         GL_CALL(glDrawArrays(GL_TRIANGLE_FAN, 0, 4));
125         GL_CALL(glEnable(GL_BLEND));
126         GL_CALL(glBindTexture(GL_TEXTURE_2D, 0));
127 
128         program.deactivate();
129         OpenGL::render_end();
130     }
131 
fini()132     void fini() override
133     {
134         if (active)
135         {
136             output->render->rem_post(&hook);
137         }
138 
139         OpenGL::render_begin();
140         program.free_resources();
141         OpenGL::render_end();
142 
143         output->rem_binding(&toggle_cb);
144     }
145 };
146 
147 DECLARE_WAYFIRE_PLUGIN(wayfire_invert_screen);
148