├── .gitignore
├── README.md
├── build.sh
├── c-filters.c
├── init.lua
└── types
└── c-filters.lua
/.gitignore:
--------------------------------------------------------------------------------
1 | *.so
2 | .vscode/
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Surface filters for AwesomeWM
2 |
3 | ## Description
4 |
5 | This repository adds support for various pixel effects, such as blurring or recoloring, that can be used by simply wrapping a widget in them.
6 |
7 | ## Usage
8 |
9 | First, clone this repository to your awesome wm config directory:
10 |
11 | ```bash
12 | git clone https://github.com/ReadyWidgets/filters "${XDG_CONFIG_HOME:-$HOME/.config}/awesome/filters"
13 | ```
14 |
15 | Next, compile `c-filters.c`:
16 |
17 | ```bash
18 | cd "${XDG_CONFIG_HOME:-$HOME/.config}/awesome/filters"
19 | bash build.sh
20 | ```
21 |
22 | Then, you can use the filters using something like this:
23 |
24 | ```lua
25 | local mywidget = wibox.widget {
26 | {
27 | markup = "Hello, world!",
28 | halign = "center",
29 | valign = "center",
30 | widget = wibox.widget.textbox,
31 | },
32 | radius = 20,
33 | opacity = 1.0,
34 | widget = filters.shadow,
35 | }
36 | ```
37 |
38 | ## Available filters and properties
39 |
40 | - Blur
41 | - `radius: integer = 10`
42 | - `no_use_kernel: boolean = false`
43 | - Shadow
44 | - `radius: integer = 10`
45 | - `opacity: number = 1.0`
46 | - Tint
47 | - `color: string = "#000000"`
48 |
--------------------------------------------------------------------------------
/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | cd "$( dirname "${BASH_SOURCE[0]}" )" || exit 1
3 |
4 | file="c-filters.c"
5 |
6 | gcc -std=c11 -Werror-implicit-function-declaration -shared -fPIC "$(pkg-config --cflags --libs gtk+-3.0 glib-2.0 gio-2.0 gio-unix-2.0 luajit cairo)" -lm "$file" -o "${file%.c}.so"
7 |
--------------------------------------------------------------------------------
/c-filters.c:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | #include
8 | #include
9 |
10 | #include
11 | #include
12 | #include
13 |
14 | typedef long long int i64;
15 | typedef long int i32;
16 | typedef int i16;
17 | typedef char i8;
18 |
19 | typedef unsigned long long int u64;
20 | typedef unsigned long int u32;
21 | typedef unsigned int u16;
22 | typedef unsigned char u8;
23 |
24 | #define Surface cairo_surface_t
25 |
26 | #define def(name) static int name(lua_State* L)
27 |
28 | #define new(type) malloc(sizeof(type))
29 |
30 | #define for_range(loopvar, stop) for (u8 loopvar = 0; loopvar < stop; loopvar++)
31 |
32 | #define print(fmt, ...) printf(("\x1b[1;34mc_filter -> \x1b[0m" fmt "\n"), __VA_ARGS__)
33 |
34 | typedef struct Pixel {
35 | u8 red, green, blue, alpha;
36 | } __attribute__((packed)) Pixel;
37 |
38 | #define index_color(pixel, index) ((u8*)(pixel))[index]
39 |
40 | #define auto_pointer(type, name, size, value, block) { type* name = malloc(sizeof(type) * size); name = value; block; free(name); }
41 |
42 | /*
43 | // (surface: cairo.Surface, width: integer, height: integer) -> GdkPixbuf.Pixbuf
44 | def(export_surface_to_pixbuf) {
45 | Surface* surface = (Surface*)lua_touserdata(L, 1);
46 | int width = (i64)luaL_checknumber(L, 2);
47 | int height = (i64)luaL_checknumber(L, 3);
48 |
49 | GdkPixbuf* pixbuf = gdk_pixbuf_get_from_surface(surface, 0, 0, width, height);
50 |
51 | lua_pushlightuserdata(L, pixbuf);
52 |
53 | return 1;
54 | }
55 | */
56 |
57 | double clamp(double number, double floor, double ceiling) {
58 | if (number < floor) {
59 | return floor;
60 | } else if (number > ceiling) {
61 | return ceiling;
62 | } else {
63 | return number;
64 | }
65 | }
66 |
67 | i64 int_clamp(i64 number, i64 floor, i64 ceiling) {
68 | return (i64)clamp(number, floor, ceiling);
69 | }
70 |
71 | // (number: number, floor: number, ceiling: number) -> number
72 | def(export_clamp) {
73 | double number = luaL_checknumber(L, 1);
74 | double floor = luaL_checknumber(L, 2);
75 | double ceiling = luaL_checknumber(L, 3);
76 |
77 | double result = clamp(number, floor, ceiling);
78 |
79 | lua_pushnumber(L, result);
80 |
81 | return 1;
82 | }
83 |
84 | const double pi = 3.1415926535897932384626433832795;
85 | // Based on https://stackoverflow.com/a/8204867
86 | double* generate_blur_kernel(i32 radius, i32 sigma) {
87 | double* kernel = malloc(sizeof(double) * (radius * radius));
88 | double mean = radius / 2;
89 | double sum = 0.0;
90 |
91 | for_range (x, radius) {
92 | for_range (y, radius) {
93 | i16 index = x + (y * radius);
94 |
95 | kernel[index] = exp(
96 | -0.5 * (pow((x - mean) / sigma, 2.0) + pow((y - mean) / sigma, 2.0))
97 | ) / (
98 | 2 * pi * sigma * sigma
99 | );
100 |
101 | sum += kernel[index];
102 | }
103 | }
104 |
105 | for_range (x, radius) {
106 | for_range (y, radius) {
107 | kernel[x + (y * radius)] /= sum;
108 | }
109 | }
110 |
111 | return kernel;
112 | }
113 |
114 | GdkPixbuf* blur_pixbuf(GdkPixbuf* pixbuf, const i32 radius, const bool no_use_kernel) {
115 | guint* _length = new(guint);
116 |
117 | Pixel* pixels = (Pixel*)gdk_pixbuf_get_pixels_with_length(pixbuf, _length);
118 |
119 | const u32 length = *_length / 4;
120 | free(_length);
121 |
122 | const u32 rowstride = gdk_pixbuf_get_rowstride(pixbuf);
123 | const u32 width = gdk_pixbuf_get_width(pixbuf);
124 | const u32 height = gdk_pixbuf_get_height(pixbuf);
125 |
126 | const u16 half = ceil(0.5 * radius);
127 |
128 | const double multiplier = 1.0f / (double)(radius * radius);
129 |
130 | Pixel* blurred_pixels = malloc(sizeof(Pixel) * length);
131 |
132 | double* kernel = generate_blur_kernel(radius, (radius / 2.0));
133 |
134 | for_range (y, height) {
135 | for_range (x, width) {
136 |
137 | u32 index = x + (y * height);
138 |
139 | blurred_pixels[index].alpha = 0;
140 | blurred_pixels[index].red = 0;
141 | blurred_pixels[index].green = 0;
142 | blurred_pixels[index].blue = 0;
143 |
144 | u32 y_floor = clamp(y - half, 0, height);
145 | u32 y_ceil = clamp(y + half, 0, height);
146 | u32 x_floor = clamp(x - half, 0, width);
147 | u32 x_ceil = clamp(x + half, 0, width);
148 |
149 | for_range (channel_offset, 4) {
150 | u32 color_index = index * 4 + channel_offset;
151 |
152 | double current_color = 0; // index_color(blurred_pixels, color_index);
153 |
154 | if (no_use_kernel) {
155 | for (i32 y_offset = y_floor; y_offset < y_ceil; y_offset++) {
156 | for (i32 x_offset = x_floor; x_offset < x_ceil; x_offset++) {
157 | current_color += (double)index_color(pixels, ((x_offset + (y_offset * height)) * 4 + channel_offset));
158 | }
159 | }
160 |
161 | current_color *= multiplier;
162 | } else {
163 | for_range (y_offset, radius) {
164 | for_range (x_offset, radius) {
165 | double kernel_value = kernel[x_offset + (y_offset * radius)];
166 | current_color += index_color(
167 | pixels,
168 | ((int_clamp(x + (x_offset - half), 0, width) + ((int_clamp(y + (y_offset - half), 0, height)) * height)) * 4 + channel_offset)
169 | ) * kernel_value;
170 | }
171 | }
172 | }
173 |
174 | index_color(blurred_pixels, color_index) = (u8)current_color;
175 | }
176 | }
177 | }
178 |
179 | GdkPixbuf* blurred_pixbuf = gdk_pixbuf_new_from_data(
180 | (u8*)blurred_pixels,
181 | GDK_COLORSPACE_RGB,
182 | true,
183 | 8,
184 | width,
185 | height,
186 | rowstride,
187 | NULL,
188 | NULL
189 | );
190 |
191 | free(kernel);
192 |
193 | return blurred_pixbuf;
194 | }
195 |
196 | def(export_blur_pixbuf) {
197 | GdkPixbuf* pixbuf = lua_touserdata(L, 1);
198 | i32 radius = luaL_checkint(L, 2);
199 | bool no_use_kernel = lua_toboolean(L, 3);
200 |
201 | GdkPixbuf* blurred_pixbuf = blur_pixbuf(pixbuf, radius, no_use_kernel);
202 |
203 | lua_pushlightuserdata(L, blurred_pixbuf);
204 |
205 | return 1;
206 | }
207 |
208 | typedef enum BlendingMode {
209 | ADD
210 | } BlendingMode;
211 |
212 | // Note: pixbuf_a and pixbuf_b MUST have the same dimensions
213 | GdkPixbuf* combine_pixbufs(GdkPixbuf* pixbuf_a, GdkPixbuf* pixbuf_b, const BlendingMode blending_mode) {
214 | guint* _length = new(guint);
215 |
216 | u8* pixels_a = gdk_pixbuf_get_pixels_with_length(pixbuf_a, _length);
217 |
218 | const u32 length = *_length;
219 | const u32 rowstride = gdk_pixbuf_get_rowstride(pixbuf_a);
220 | const u32 width = gdk_pixbuf_get_width(pixbuf_a);
221 | const u32 height = gdk_pixbuf_get_height(pixbuf_a);
222 |
223 | u8* pixels_b = gdk_pixbuf_get_pixels_with_length(pixbuf_b, _length);
224 |
225 | free(_length);
226 |
227 | u8* combined_pixels = malloc(sizeof(u8) * length);
228 |
229 | switch (blending_mode) {
230 | case ADD:
231 | for (u32 i = 0; i < length; i++) {
232 | combined_pixels[i] = clamp(pixels_a[i] + pixels_b[i], 0, 255);
233 | }
234 |
235 | break;
236 | }
237 |
238 | GdkPixbuf* combined_pixbuf = gdk_pixbuf_new_from_data(
239 | combined_pixels,
240 | GDK_COLORSPACE_RGB,
241 | true,
242 | 8,
243 | width,
244 | height,
245 | rowstride,
246 | NULL,
247 | NULL
248 | );
249 |
250 | return combined_pixbuf;
251 | }
252 |
253 | def(export_combine_pixbufs) {
254 | GdkPixbuf* pixbuf_a = lua_touserdata(L, 1);
255 | GdkPixbuf* pixbuf_b = lua_touserdata(L, 2);
256 | BlendingMode mode = luaL_checkint(L, 3);
257 |
258 | GdkPixbuf* combined_pixbuf = combine_pixbufs(pixbuf_a, pixbuf_b, mode);
259 |
260 | lua_pushlightuserdata(L, combined_pixbuf);
261 |
262 | return 1;
263 | }
264 |
265 | GdkPixbuf* tint_pixbuf(GdkPixbuf* pixbuf, const u8 red, const u8 green, const u8 blue) {
266 | guint* _length = new(guint);
267 |
268 | Pixel* pixels = (Pixel*)gdk_pixbuf_get_pixels_with_length(pixbuf, _length);
269 |
270 | const u32 length = *_length / 4;
271 | const u32 rowstride = gdk_pixbuf_get_rowstride(pixbuf);
272 | const u32 width = gdk_pixbuf_get_width(pixbuf);
273 | const u32 height = gdk_pixbuf_get_height(pixbuf);
274 |
275 | free(_length);
276 |
277 | Pixel* tinted_pixels = malloc(sizeof(Pixel) * length);
278 |
279 | for (u32 i = 0; i < length; i++) {
280 | tinted_pixels[i].red = red;
281 | tinted_pixels[i].green = green;
282 | tinted_pixels[i].blue = blue;
283 | tinted_pixels[i].alpha = pixels[i].alpha;
284 | }
285 |
286 | GdkPixbuf* tinted_pixbuf = gdk_pixbuf_new_from_data(
287 | (u8*)tinted_pixels,
288 | GDK_COLORSPACE_RGB,
289 | true,
290 | 8,
291 | width,
292 | height,
293 | rowstride,
294 | NULL,
295 | NULL
296 | );
297 |
298 | return tinted_pixbuf;
299 | }
300 |
301 | def(export_tint_pixbuf) {
302 | GdkPixbuf* pixbuf = lua_touserdata(L, 1);
303 | u8 red = luaL_checkint(L, 2);
304 | u8 green = luaL_checkint(L, 3);
305 | u8 blue = luaL_checkint(L, 4);
306 |
307 | GdkPixbuf* tinted_pixbuf = tint_pixbuf(pixbuf, red, green, blue);
308 |
309 | lua_pushlightuserdata(L, tinted_pixbuf);
310 |
311 | return 1;
312 | }
313 |
314 | GdkPixbuf* apply_multiplier_to_pixbuf(GdkPixbuf* pixbuf, const double red, const double green, const double blue, const double alpha) {
315 | guint* _length = new(guint);
316 |
317 | Pixel* pixels = (Pixel*)gdk_pixbuf_get_pixels_with_length(pixbuf, _length);
318 |
319 | const u32 length = *_length / 4;
320 | const u32 rowstride = gdk_pixbuf_get_rowstride(pixbuf);
321 | const u32 width = gdk_pixbuf_get_width(pixbuf);
322 | const u32 height = gdk_pixbuf_get_height(pixbuf);
323 |
324 | free(_length);
325 |
326 | Pixel* tinted_pixels = malloc(sizeof(Pixel) * length);
327 |
328 | for (u32 i = 0; i < length; i++) {
329 | tinted_pixels[i].red = clamp(pixels[i].red * red, 0, 255);
330 | tinted_pixels[i].green = clamp(pixels[i].green * green, 0, 255);
331 | tinted_pixels[i].blue = clamp(pixels[i].blue * blue, 0, 255);
332 | tinted_pixels[i].alpha = clamp(pixels[i].alpha * alpha, 0, 255);
333 | }
334 |
335 | GdkPixbuf* tinted_pixbuf = gdk_pixbuf_new_from_data(
336 | (u8*)tinted_pixels,
337 | GDK_COLORSPACE_RGB,
338 | true,
339 | 8,
340 | width,
341 | height,
342 | rowstride,
343 | NULL,
344 | NULL
345 | );
346 |
347 | return tinted_pixbuf;
348 | }
349 |
350 | GdkPixbuf* add_shadow_to_pixbuf(GdkPixbuf* pixbuf, u32 radius, double opcaity) {
351 | GdkPixbuf* blurred_pixbuf = blur_pixbuf(pixbuf, radius, true);
352 |
353 | GdkPixbuf* tinted_pixbuf = apply_multiplier_to_pixbuf(blurred_pixbuf, 0.0, 0.0, 0.0, 0.75);
354 | g_free(blurred_pixbuf);
355 |
356 | GdkPixbuf* shaded_pixbuf = combine_pixbufs(pixbuf, tinted_pixbuf, ADD);
357 | g_free(tinted_pixbuf);
358 |
359 | return shaded_pixbuf;
360 | }
361 |
362 | def(export_add_shadow_to_pixbuf) {
363 | GdkPixbuf* pixbuf = lua_touserdata(L, 1);
364 | u32 radius = luaL_checkint(L, 2);
365 | double opcaity = lua_tonumber(L, 3);
366 |
367 | GdkPixbuf* shaded_pixbuf = add_shadow_to_pixbuf(pixbuf, radius, opcaity);
368 |
369 | lua_pushlightuserdata(L, shaded_pixbuf);
370 |
371 | return 1;
372 | }
373 |
374 | static const struct luaL_Reg filters[] = {
375 | //{ "surface_to_pixbuf", export_surface_to_pixbuf },
376 | { "clamp", export_clamp },
377 | { "blur_pixbuf", export_blur_pixbuf },
378 | { "combine_pixbufs", export_combine_pixbufs },
379 | { "tint_pixbuf", export_tint_pixbuf },
380 | { "add_shadow_to_pixbuf", export_add_shadow_to_pixbuf },
381 | { NULL, NULL }
382 | };
383 |
384 | int luaopen_filters(lua_State* L) {
385 | luaL_newlib(L, filters);
386 |
387 | return 1;
388 | }
389 |
--------------------------------------------------------------------------------
/init.lua:
--------------------------------------------------------------------------------
1 | local awful = require("awful")
2 | local gears = require("gears")
3 | local wibox = require("wibox")
4 |
5 | local lgi = require("lgi")
6 | local Gdk, GdkPixbuf, cairo = lgi.Gdk, lgi.GdkPixbuf, lgi.cairo
7 |
8 | ---@type filters.c-filters
9 | local c_filters = require("filters.c-filters")
10 |
11 | local assert_param_type, fill_context_with_surface, gen_property, copy
12 |
13 | local export = {}
14 |
15 | --- Check if a given parameter is of the expected type, otherwise throw an error
16 | ---@param func_name string
17 | ---@param position integer
18 | ---@param wanted_type type
19 | ---@param value any
20 | function assert_param_type(func_name, position, wanted_type, value)
21 | local value_type = type(value)
22 |
23 | assert(value_type == wanted_type, ("Wrong type of parameter #%d passed to '%s' (expected %s, got %s)"):format(position, func_name, wanted_type, value_type))
24 | end
25 |
26 | --- Convert a cairo surface into a Gdk pixel buffer
27 | ---@param surface cairo.Surface
28 | ---@param width integer
29 | ---@param height integer
30 | ---@return GdkPixbuf.Pixbuf
31 | function export.surface_to_pixbuf(surface, width, height)
32 | return Gdk.pixbuf_get_from_surface(surface, 0, 0, width, height)
33 | end
34 |
35 | ---@param pixbuf GdkPixbuf.Pixbuf
36 | ---@return cairo.Surface
37 | function export.pixbuf_to_surface(pixbuf)
38 | local surface = awesome.pixbuf_to_surface(pixbuf._native, gears.surface())
39 |
40 | if cairo.Surface:is_type_of(surface) then
41 | return surface
42 | end
43 |
44 | return cairo.Surface(surface, true)
45 | end
46 |
47 | --- Fill a cairo context with a cairo surface
48 | ---@param cr cairo.Context
49 | ---@param surface cairo.Surface
50 | function fill_context_with_surface(cr, surface)
51 | cr:set_source_surface(surface, 0, 0)
52 | cr:paint()
53 | end
54 |
55 | function gen_property(object, property)
56 | assert_param_type("gen_property", 1, "table", object)
57 | assert_param_type("gen_property", 2, "string", property)
58 |
59 | object["get_" .. property] = function(self)
60 | --print("Called get_"..property.."()")
61 | return self._private[property]
62 | end
63 |
64 | object["set_" .. property] = function(self, value)
65 | --print("Called set_"..property.."(" .. tostring(value) .. ")")
66 | self._private[property] = value
67 | self:emit_signal(("property::" .. property), value)
68 | self._private.force_redraw = true
69 | self:emit_signal("widget::redraw_needed")
70 | end
71 | end
72 |
73 | do
74 | local base = { mt = {} }
75 | base.mt.__index = base.mt
76 | setmetatable(base, base.mt)
77 |
78 | function base:get_widget()
79 | return self._private.widget
80 | end
81 |
82 | function base:set_widget(widget)
83 | if self._private.child_redraw_listener == nil then
84 | function self._private.child_redraw_listener()
85 | self._private.force_redraw = true
86 | self:emit_signal("widget::redraw_needed")
87 | end
88 | end
89 |
90 | local child_redraw_listener = self._private.child_redraw_listener
91 |
92 | if self._private.widget ~= nil then
93 | self._private.widget:disconnect_signal("widget::redraw_needed", child_redraw_listener)
94 | end
95 |
96 | widget:connect_signal("widget::redraw_needed", child_redraw_listener)
97 |
98 | wibox.widget.base.set_widget_common(self, widget)
99 | end
100 |
101 | function base:get_children()
102 | return { self._private.widget }
103 | end
104 |
105 | function base:set_children(children)
106 | self:set_widget(children[1])
107 | end
108 |
109 | function base:draw(context, cr, w, h)
110 | --do
111 | -- return fill_context_with_surface(cr, self._private.cached_surface)
112 | --end
113 |
114 | local child = self:get_widget()
115 |
116 | if child == nil then
117 | return
118 | end
119 |
120 | if (not self._private.force_redraw) and (self._private.cached_surface ~= nil) then
121 | fill_context_with_surface(cr, self._private.cached_surface)
122 | return
123 | end
124 |
125 | ---@type cairo.Surface
126 | local surface
127 | if self.on_draw ~= nil then
128 | surface = self:on_draw(cr, w, h, child)
129 | else
130 | surface = wibox.widget.draw_to_image_surface(child, w, h)
131 | end
132 |
133 | self._private.force_redraw = false
134 | self._private.cached_surface = surface
135 |
136 | fill_context_with_surface(cr, surface)
137 | end
138 |
139 | function base.mt.__call(cls, kwargs)
140 | if kwargs == nil then
141 | kwargs = {}
142 | end
143 |
144 | local self = gears.object { enable_properties = true }
145 |
146 | gears.table.crush(self, wibox.widget.base.make_widget())
147 | gears.table.crush(self, cls)
148 |
149 | if cls.parse_kwargs ~= nil then
150 | cls.parse_kwargs(kwargs)
151 | end
152 |
153 | if self._private == nil then
154 | self._private = {}
155 | end
156 |
157 | --gears.table.crush(self, kwargs)
158 | for k, v in pairs(kwargs) do
159 | self[k] = v
160 | end
161 |
162 | return self
163 | end
164 |
165 | export.base = base
166 | end
167 |
168 | ---@generic T1 : table
169 | ---@param tb T1
170 | ---@return T1
171 | function copy(tb)
172 | local copy_of_tb = {}
173 |
174 | for k, v in pairs(tb) do
175 | copy_of_tb[k] = v
176 | end
177 |
178 | setmetatable(copy_of_tb, getmetatable(tb))
179 |
180 | return copy_of_tb
181 | end
182 |
183 | do
184 | local blur = copy(export.base)
185 |
186 | gen_property(blur, "radius")
187 | gen_property(blur, "no_use_kernel")
188 |
189 | function blur:on_draw(cr, w, h, child)
190 | local pixbuf = export.surface_to_pixbuf(wibox.widget.draw_to_image_surface(child, w, h), w, h)._native
191 |
192 | local blurred_pixbuf = GdkPixbuf.Pixbuf(c_filters.blur_pixbuf(pixbuf, self.radius or 10, self.no_use_kernel or false))
193 |
194 | return fill_context_with_surface(cr, export.pixbuf_to_surface(blurred_pixbuf))
195 | end
196 |
197 | function blur.parse_kwargs(kwargs)
198 | kwargs.radius = kwargs.radius or 10
199 | kwargs.no_use_kernel = kwargs.no_use_kernel or false
200 | end
201 |
202 | export.blur = blur
203 | end
204 |
205 | do
206 | local shadow = copy(export.base)
207 |
208 | gen_property(shadow, "radius")
209 | gen_property(shadow, "opacity")
210 |
211 | function shadow:on_draw(cr, w, h, child)
212 | local pixbuf = export.surface_to_pixbuf(wibox.widget.draw_to_image_surface(child, w, h), w, h)._native
213 |
214 | local blurred_pixbuf = GdkPixbuf.Pixbuf(c_filters.add_shadow_to_pixbuf(pixbuf, self.radius or 10, self.opacity or 1.0))
215 |
216 | return fill_context_with_surface(cr, export.pixbuf_to_surface(blurred_pixbuf))
217 | end
218 |
219 | function shadow.parse_kwargs(kwargs)
220 | kwargs.radius = kwargs.radius or 10
221 | kwargs.opacity = kwargs.opacity or 1.0
222 | end
223 |
224 | export.shadow = shadow
225 | end
226 |
227 | do
228 | local tint = copy(export.base)
229 |
230 | gen_property(tint, "color")
231 |
232 | local floor = math.floor
233 |
234 | function tint:on_draw(cr, w, h, child)
235 | local pixbuf = export.surface_to_pixbuf(wibox.widget.draw_to_image_surface(child, w, h), w, h)._native
236 |
237 | local r, g, b, _ = gears.color.parse_color(self.color or "#000000")
238 | r = floor(r * 255)
239 | g = floor(g * 255)
240 | b = floor(b * 255)
241 | local blurred_pixbuf = GdkPixbuf.Pixbuf(c_filters.tint_pixbuf(pixbuf, r, g, b))
242 |
243 | return fill_context_with_surface(cr, export.pixbuf_to_surface(blurred_pixbuf))
244 | end
245 |
246 | function tint.parse_kwargs(kwargs)
247 | kwargs.color = kwargs.color or "#000000"
248 | end
249 |
250 | export.tint = tint
251 | end
252 |
253 | return export
254 |
--------------------------------------------------------------------------------
/types/c-filters.lua:
--------------------------------------------------------------------------------
1 | ---@meta c-filters
2 |
3 | ---@class filters.c-filters
4 | local m = {}
5 |
6 | ---@param pixbuf userdata A native Pixbuf
7 | ---@param radius integer Blur radius in pixels
8 | ---@param no_use_kernel boolean If true, no kernel will be used; instead the pixels will simply get averaged
9 | function m.blur_pixbuf(pixbuf, radius, no_use_kernel) end
10 |
11 | ---@param pixbuf userdata A native Pixbuf
12 | ---@param radius integer Shadow radius in pixels
13 | ---@param opacity number Shadow opacity, from 0.0 to 1.0
14 | function m.add_shadow_to_pixbuf(pixbuf, radius, opacity) end
15 |
16 | ---@param pixbuf userdata A native Pixbuf
17 | ---@param red number
18 | ---@param green number
19 | ---@param blue number
20 | function m.tint_pixbuf(pixbuf, red, green, blue) end
21 |
--------------------------------------------------------------------------------